mirror of
				https://github.com/THIS-IS-NOT-A-BACKUP/zspotify.git
				synced 2025-11-03 21:10:34 +00:00 
			
		
		
		
	fixed user playlist download issue
1. added new scope `playlist-read-private` for access token 2. Added progress bar far playlist download(previously it's showing for each song separately)
This commit is contained in:
		
							parent
							
								
									714e2eccfb
								
							
						
					
					
						commit
						65093fa0d2
					
				
							
								
								
									
										15
									
								
								src/app.py
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								src/app.py
									
									
									
									
									
								
							@ -6,7 +6,7 @@ from tabulate import tabulate
 | 
				
			|||||||
from album import download_album, download_artist_albums
 | 
					from album import download_album, download_artist_albums
 | 
				
			||||||
from const import TRACK, NAME, ID, ARTISTS, ITEMS, TRACKS, EXPLICIT, ALBUMS, OWNER, \
 | 
					from const import TRACK, NAME, ID, ARTISTS, ITEMS, TRACKS, EXPLICIT, ALBUMS, OWNER, \
 | 
				
			||||||
    PLAYLISTS, DISPLAY_NAME
 | 
					    PLAYLISTS, DISPLAY_NAME
 | 
				
			||||||
from playlist import get_playlist_songs, get_playlist_info, download_from_user_playlist
 | 
					from playlist import get_playlist_songs, get_playlist_info, download_from_user_playlist, download_playlist
 | 
				
			||||||
from podcast import download_episode, get_show_episodes
 | 
					from podcast import download_episode, get_show_episodes
 | 
				
			||||||
from track import download_track, get_saved_tracks
 | 
					from track import download_track, get_saved_tracks
 | 
				
			||||||
from utils import sanitize_data, splash, split_input, regex_input_for_urls
 | 
					from utils import sanitize_data, splash, split_input, regex_input_for_urls
 | 
				
			||||||
@ -126,14 +126,14 @@ def search(search_term):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    artists = resp[ARTISTS][ITEMS]
 | 
					    artists = resp[ARTISTS][ITEMS]
 | 
				
			||||||
    if len(artists) > 0:
 | 
					    if len(artists) > 0:
 | 
				
			||||||
        print("###  ARTISTS  ###")
 | 
					        print('###  ARTISTS  ###')
 | 
				
			||||||
        artist_data = []
 | 
					        artist_data = []
 | 
				
			||||||
        for artist in artists:
 | 
					        for artist in artists:
 | 
				
			||||||
            artist_data.append([counter, artist[NAME]])
 | 
					            artist_data.append([counter, artist[NAME]])
 | 
				
			||||||
            counter += 1
 | 
					            counter += 1
 | 
				
			||||||
        total_artists = counter - total_tracks - total_albums - 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")
 | 
					        print('\n')
 | 
				
			||||||
    else:
 | 
					    else:
 | 
				
			||||||
        total_artists = 0
 | 
					        total_artists = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -161,14 +161,7 @@ def search(search_term):
 | 
				
			|||||||
            elif position <= total_artists + total_tracks + total_albums:
 | 
					            elif position <= total_artists + total_tracks + total_albums:
 | 
				
			||||||
                download_artist_albums(artists[position - total_tracks - total_albums - 1][ID])
 | 
					                download_artist_albums(artists[position - total_tracks - total_albums - 1][ID])
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                playlist_choice = playlists[position -
 | 
					                download_playlist(playlists, position - total_tracks - total_albums - total_artists)
 | 
				
			||||||
                                            total_tracks - total_albums - total_artists - 1]
 | 
					 | 
				
			||||||
                playlist_songs = get_playlist_songs(playlist_choice[ID])
 | 
					 | 
				
			||||||
                for song in playlist_songs:
 | 
					 | 
				
			||||||
                    if song[TRACK][ID] is not None:
 | 
					 | 
				
			||||||
                        download_track(song[TRACK][ID], sanitize_data(playlist_choice[NAME].strip()) + "/",
 | 
					 | 
				
			||||||
                                       disable_progressbar=True)
 | 
					 | 
				
			||||||
                        print("\n")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == '__main__':
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
				
			|||||||
@ -68,6 +68,8 @@ PREMIUM = 'premium'
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
USER_READ_EMAIL = 'user-read-email'
 | 
					USER_READ_EMAIL = 'user-read-email'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PLAYLIST_READ_PRIVATE = 'playlist-read-private'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WINDOWS_SYSTEM = 'Windows'
 | 
					WINDOWS_SYSTEM = 'Windows'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CREDENTIALS_JSON = '../credentials.json'
 | 
					CREDENTIALS_JSON = '../credentials.json'
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,5 @@
 | 
				
			|||||||
 | 
					from tqdm import tqdm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from const import ITEMS, ID, TRACK, NAME
 | 
					from const import ITEMS, ID, TRACK, NAME
 | 
				
			||||||
from track import download_track
 | 
					from track import download_track
 | 
				
			||||||
from utils import sanitize_data
 | 
					from utils import sanitize_data
 | 
				
			||||||
@ -45,15 +47,15 @@ def get_playlist_info(playlist_id):
 | 
				
			|||||||
    return resp['name'].strip(), resp['owner']['display_name'].strip()
 | 
					    return resp['name'].strip(), resp['owner']['display_name'].strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def download_playlist(playlists, playlist_choice):
 | 
					def download_playlist(playlists, playlist_number):
 | 
				
			||||||
    """Downloads all the songs from a playlist"""
 | 
					    """Downloads all the songs from a playlist"""
 | 
				
			||||||
    playlist_songs = get_playlist_songs(playlists[int(playlist_choice) - 1][ID])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for song in playlist_songs:
 | 
					    playlist_songs = [song for song in get_playlist_songs(playlists[int(playlist_number) - 1][ID]) if song[TRACK][ID]]
 | 
				
			||||||
        if song[TRACK][ID] is not None:
 | 
					    p_bar = tqdm(playlist_songs, unit='song', total=len(playlist_songs), unit_scale=True)
 | 
				
			||||||
            download_track(song[TRACK][ID], sanitize_data(
 | 
					    for song in p_bar:
 | 
				
			||||||
                playlists[int(playlist_choice) - 1][NAME].strip()) + '/')
 | 
					        download_track(song[TRACK][ID], sanitize_data(playlists[int(playlist_number) - 1][NAME].strip()) + '/',
 | 
				
			||||||
        print('\n')
 | 
					                       disable_progressbar=True)
 | 
				
			||||||
 | 
					        p_bar.set_description(song[TRACK][NAME])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def download_from_user_playlist():
 | 
					def download_from_user_playlist():
 | 
				
			||||||
@ -79,7 +81,7 @@ def download_from_user_playlist():
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        print(f'Downloading from {start} to {end}...')
 | 
					        print(f'Downloading from {start} to {end}...')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for playlist in range(start, end):
 | 
					        for playlist_number in range(start, end):
 | 
				
			||||||
            download_playlist(playlists, playlist)
 | 
					            download_playlist(playlists, playlist_number)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        print('\n**All playlists have been downloaded**\n')
 | 
					        print('\n**All playlists have been downloaded**\n')
 | 
				
			||||||
 | 
				
			|||||||
@ -73,11 +73,11 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='',
 | 
				
			|||||||
    else:
 | 
					    else:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            if not is_playable:
 | 
					            if not is_playable:
 | 
				
			||||||
                print('###   SKIPPING:', song_name,
 | 
					                print('\n###   SKIPPING:', song_name,
 | 
				
			||||||
                      '(SONG IS UNAVAILABLE)   ###')
 | 
					                      '(SONG IS UNAVAILABLE)   ###')
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                if os.path.isfile(filename) and os.path.getsize(filename) and ZSpotify.get_config(SKIP_EXISTING_FILES):
 | 
					                if os.path.isfile(filename) and os.path.getsize(filename) and ZSpotify.get_config(SKIP_EXISTING_FILES):
 | 
				
			||||||
                    print('###   SKIPPING:', song_name,
 | 
					                    print('\n###   SKIPPING:', song_name,
 | 
				
			||||||
                          '(SONG ALREADY EXISTS)   ###')
 | 
					                          '(SONG ALREADY EXISTS)   ###')
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    if track_id != scraped_song_id:
 | 
					                    if track_id != scraped_song_id:
 | 
				
			||||||
@ -107,7 +107,7 @@ def download_track(track_id: str, extra_paths='', prefix=False, prefix_value='',
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    if not ZSpotify.get_config(OVERRIDE_AUTO_WAIT):
 | 
					                    if not ZSpotify.get_config(OVERRIDE_AUTO_WAIT):
 | 
				
			||||||
                        time.sleep(ZSpotify.get_config(ANTI_BAN_WAIT_TIME))
 | 
					                        time.sleep(ZSpotify.get_config(ANTI_BAN_WAIT_TIME))
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception:
 | 
				
			||||||
            print('###   SKIPPING:', song_name,
 | 
					            print('###   SKIPPING:', song_name,
 | 
				
			||||||
                  '(GENERAL DOWNLOAD ERROR)   ###')
 | 
					                  '(GENERAL DOWNLOAD ERROR)   ###')
 | 
				
			||||||
            if os.path.exists(filename):
 | 
					            if os.path.exists(filename):
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,8 @@ from librespot.audio.decoders import VorbisOnlyAudioQuality
 | 
				
			|||||||
from librespot.core import Session
 | 
					from librespot.core import Session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from const import CREDENTIALS_JSON, TYPE, \
 | 
					from const import CREDENTIALS_JSON, TYPE, \
 | 
				
			||||||
    PREMIUM, USER_READ_EMAIL, AUTHORIZATION, OFFSET, LIMIT, CONFIG_FILE_PATH, FORCE_PREMIUM, RAW_AUDIO_AS_IS
 | 
					    PREMIUM, USER_READ_EMAIL, AUTHORIZATION, OFFSET, LIMIT, CONFIG_FILE_PATH, FORCE_PREMIUM, RAW_AUDIO_AS_IS, \
 | 
				
			||||||
 | 
					    PLAYLIST_READ_PRIVATE
 | 
				
			||||||
from utils import MusicFormat
 | 
					from utils import MusicFormat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -68,13 +69,18 @@ class ZSpotify:
 | 
				
			|||||||
    def get_content_stream(cls, content_id, quality):
 | 
					    def get_content_stream(cls, content_id, quality):
 | 
				
			||||||
        return cls.SESSION.content_feeder().load(content_id, VorbisOnlyAudioQuality(quality), False, None)
 | 
					        return cls.SESSION.content_feeder().load(content_id, VorbisOnlyAudioQuality(quality), False, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def __get_auth_token(cls):
 | 
				
			||||||
 | 
					        return cls.SESSION.tokens().get_token(USER_READ_EMAIL, PLAYLIST_READ_PRIVATE).access_token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def get_auth_header(cls):
 | 
					    def get_auth_header(cls):
 | 
				
			||||||
        return {AUTHORIZATION: f'Bearer {cls.SESSION.tokens().get(USER_READ_EMAIL)}'}
 | 
					        return {
 | 
				
			||||||
 | 
					            AUTHORIZATION: f'Bearer {cls.__get_auth_token()}'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def get_auth_header_and_params(cls, limit, offset):
 | 
					    def get_auth_header_and_params(cls, limit, offset):
 | 
				
			||||||
        return {AUTHORIZATION: f'Bearer {cls.SESSION.tokens().get(USER_READ_EMAIL)}'}, {LIMIT: limit, OFFSET: offset}
 | 
					        return {AUTHORIZATION: f'Bearer {cls.__get_auth_token()}'}, {LIMIT: limit, OFFSET: offset}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def invoke_url_with_params(cls, url, limit, offset, **kwargs):
 | 
					    def invoke_url_with_params(cls, url, limit, offset, **kwargs):
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user