mirror of
https://github.com/THIS-IS-NOT-A-BACKUP/zspotify.git
synced 2024-11-26 18:03:17 +01:00
Merge branch 'main' into argument-parsing
This commit is contained in:
commit
d69fb8e754
@ -16,4 +16,4 @@ COPY --from=builder /install /usr/local
|
||||
COPY zspotify /app
|
||||
COPY *zs_config.json /
|
||||
WORKDIR /app
|
||||
ENTRYPOINT ["/usr/local/bin/python", "__main__.py"]
|
||||
ENTRYPOINT ["/usr/local/bin/python", "app.py"]
|
||||
|
26
README.md
26
README.md
@ -1,17 +1,14 @@
|
||||
![Stars](https://img.shields.io/github/stars/Footsiefat/zspotify.svg)
|
||||
![Forks](https://img.shields.io/github/forks/Footsiefat/zspotify.svg)
|
||||
![Size](https://img.shields.io/github/repo-size/Footsiefat/zspotify)
|
||||
|
||||
# ZSpotify
|
||||
|
||||
### A Spotify downloader needing only a python interpreter and ffmpeg.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://user-images.githubusercontent.com/12180913/138040605-c9d46e45-3830-4a4b-a7ac-c56bb0d76335.png">
|
||||
</p>
|
||||
|
||||
[Discord Server](https://discord.gg/skVNQKtyFq) - [Matrix Server](https://matrix.to/#/#zspotify:matrix.org) - [Gitea Mirror](https://git.robinsmediateam.dev/Footsiefat/zspotify) - [Main Site](https://footsiefat.github.io/)
|
||||
|
||||
```
|
||||
Requirements:
|
||||
|
||||
@ -30,18 +27,15 @@ Python packages:
|
||||
\*ffmpeg can be installed via apt for Debian-based distros or by downloading the binaries from [ffmpeg.org](https://ffmpeg.org) and placing them in your %PATH% in Windows. Mac users can install it with [Homebrew](https://brew.sh) by running `brew install ffmpeg`.
|
||||
|
||||
\*\*Git can be installed via apt for Debian-based distros or by downloading the binaries from [git-scm.com](https://git-scm.com/download/win) for Windows.
|
||||
|
||||
### Command line usage:
|
||||
|
||||
```
|
||||
Basic command line usage:
|
||||
python zspotify <track/album/playlist/episode/artist url> Downloads the track, album, playlist or podcast episode specified as a command line argument. If an artist url is given, all albums by specified artist will be downloaded.
|
||||
Command line usage:
|
||||
python zspotify Loads search prompt to find then download a specific track, album or playlist
|
||||
python zspotify <track/album/playlist/episode url> Downloads the track, album, playlist or podcast episode specified as a command line argument
|
||||
python zspotify <artist url> Downloads all albums by specified artist
|
||||
|
||||
Extra command line options:
|
||||
-p, --playlist Downloads a saved playlist from your account
|
||||
-ls, --liked-songs Downloads all the liked songs from your account
|
||||
-s, --search Loads search prompt to find then download a specific track, album or playlist
|
||||
-ns, --no-splash Suppress the splash screen when loading.
|
||||
|
||||
Options that can be configured in zs_config.json:
|
||||
ROOT_PATH Change this path if you don't like the default directory where ZSpotify saves the music
|
||||
@ -60,32 +54,22 @@ Options that can be configured in zs_config.json:
|
||||
### Docker Usage
|
||||
|
||||
```
|
||||
Pull the official docker image (automatically updates):
|
||||
docker pull cooper7692/zspotify-docker
|
||||
Or build the docker image yourself from the Dockerfile:
|
||||
Build the docker image from the Dockerfile:
|
||||
docker build -t zspotify .
|
||||
Create and run a container from the image:
|
||||
docker run --rm -v "$PWD/ZSpotify Music:/ZSpotify Music" -v "$PWD/ZSpotify Podcasts:/ZSpotify Podcasts" -it zspotify
|
||||
```
|
||||
|
||||
### Will my account get banned if I use this tool?
|
||||
|
||||
Currently no user has reported their account getting banned after using ZSpotify.
|
||||
This isn't to say _you_ won't get banned as it is technically against Spotify's TOS.
|
||||
**Use ZSpotify at your own risk**, the developers of ZSpotify are not responsible if your account gets banned.
|
||||
|
||||
### What do I do if I see "Your session has been terminated"?
|
||||
|
||||
If you see this, don't worry! Just try logging back in. If you see the incorrect username or password error, reset your password and you should be able to log back in and continue using Spotify.
|
||||
|
||||
### Contributing
|
||||
|
||||
Please refer to [CONTRIBUTING](CONTRIBUTING.md)
|
||||
|
||||
### Changelog
|
||||
|
||||
Please refer to [CHANGELOG](CHANGELOG.md)
|
||||
|
||||
### Common Errors
|
||||
|
||||
Please refer to [COMMON_ERRORS](COMMON_ERRORS.md)
|
||||
|
@ -1,34 +1,4 @@
|
||||
import argparse
|
||||
|
||||
from app import client
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(prog='zspotify',
|
||||
description='A Spotify downloader needing only a python interpreter and ffmpeg.')
|
||||
parser.add_argument('-ns', '--no-splash',
|
||||
action='store_true',
|
||||
help='Suppress the splash screen when loading.')
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument('url',
|
||||
type=str,
|
||||
default='',
|
||||
nargs='?',
|
||||
help='Downloads the track, album, playlist, podcast episode, or all albums by an artist from a url.')
|
||||
group.add_argument('-ls', '--liked-songs',
|
||||
dest='liked_songs',
|
||||
action='store_true',
|
||||
help='Downloads all the liked songs from your account.')
|
||||
group.add_argument('-p', '--playlist',
|
||||
action='store_true',
|
||||
help='Downloads a saved playlist from your account.')
|
||||
group.add_argument('-s', '--search',
|
||||
dest='search_spotify',
|
||||
action='store_true',
|
||||
help='Loads search prompt to find then download a specific track, album or playlist')
|
||||
|
||||
parser.set_defaults(func=client)
|
||||
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
client()
|
||||
|
133
zspotify/app.py
133
zspotify/app.py
@ -1,3 +1,5 @@
|
||||
import sys
|
||||
|
||||
from librespot.audio.decoders import AudioQuality
|
||||
from tabulate import tabulate
|
||||
|
||||
@ -13,64 +15,56 @@ from zspotify import ZSpotify
|
||||
SEARCH_URL = 'https://api.spotify.com/v1/search'
|
||||
|
||||
|
||||
def client(args) -> None:
|
||||
def client() -> None:
|
||||
""" Connects to spotify to perform query's and get songs to download """
|
||||
ZSpotify()
|
||||
|
||||
if not args.no_splash:
|
||||
splash()
|
||||
splash()
|
||||
|
||||
if ZSpotify.check_premium():
|
||||
if not args.no_splash:
|
||||
print('[ DETECTED PREMIUM ACCOUNT - USING VERY_HIGH QUALITY ]\n\n')
|
||||
print('[ DETECTED PREMIUM ACCOUNT - USING VERY_HIGH QUALITY ]\n\n')
|
||||
ZSpotify.DOWNLOAD_QUALITY = AudioQuality.VERY_HIGH
|
||||
else:
|
||||
if not args.no_splash:
|
||||
print('[ DETECTED FREE ACCOUNT - USING HIGH QUALITY ]\n\n')
|
||||
print('[ DETECTED FREE ACCOUNT - USING HIGH QUALITY ]\n\n')
|
||||
ZSpotify.DOWNLOAD_QUALITY = AudioQuality.HIGH
|
||||
|
||||
if args.url:
|
||||
track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(
|
||||
args.url)
|
||||
|
||||
if track_id is not None:
|
||||
download_track(track_id)
|
||||
elif artist_id is not None:
|
||||
download_artist_albums(artist_id)
|
||||
elif album_id is not None:
|
||||
download_album(album_id)
|
||||
elif playlist_id is not None:
|
||||
playlist_songs = get_playlist_songs(playlist_id)
|
||||
name, _ = get_playlist_info(playlist_id)
|
||||
for song in playlist_songs:
|
||||
download_track(song[TRACK][ID],
|
||||
sanitize_data(name) + '/')
|
||||
if len(sys.argv) > 1:
|
||||
if sys.argv[1] == '-p' or sys.argv[1] == '--playlist':
|
||||
download_from_user_playlist()
|
||||
elif sys.argv[1] == '-ls' or sys.argv[1] == '--liked-songs':
|
||||
for song in get_saved_tracks():
|
||||
if not song[TRACK][NAME]:
|
||||
print('### SKIPPING: SONG DOES NOT EXIST ON SPOTIFY ANYMORE ###')
|
||||
else:
|
||||
download_track(song[TRACK][ID], 'Liked Songs/')
|
||||
print('\n')
|
||||
elif episode_id is not None:
|
||||
download_episode(episode_id)
|
||||
elif show_id is not None:
|
||||
for episode in get_show_episodes(show_id):
|
||||
download_episode(episode)
|
||||
else:
|
||||
track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(sys.argv[1])
|
||||
|
||||
if args.playlist:
|
||||
download_from_user_playlist()
|
||||
if track_id is not None:
|
||||
download_track(track_id)
|
||||
elif artist_id is not None:
|
||||
download_artist_albums(artist_id)
|
||||
elif album_id is not None:
|
||||
download_album(album_id)
|
||||
elif playlist_id is not None:
|
||||
playlist_songs = get_playlist_songs(playlist_id)
|
||||
name, _ = get_playlist_info(playlist_id)
|
||||
for song in playlist_songs:
|
||||
download_track(song[TRACK][ID],
|
||||
sanitize_data(name) + '/')
|
||||
print('\n')
|
||||
elif episode_id is not None:
|
||||
download_episode(episode_id)
|
||||
elif show_id is not None:
|
||||
for episode in get_show_episodes(show_id):
|
||||
download_episode(episode)
|
||||
|
||||
if args.liked_songs:
|
||||
for song in get_saved_tracks():
|
||||
if not song[TRACK][NAME]:
|
||||
print(
|
||||
'### SKIPPING: SONG DOES NOT EXIST ON SPOTIFY ANYMORE ###')
|
||||
else:
|
||||
download_track(song[TRACK][ID], 'Liked Songs/')
|
||||
print('\n')
|
||||
|
||||
if args.search_spotify:
|
||||
else:
|
||||
search_text = ''
|
||||
while len(search_text) == 0:
|
||||
search_text = input('Enter search or URL: ')
|
||||
|
||||
track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(
|
||||
search_text)
|
||||
track_id, album_id, playlist_id, episode_id, show_id, artist_id = regex_input_for_urls(search_text)
|
||||
|
||||
if track_id is not None:
|
||||
download_track(track_id)
|
||||
@ -120,6 +114,7 @@ def search(search_term):
|
||||
raise ValueError('Invalid limit passed. Max is 50.\n')
|
||||
params['limit'] = splits[index+1]
|
||||
|
||||
|
||||
if split == '-t' or split == '-type':
|
||||
|
||||
allowed_types = ['track', 'playlist', 'album', 'artist']
|
||||
@ -153,7 +148,6 @@ def search(search_term):
|
||||
counter = 1
|
||||
dics = []
|
||||
|
||||
total_tracks = 0
|
||||
if TRACK in params['type'].split(','):
|
||||
tracks = resp[TRACKS][ITEMS]
|
||||
if len(tracks) > 0:
|
||||
@ -168,20 +162,20 @@ def search(search_term):
|
||||
track_data.append([counter, f'{track[NAME]} {explicit}',
|
||||
','.join([artist[NAME] for artist in track[ARTISTS]])])
|
||||
dics.append({
|
||||
ID: track[ID],
|
||||
NAME: track[NAME],
|
||||
'type': TRACK,
|
||||
ID : track[ID],
|
||||
NAME : track[NAME],
|
||||
'type' : TRACK,
|
||||
})
|
||||
|
||||
counter += 1
|
||||
total_tracks = counter - 1
|
||||
print(tabulate(track_data, headers=[
|
||||
'S.NO', 'Name', 'Artists'], tablefmt='pretty'))
|
||||
print(tabulate(track_data, headers=['S.NO', 'Name', 'Artists'], tablefmt='pretty'))
|
||||
print('\n')
|
||||
del tracks
|
||||
del track_data
|
||||
else:
|
||||
total_tracks = 0
|
||||
|
||||
total_albums = 0
|
||||
if ALBUM in params['type'].split(','):
|
||||
albums = resp[ALBUMS][ITEMS]
|
||||
if len(albums) > 0:
|
||||
@ -191,20 +185,20 @@ def search(search_term):
|
||||
album_data.append([counter, album[NAME],
|
||||
','.join([artist[NAME] for artist in album[ARTISTS]])])
|
||||
dics.append({
|
||||
ID: album[ID],
|
||||
NAME: album[NAME],
|
||||
'type': ALBUM,
|
||||
ID : album[ID],
|
||||
NAME : album[NAME],
|
||||
'type' : ALBUM,
|
||||
})
|
||||
|
||||
counter += 1
|
||||
total_albums = counter - total_tracks - 1
|
||||
print(tabulate(album_data, headers=[
|
||||
'S.NO', 'Album', 'Artists'], tablefmt='pretty'))
|
||||
print(tabulate(album_data, headers=['S.NO', 'Album', 'Artists'], tablefmt='pretty'))
|
||||
print('\n')
|
||||
del albums
|
||||
del album_data
|
||||
else:
|
||||
total_albums = 0
|
||||
|
||||
total_artists = 0
|
||||
if ARTIST in params['type'].split(','):
|
||||
artists = resp[ARTISTS][ITEMS]
|
||||
if len(artists) > 0:
|
||||
@ -213,39 +207,39 @@ def search(search_term):
|
||||
for artist in artists:
|
||||
artist_data.append([counter, artist[NAME]])
|
||||
dics.append({
|
||||
ID: artist[ID],
|
||||
NAME: artist[NAME],
|
||||
'type': ARTIST,
|
||||
ID : artist[ID],
|
||||
NAME : artist[NAME],
|
||||
'type' : ARTIST,
|
||||
})
|
||||
counter += 1
|
||||
total_artists = counter - total_tracks - total_albums - 1
|
||||
print(tabulate(artist_data, headers=[
|
||||
'S.NO', 'Name'], tablefmt='pretty'))
|
||||
print(tabulate(artist_data, headers=['S.NO', 'Name'], tablefmt='pretty'))
|
||||
print('\n')
|
||||
del artists
|
||||
del artist_data
|
||||
else:
|
||||
total_artists = 0
|
||||
|
||||
total_playlists = 0
|
||||
if PLAYLIST in params['type'].split(','):
|
||||
playlists = resp[PLAYLISTS][ITEMS]
|
||||
if len(playlists) > 0:
|
||||
print('### PLAYLISTS ###')
|
||||
playlist_data = []
|
||||
for playlist in playlists:
|
||||
playlist_data.append(
|
||||
[counter, playlist[NAME], playlist[OWNER][DISPLAY_NAME]])
|
||||
playlist_data.append([counter, playlist[NAME], playlist[OWNER][DISPLAY_NAME]])
|
||||
dics.append({
|
||||
ID: playlist[ID],
|
||||
NAME: playlist[NAME],
|
||||
'type': PLAYLIST,
|
||||
ID : playlist[ID],
|
||||
NAME : playlist[NAME],
|
||||
'type' : PLAYLIST,
|
||||
})
|
||||
counter += 1
|
||||
total_playlists = counter - total_artists - total_tracks - total_albums - 1
|
||||
print(tabulate(playlist_data, headers=[
|
||||
'S.NO', 'Name', 'Owner'], tablefmt='pretty'))
|
||||
print(tabulate(playlist_data, headers=['S.NO', 'Name', 'Owner'], tablefmt='pretty'))
|
||||
print('\n')
|
||||
del playlists
|
||||
del playlist_data
|
||||
else:
|
||||
total_playlists = 0
|
||||
|
||||
if total_tracks + total_albums + total_artists + total_playlists == 0:
|
||||
print('NO RESULTS FOUND - EXITING...')
|
||||
@ -267,3 +261,4 @@ def search(search_term):
|
||||
download_artist_albums(dic[ID])
|
||||
else:
|
||||
download_playlist(dic)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user