diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e9617c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,142 @@ +#not adding examples.py +examples.py + +#official python gitignore +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..662ea6a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Abhishek Chaudhari + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b5f3a08 --- /dev/null +++ b/README.md @@ -0,0 +1,268 @@ +# [JioSaavn-Python](https://github.com/abhichaudharii/jiosaavn-python) + +An unofficial Python3 wrapper for JioSaavn, a popular Indian music streaming service.. I am in no way affiliated with [JioSaavn](https://www.jiosaavn.com/), use at your own risk. + +### Show some ❤️ and ⭐ the repo to support the project. It will motivate me to keep this project alive. +[![Telegram Channel](https://img.shields.io/badge/Telegram-Channel-orange)](https://t.me/ab_projects) [![made-with-python](https://img.shields.io/badge/Made%20with-Python-1f425f.svg)](https://www.python.org/) + +### Hire Me: [Abhi Chaudhari](https://www.freelancer.com/hireme/abhichaudharii)! + + +## Features +- Get New Releases +- Get Song Lyrics +- Get Song Info +- Get Song Direct Download Link +- Get Featured Playlists +- Get Playlist Songs +- Get Top Charts +- Get Top Artists +- Get Artist's Top Songs +- Search for Songs +- Search for Albums +- Search for Artists +- Search for Playlists +- Search for Podcasts +- Search Top Queries + +### Getting it +To download jiosaavn-python, either fork this github repo or simply use Pypi via pip. +```bash +pip install jiosaavn-python +``` + +#### Get New Releases + +You can use the following code to get new releases from `JioSaavn`. The below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. We are using `limit=2` to limit the number of results returned. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.get_new_releases(page=1, limit=2)) +print(json.dumps(data, indent=4)) +``` + +#### Get Song Lyrics + +You can use the following code to get song lyrics from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.get_song_lyrics("https://www.jiosaavn.com/song/srivalli/RBpGRidYdVI")) +print(json.dumps(data, indent=4)) +``` + +#### Get Song Info + +You can use the following code to get song info from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.get_song_details("https://www.jiosaavn.com/song/srivalli/RBpGRidYdVI")) +print(json.dumps(data, indent=4)) +``` + +#### Get Song Direct Download Link + +You can use the following code to get a song direct download from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.get_song_direct_link("https://www.jiosaavn.com/song/srivalli/RBpGRidYdVI")) +print(json.dumps(data, indent=4)) +``` + +#### Get Featured Playlists + +You can use the following code to get featured playlists from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. We are using `limit=2` to limit the number of results returned. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.get_featured_playlists(page=1, limit=2)) +print(json.dumps(data, indent=4)) +``` + +#### Get Playlist Songs + +You can use the following code to get playlist songs from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. We are using `limit=2` to limit the number of results returned + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.get_playlist_songs("https://www.jiosaavn.com/featured/the-landers/2-aGcw5eLvQwkg5tVhI3fw__", page=1, limit=2)) +print(json.dumps(data, indent=4)) +``` + +#### Get Top Charts + +You can use the following code to top charts from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.get_top_charts()) +print(json.dumps(data, indent=4)) +``` + +#### Get Top Artists + +You can use the following code to get top artists from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.get_top_artists()) +print(json.dumps(data, indent=4)) +``` + +#### Get Artist's Top Songs + +You can use the following code to get the artist's top songs from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. We are using `limit=2` to limit the number of results returned. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.get_artist_top_songs("https://www.jiosaavn.com/artist/allu-arjun-songs/BGi8EcKdZXk_", limit=2)) +print(json.dumps(data, indent=4)) +``` + +#### Search for Songs + +You can use the following code to search songs from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.search_songs("Narayan mil jayega")) +print(json.dumps(data, indent=4)) +``` + +#### Search for Albums + +You can use the following code to search albums from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.search_albums("Narayan mil jayega")) +print(json.dumps(data, indent=4)) +``` + +#### Search for Artists + +You can use the following code to search artists from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.search_artists("Allu Arjun")) +print(json.dumps(data, indent=4)) +``` + +#### Search for Playlists + +You can use the following code to search playlists from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.search_playlists("Krishna")) +print(json.dumps(data, indent=4)) +``` + +#### Search for Podcasts + +You can use the following code to search podcasts from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.search_podcasts("Krishan")) +print(json.dumps(data, indent=4)) +``` + +#### Search Top Queries + +You can use the following code to search top queries from `JioSaavn`. You can pass Song `ID/LINK` Below code shows the example using `asyncio` but you can also use the following in the async function with the `await` keyword. + +```python +import json +import asyncio +from jiosaavn import JioSaavn + +saavn = JioSaavn() +data = asyncio.run(saavn.search_topquery("the landers")) +print(json.dumps(data, indent=4)) +``` + + +License +---- + +MIT License + +Copyright (c) 2023 Abhishek Chaudhari + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c419263 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/jiosaavn/__init__.py b/jiosaavn/__init__.py new file mode 100644 index 0000000..c4fe9ea --- /dev/null +++ b/jiosaavn/__init__.py @@ -0,0 +1,5 @@ +from .jiosaavn import JioSaavn + +"""An unofficial Python3 wrapper for JioSaavn, a popular Indian music streaming service.. +.. moduleauthor:: Abhishek Chaudhari +""" \ No newline at end of file diff --git a/jiosaavn/jiosaavn.py b/jiosaavn/jiosaavn.py new file mode 100644 index 0000000..c674ec9 --- /dev/null +++ b/jiosaavn/jiosaavn.py @@ -0,0 +1,371 @@ +import httpx +from random import choice + +REQ_TIMEOUT = 20 +USER_AGENTS = [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/110.0", + "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0", + "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0", +] + +headers = { + "User-Agent": "", + "Accept": "application/json, text/plain, */*", + "Accept-Language": "en-US,en;q=0.5", + "Connection": "keep-alive", + "Referer": "https://www.jiosaavn.com/", + "Sec-Fetch-Dest": "empty", + "Sec-Fetch-Mode": "cors", + "Sec-Fetch-Site": "same-origin", +} + + +class JioSaavn: + """An unofficial Python3 wrapper for JioSaavn, a popular Indian music streaming service..""" + + def __init__(self): + self.api_url = "https://www.jiosaavn.com/api.php" + self.api_client = httpx.AsyncClient(timeout=REQ_TIMEOUT) + self.api_client.headers.update(headers) + + self.user_agents = USER_AGENTS + self._set_random_user_agent() + + def _set_random_user_agent(self): + """Setting unique user agent on every in the instance. Just to be safe""" + + random_user_agent = choice(self.user_agents) + self.api_client.headers.update({"User-Agent": random_user_agent}) + + async def _format_response(self, response): + """Returns the formatted response""" + + if isinstance(response, list): + return {"data": response} + elif isinstance(response, dict): + if "data" in response: + return response + else: + return {"data": response} + + async def _get_id_from_url(self, url): + """Splits given URL and returns the corresponding id""" + + return url.split("/")[-1] + + async def get_new_releases(self, page=1, limit=50): + """Finds new releases from JioSaavn with given page and limit""" + + params = { + "api_version": "4", + "_format": "json", + "__call": "content.getAlbums", + "p": str(page), + "ctx": "web6dot0", + "_marker": "0", + "n": str(limit), + } + + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + data = resp.json() + data["status"] = resp.status_code + return data + else: + return {"status": resp.status_code} + + async def get_featured_playlists(self, page=1, limit=50): + """Finds featured playlists from JioSaavn with given page and limit""" + + params = { + "__call": "content.getFeaturedPlaylists", + "fetch_from_serialized_files": "true", + "api_version": "4", + "_format": "json", + "_marker": "0", + "ctx": "web6dot0", + "p": str(page), + "n": str(limit), + } + + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + data = await self._format_response(resp.json()) + data["status"] = resp.status_code + return data + else: + return {"status": resp.status_code} + + async def get_playlist_songs(self, playlist_id, page=1, limit=1): + """Finds songs from passed playlist ID/URL from JioSaavn with given page and limit""" + + # Checking if the user has passed the URL or id + if "jiosaavn" in playlist_id: + playlist_id = await self._get_id_from_url(playlist_id) + + params = { + "__call": "webapi.get", + "token": playlist_id, + "type": "playlist", + "p": str(page), + "n": str(limit), + "includeMetaTags": "0", + "ctx": "web6dot0", + "api_version": "4", + "_format": "json", + "_marker": "0", + } + + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + data = await self._format_response(resp.json()) + data["status"] = resp.status_code + return data + else: + return {"status": resp.status_code} + + async def get_top_charts(self): + """Finds top charts from JioSaavn""" + + params = { + "__call": "content.getCharts", + "api_version": "4", + "_format": "json", + "_marker": "0", + "ctx": "web6dot0", + } + + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + data = await self._format_response(resp.json()) + data["status"] = resp.status_code + return data + else: + return {"status": resp.status_code} + + async def get_top_artists(self): + """Finds top artists from JioSaavn""" + + params = { + "__call": "social.getTopArtists", + "api_version": "4", + "_format": "json", + "_marker": "0", + "ctx": "web6dot0", + } + + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + data = await self._format_response(resp.json()) + data["status"] = resp.status_code + return data + else: + return {"status": resp.status_code} + + async def get_artist_top_songs(self, singer_id, page=1, limit=1): + """Finds songs from passed artist ID/URL from JioSaavn with given page and limit""" + + # Checking if the user has passed the ID/URL + if "jiosaavn" in singer_id: + singer_id = await self._get_id_from_url(singer_id) + + params = { + "token": singer_id, + "p": str(page), + "n_song": str(limit), + "__call": "webapi.get", + "type": "artist", + "n_album": "0", + "sub_type": "songs", + "category": "", + "sort_order": "", + "includeMetaTags": "0", + "ctx": "web6dot0", + "api_version": "4", + "_format": "json", + "_marker": "0", + } + + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + data = await self._format_response(resp.json()["topSongs"]) + data["status"] = resp.status_code + return data + else: + return {"status": resp.status_code} + + async def get_song_details(self, song_id): + """Finds song details for the passed id/url on JioSaavn and returns""" + + # Checking if the user has passed the ID/URL + if "jiosaavn" in song_id: + song_id = await self._get_id_from_url(song_id) + + print(f"[I] Finds SONG DETAILS with ID:- {song_id}") + + params = { + "__call": "webapi.get", + "token": song_id, + "type": "song", + "includeMetaTags": "0", + "ctx": "web6dot0", + "api_version": "4", + "_format": "json", + "_marker": "0", + } + + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + return resp.json() + else: + return {"status": resp.status_code} + + async def get_song_direct_link(self, song_id): + """Finds song direct link for the passed ID/URL on JioSaavn and returns that""" + + songs = await self.get_song_details(song_id) + more_info = songs["songs"][0]["more_info"] + song_enc_url = more_info["encrypted_media_url"] + bitrate = "320" if more_info["320kbps"] else "128" + + params = { + "__call": "song.generateAuthToken", + "url": song_enc_url, + "bitrate": str(bitrate), + "api_version": "4", + "_format": "json", + "ctx": "web6dot0", + "_marker": "0", + } + + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + data = await self._format_response(resp.json()) + return data["data"]["auth_url"] + else: + return {"status": resp.status_code} + + async def get_song_lyrics(self, song_id): + """Finds lyrics for the passed ID/URL on JioSaavn and returns them""" + + songs = await self.get_song_details(song_id) + song_id = songs["songs"][0]["id"] + + params = { + "__call": "lyrics.getLyrics", + "lyrics_id": song_id, + "ctx": "web6dot0", + "api_version": "4", + "_format": "json", + "_marker": "0", + } + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + data = await self._format_response(resp.json()) + data["status"] = resp.status_code + return data + else: + return {"status": resp.status_code} + + async def search_on_saavn(self, search_query): + """Searches a passed search query on JioSaavn.""" + + params = { + "__call": "autocomplete.get", + "query": search_query, + "_format": "json", + "_marker": "0", + "ctx": "web6dot0", + } + + resp = await self.api_client.post(url=self.api_url, params=params) + if resp.status_code == 200: + data = await self._format_response(resp.json()) + data["status"] = resp.status_code + return data + else: + return {"status": resp.status_code} + + async def search_songs(self, search_query): + """Searches a passed search query on JioSaavn and returns all the songs""" + + result = await self.search_on_saavn(search_query) + if result["status"] == 200: + return {"status": result["status"], "data": result["data"]["songs"]["data"]} + else: + return { + "status": result["status"], + "message": "Could not find songs for the search query", + } + + async def search_albums(self, search_query): + """Searches a passed search query on JioSaavn and returns all the albums""" + + result = await self.search_on_saavn(search_query) + if result["status"] == 200: + return { + "status": result["status"], + "data": result["data"]["albums"]["data"], + } + else: + return { + "status": result["status"], + "message": "Could not find albums for the search query", + } + + async def search_playlists(self, search_query): + """Searches a passed search query on JioSaavn and returns all the playlists""" + + result = await self.search_on_saavn(search_query) + if result["status"] == 200: + return { + "status": result["status"], + "data": result["data"]["playlists"]["data"], + } + else: + return { + "status": result["status"], + "message": "Could not find playlists for the search query", + } + + async def search_artists(self, search_query): + """Searches a passed search query on JioSaavn and returns all the artists""" + + result = await self.search_on_saavn(search_query) + if result["status"] == 200: + return { + "status": result["status"], + "data": result["data"]["artists"]["data"], + } + else: + return { + "status": result["status"], + "message": "Could not find artists for the search query", + } + + async def search_topquery(self, search_query): + """Searches a passed search query on JioSaavn and returns all the topquery""" + + result = await self.search_on_saavn(search_query) + if result["status"] == 200: + return { + "status": result["status"], + "data": result["data"]["topquery"]["data"], + } + else: + return { + "status": result["status"], + "message": "Could not find top query for the search query", + } + + async def search_podcasts(self, search_query): + """Searches a passed search query on JioSaavn and returns all the podcasts""" + + result = await self.search_on_saavn(search_query) + if result["status"] == 200: + return {"status": result["status"], "data": result["data"]["shows"]["data"]} + else: + return { + "status": result["status"], + "message": "Could not find podcasts for the search query", + } diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..224a779 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[metadata] +description-file = README.md \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6102b4a --- /dev/null +++ b/setup.py @@ -0,0 +1,29 @@ +from distutils.core import setup + +with open("README.md") as f: + long_description = f.read() + +setup( + name = 'jiosaavn-python', + packages = ['jiosaavn'], + version = '0.1', + license='MIT', + description = 'An unofficial Python3 wrapper for JioSaavn, a popular Indian music streaming service..', + author = 'Abhishek Chaudhari', + author_email = 'abhichaudhari@protonmail.com', + url = 'https://github.com/abhichaudharii/jiosaavn-python', + download_url = 'https://github.com/abhichaudharii/jiosaavn-python/archive/refs/tags/v0.1.tar.gz', + long_description_content_type="text/markdown", + long_description=long_description, + keywords = ['JioSaavn', 'JioSaavn-API', 'Saavn', 'Saavn API', 'download songs', 'lyrics', 'playlist'], + install_requires=[ + 'httpx' + ], + classifiers=[ + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'Topic :: Software Development :: Build Tools', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python :: 3.8', + ], +) \ No newline at end of file