mirror of
				https://github.com/THIS-IS-NOT-A-BACKUP/zspotify.git
				synced 2025-11-04 05:20:34 +00:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/main'
This commit is contained in:
		
						commit
						0f2e57aa21
					
				
							
								
								
									
										80
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										80
									
								
								README.md
									
									
									
									
									
								
							@ -37,26 +37,78 @@ Python packages:
 | 
				
			|||||||
Basic 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. Can take multiple urls.
 | 
					  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. Can take multiple urls.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Extra command line options:
 | 
					Different usage modes:
 | 
				
			||||||
 | 
					  (nothing)            Download the tracks/alumbs/playlists URLs from the parameter
 | 
				
			||||||
 | 
					  -d,  --download      Download all tracks/alumbs/playlists URLs from the specified file
 | 
				
			||||||
  -p,  --playlist      Downloads a saved playlist from your account
 | 
					  -p,  --playlist      Downloads a saved playlist from your account
 | 
				
			||||||
  -ls, --liked-songs   Downloads all the liked songs 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
 | 
					  -s,  --search        Loads search prompt to find then download a specific track, album or playlist
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					Extra command line options:
 | 
				
			||||||
  -ns, --no-splash     Suppress the splash screen when loading.
 | 
					  -ns, --no-splash     Suppress the splash screen when loading.
 | 
				
			||||||
 | 
					  --config-location    Use a different zs_config.json, defaults to the one in the program directory
 | 
				
			||||||
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
 | 
					 | 
				
			||||||
  ROOT_PODCAST_PATH   Change this path if you don't like the default directory where ZSpotify saves the podcasts
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  SKIP_EXISTING_FILES Set this to false if you want ZSpotify to overwrite files with the same name rather than skipping the song
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  MUSIC_FORMAT        Can be "mp3" or "ogg", mp3 is required for track metadata however ogg is slightly higher quality as it is not transcoded.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  FORCE_PREMIUM       Set this to true if ZSpotify isn't automatically detecting that you are using a premium account
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  ANTI_BAN_WAIT_TIME  Change this setting if the time waited between bulk downloads is too high or low
 | 
					 | 
				
			||||||
  OVERRIDE_AUTO_WAIT  Change this to true if you want to completely disable the wait between songs for faster downloads with the risk of instability
 | 
					 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Options:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					All these options can either be configured in the zs_config or via the commandline, in case of both the commandline-option has higher priority.  
 | 
				
			||||||
 | 
					Be aware you have to set boolean values in the commandline like this: `--download-real-time=True`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Key (zs-config)              | commandline parameter            | Description
 | 
				
			||||||
 | 
					|------------------------------|----------------------------------|---------------------------------------------------------------------|
 | 
				
			||||||
 | 
					| ROOT_PATH                    | --root-path                      | directory where ZSpotify saves the music
 | 
				
			||||||
 | 
					| ROOT_PODCAST_PATH            | --root-podcast-path              | directory where ZSpotify saves the podcasts
 | 
				
			||||||
 | 
					| SKIP_EXISTING_FILES          | --skip-existing-files            | Skip songs with the same name
 | 
				
			||||||
 | 
					| SKIP_PREVIOUSLY_DOWNLOADED   | --skip-previously-downloaded     | Create a .song_archive file and skip previously downloaded songs
 | 
				
			||||||
 | 
					| DOWNLOAD_FORMAT              | --download-format                | The download audio format (aac, fdk_aac, m4a, mp3, ogg, opus, vorbis)
 | 
				
			||||||
 | 
					| FORCE_PREMIUM                | --force-premium                  | Force the use of high quality downloads (only with premium accounts)
 | 
				
			||||||
 | 
					| ANTI_BAN_WAIT_TIME           | --anti-ban-wait-time             | The wait time between bulk downloads
 | 
				
			||||||
 | 
					| OVERRIDE_AUTO_WAIT           | --override-auto-wait             | Totally disable wait time between songs with the risk of instability
 | 
				
			||||||
 | 
					| CHUNK_SIZE                   | --chunk-size                     | chunk size for downloading
 | 
				
			||||||
 | 
					| SPLIT_ALBUM_DISCS            | --split-album-discs              | split downloaded albums by disc
 | 
				
			||||||
 | 
					| DOWNLOAD_REAL_TIME           | --download-real-time             | only downloads songs as fast as they would be played, can prevent account bans
 | 
				
			||||||
 | 
					| LANGUAGE                     | --language                       | Language for spotify metadata
 | 
				
			||||||
 | 
					| BITRATE                      | --bitrate                        | Overwrite the bitrate for ffmpeg encoding
 | 
				
			||||||
 | 
					| SONG_ARCHIVE                 | --song-archive                   | The song_archive file for SKIP_PREVIOUSLY_DOWNLOADED
 | 
				
			||||||
 | 
					| CREDENTIALS_LOCATION         | --credentials-location           | The location of the credentials.json
 | 
				
			||||||
 | 
					| OUTPUT                       | --output                         | The output location/format (see below)
 | 
				
			||||||
 | 
					| PRINT_SPLASH                 | --print-splash                   | Print the splash message
 | 
				
			||||||
 | 
					| PRINT_SKIPS                  | --print-skips                    | Print messages if a song is being skipped
 | 
				
			||||||
 | 
					| PRINT_DOWNLOAD_PROGRESS      | --print-download-progress        | Print the download/playlist progress bars
 | 
				
			||||||
 | 
					| PRINT_ERRORS                 | --print-errors                   | Print errors
 | 
				
			||||||
 | 
					| PRINT_DOWNLOADS              | --print-downloads                | Print messages when a song is finished downloading
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Output format:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					With the option `OUTPUT` (or the commandline parameter `--output`) you can specify the output location and format.  
 | 
				
			||||||
 | 
					The value is relative to the `ROOT_PATH`/`ROOT_PODCAST_PATH` directory and can contain the following placeholder:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| Placeholder     | Description
 | 
				
			||||||
 | 
					|-----------------|--------------------------------
 | 
				
			||||||
 | 
					| {artist}        | The song artist
 | 
				
			||||||
 | 
					| {album}         | The song album
 | 
				
			||||||
 | 
					| {song_name}     | The song name
 | 
				
			||||||
 | 
					| {release_year}  | The song release year
 | 
				
			||||||
 | 
					| {disc_number}   | The disc number
 | 
				
			||||||
 | 
					| {track_number}  | The track_number
 | 
				
			||||||
 | 
					| {id}            | The song id
 | 
				
			||||||
 | 
					| {track_id}      | The track id
 | 
				
			||||||
 | 
					| {ext}           | The file extension
 | 
				
			||||||
 | 
					| {album_id}      | (only when downloading albums) ID of the album
 | 
				
			||||||
 | 
					| {album_num}     | (only when downloading albums) Incrementing track number
 | 
				
			||||||
 | 
					| {playlist}      | (only when downloading playlists) Name of the playlist 
 | 
				
			||||||
 | 
					| {playlist_num}  | (only when downloading playlists) Incrementing track number
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example values could be:
 | 
				
			||||||
 | 
					~~~~
 | 
				
			||||||
 | 
					{playlist}/{artist} - {song_name}.{ext}
 | 
				
			||||||
 | 
					{playlist}/{playlist_num} - {artist} - {song_name}.{ext}
 | 
				
			||||||
 | 
					Liked Songs/{artist} - {song_name}.{ext}
 | 
				
			||||||
 | 
					{artist} - {song_name}.{ext}
 | 
				
			||||||
 | 
					{artist}/{album}/{album_num} - {artist} - {song_name}.{ext}
 | 
				
			||||||
 | 
					/home/user/downloads/{artist} - {song_name} [{id}].{ext}
 | 
				
			||||||
 | 
					~~~~
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### Docker Usage
 | 
					### Docker Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
				
			|||||||
@ -3,6 +3,5 @@ git+https://github.com/kokarare1212/librespot-python
 | 
				
			|||||||
music_tag
 | 
					music_tag
 | 
				
			||||||
Pillow
 | 
					Pillow
 | 
				
			||||||
protobuf
 | 
					protobuf
 | 
				
			||||||
pydub
 | 
					 | 
				
			||||||
tabulate
 | 
					tabulate
 | 
				
			||||||
tqdm
 | 
					tqdm
 | 
				
			||||||
@ -255,9 +255,7 @@ def search(search_term):
 | 
				
			|||||||
        selection = ''
 | 
					        selection = ''
 | 
				
			||||||
        print('\n> SELECT A DOWNLOAD OPTION BY ID')
 | 
					        print('\n> SELECT A DOWNLOAD OPTION BY ID')
 | 
				
			||||||
        print('> SELECT A RANGE BY ADDING A DASH BETWEEN BOTH ID\'s')
 | 
					        print('> SELECT A RANGE BY ADDING A DASH BETWEEN BOTH ID\'s')
 | 
				
			||||||
        print('> OR PARTICULAR OPTIONS BY ADDING A COMMA BETWEEN ID\'s')
 | 
					        print('> OR PARTICULAR OPTIONS BY ADDING A COMMA BETWEEN ID\'s\n')
 | 
				
			||||||
        print('> For example, typing 5 to get option 5 or 10-20 to get\nevery option from 10-20 (inclusive)\n')
 | 
					 | 
				
			||||||
        print('> Or type 10,12,15,18 to get those options in particular')
 | 
					 | 
				
			||||||
        while len(selection) == 0:
 | 
					        while len(selection) == 0:
 | 
				
			||||||
            selection = str(input('ID(s): '))
 | 
					            selection = str(input('ID(s): '))
 | 
				
			||||||
        inputs = split_input(selection)
 | 
					        inputs = split_input(selection)
 | 
				
			||||||
 | 
				
			|||||||
@ -70,9 +70,7 @@ def download_from_user_playlist():
 | 
				
			|||||||
    selection = ''
 | 
					    selection = ''
 | 
				
			||||||
    print('\n> SELECT A PLAYLIST BY ID')
 | 
					    print('\n> SELECT A PLAYLIST BY ID')
 | 
				
			||||||
    print('> SELECT A RANGE BY ADDING A DASH BETWEEN BOTH ID\'s')
 | 
					    print('> SELECT A RANGE BY ADDING A DASH BETWEEN BOTH ID\'s')
 | 
				
			||||||
    print('> OR PARTICULAR OPTIONS BY ADDING A COMMA BETWEEN ID\'s')
 | 
					    print('> OR PARTICULAR OPTIONS BY ADDING A COMMA BETWEEN ID\'s\n')
 | 
				
			||||||
    print('> For example, typing 10 to get one playlist or 10-20 to get\nevery playlist from 10-20 (inclusive)\n')
 | 
					 | 
				
			||||||
    print('> Or type 10,12,15,18 to get those playlists in particular')
 | 
					 | 
				
			||||||
    while len(selection) == 0:
 | 
					    while len(selection) == 0:
 | 
				
			||||||
        selection = str(input('ID(s): '))
 | 
					        selection = str(input('ID(s): '))
 | 
				
			||||||
    playlist_choices = map(int, split_input(selection))
 | 
					    playlist_choices = map(int, split_input(selection))
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,4 @@
 | 
				
			|||||||
 | 
					import math
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
import time
 | 
					import time
 | 
				
			||||||
@ -6,15 +7,14 @@ from typing import Any, Tuple, List
 | 
				
			|||||||
from librespot.audio.decoders import AudioQuality
 | 
					from librespot.audio.decoders import AudioQuality
 | 
				
			||||||
from librespot.metadata import TrackId
 | 
					from librespot.metadata import TrackId
 | 
				
			||||||
from ffmpy import FFmpeg
 | 
					from ffmpy import FFmpeg
 | 
				
			||||||
from pydub import AudioSegment
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from const import TRACKS, ALBUM, NAME, ITEMS, DISC_NUMBER, TRACK_NUMBER, IS_PLAYABLE, ARTISTS, IMAGES, URL, \
 | 
					from const import TRACKS, ALBUM, NAME, ITEMS, DISC_NUMBER, TRACK_NUMBER, IS_PLAYABLE, ARTISTS, IMAGES, URL, \
 | 
				
			||||||
    RELEASE_DATE, ID, TRACKS_URL, SAVED_TRACKS_URL, TRACK_STATS_URL, CODEC_MAP, EXT_MAP, DURATION_MS
 | 
					    RELEASE_DATE, ID, TRACKS_URL, SAVED_TRACKS_URL, TRACK_STATS_URL, CODEC_MAP, EXT_MAP, DURATION_MS
 | 
				
			||||||
from termoutput import Printer, PrintChannel
 | 
					from termoutput import Printer, PrintChannel
 | 
				
			||||||
from utils import fix_filename, set_audio_tags, set_music_thumbnail, create_download_directory, \
 | 
					from utils import fix_filename, set_audio_tags, set_music_thumbnail, create_download_directory, \
 | 
				
			||||||
    get_directory_song_ids, add_to_directory_song_ids, get_previously_downloaded, add_to_archive
 | 
					    get_directory_song_ids, add_to_directory_song_ids, get_previously_downloaded, add_to_archive, fmt_seconds
 | 
				
			||||||
from zspotify import ZSpotify
 | 
					from zspotify import ZSpotify
 | 
				
			||||||
 | 
					import traceback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_saved_tracks() -> list:
 | 
					def get_saved_tracks() -> list:
 | 
				
			||||||
    """ Returns user's saved tracks """
 | 
					    """ Returns user's saved tracks """
 | 
				
			||||||
@ -133,6 +133,8 @@ def download_track(mode: str, track_id: str, extra_keys={}, disable_progressbar=
 | 
				
			|||||||
                    create_download_directory(filedir)
 | 
					                    create_download_directory(filedir)
 | 
				
			||||||
                    total_size = stream.input_stream.size
 | 
					                    total_size = stream.input_stream.size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    time_start = time.time()
 | 
				
			||||||
 | 
					                    downloaded = 0
 | 
				
			||||||
                    with open(filename, 'wb') as file, Printer.progress(
 | 
					                    with open(filename, 'wb') as file, Printer.progress(
 | 
				
			||||||
                            desc=song_name,
 | 
					                            desc=song_name,
 | 
				
			||||||
                            total=total_size,
 | 
					                            total=total_size,
 | 
				
			||||||
@ -141,18 +143,25 @@ def download_track(mode: str, track_id: str, extra_keys={}, disable_progressbar=
 | 
				
			|||||||
                            unit_divisor=1024,
 | 
					                            unit_divisor=1024,
 | 
				
			||||||
                            disable=disable_progressbar
 | 
					                            disable=disable_progressbar
 | 
				
			||||||
                    ) as p_bar:
 | 
					                    ) as p_bar:
 | 
				
			||||||
                        pause = duration_ms / ZSpotify.CONFIG.get_chunk_size()
 | 
					 | 
				
			||||||
                        for chunk in range(int(total_size / ZSpotify.CONFIG.get_chunk_size()) + 1):
 | 
					                        for chunk in range(int(total_size / ZSpotify.CONFIG.get_chunk_size()) + 1):
 | 
				
			||||||
                            data = stream.input_stream.stream().read(ZSpotify.CONFIG.get_chunk_size())
 | 
					                            data = stream.input_stream.stream().read(ZSpotify.CONFIG.get_chunk_size())
 | 
				
			||||||
                            p_bar.update(file.write(data))
 | 
					                            p_bar.update(file.write(data))
 | 
				
			||||||
 | 
					                            downloaded += len(data)
 | 
				
			||||||
                            if ZSpotify.CONFIG.get_download_real_time():
 | 
					                            if ZSpotify.CONFIG.get_download_real_time():
 | 
				
			||||||
                                time.sleep(pause)
 | 
					                                delta_real = time.time() - time_start
 | 
				
			||||||
 | 
					                                delta_want = (downloaded / total_size) * (duration_ms/1000)
 | 
				
			||||||
 | 
					                                if delta_want > delta_real:
 | 
				
			||||||
 | 
					                                    time.sleep(delta_want - delta_real)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    time_downloaded = time.time()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    convert_audio_format(filename)
 | 
					                    convert_audio_format(filename)
 | 
				
			||||||
                    set_audio_tags(filename, artists, name, album_name, release_year, disc_number, track_number)
 | 
					                    set_audio_tags(filename, artists, name, album_name, release_year, disc_number, track_number)
 | 
				
			||||||
                    set_music_thumbnail(filename, image_url)
 | 
					                    set_music_thumbnail(filename, image_url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    Printer.print(PrintChannel.DOWNLOADS, f'###   Downloaded "{song_name}" to "{os.path.relpath(filename, os.path.dirname(__file__))}"   ###' + "\n")
 | 
					                    time_finished = time.time()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Printer.print(PrintChannel.DOWNLOADS, f'###   Downloaded "{song_name}" to "{os.path.relpath(filename, os.path.dirname(__file__))}" in {fmt_seconds(time_downloaded - time_start)} (plus {fmt_seconds(time_finished - time_downloaded)} converting)   ###' + "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    # add song id to archive file
 | 
					                    # add song id to archive file
 | 
				
			||||||
                    if ZSpotify.CONFIG.get_skip_previously_downloaded():
 | 
					                    if ZSpotify.CONFIG.get_skip_previously_downloaded():
 | 
				
			||||||
@ -166,6 +175,7 @@ def download_track(mode: str, track_id: str, extra_keys={}, disable_progressbar=
 | 
				
			|||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            Printer.print(PrintChannel.ERRORS, '###   SKIPPING: ' + song_name + ' (GENERAL DOWNLOAD ERROR)   ###')
 | 
					            Printer.print(PrintChannel.ERRORS, '###   SKIPPING: ' + song_name + ' (GENERAL DOWNLOAD ERROR)   ###')
 | 
				
			||||||
            Printer.print(PrintChannel.ERRORS, str(e) + "\n")
 | 
					            Printer.print(PrintChannel.ERRORS, str(e) + "\n")
 | 
				
			||||||
 | 
					            Printer.print(PrintChannel.ERRORS, "".join(traceback.TracebackException.from_exception(e).format()) + "\n")
 | 
				
			||||||
            if os.path.exists(filename):
 | 
					            if os.path.exists(filename):
 | 
				
			||||||
                os.remove(filename)
 | 
					                os.remove(filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,5 @@
 | 
				
			|||||||
import datetime
 | 
					import datetime
 | 
				
			||||||
 | 
					import math
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import platform
 | 
					import platform
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
@ -30,6 +31,7 @@ def create_download_directory(download_path: str) -> None:
 | 
				
			|||||||
        with open(hidden_file_path, 'w', encoding='utf-8') as f:
 | 
					        with open(hidden_file_path, 'w', encoding='utf-8') as f:
 | 
				
			||||||
            pass
 | 
					            pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_previously_downloaded() -> List[str]:
 | 
					def get_previously_downloaded() -> List[str]:
 | 
				
			||||||
    """ Returns list of all time downloaded songs """
 | 
					    """ Returns list of all time downloaded songs """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -42,6 +44,7 @@ def get_previously_downloaded() -> List[str]:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return ids
 | 
					    return ids
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def add_to_archive(song_id: str, filename: str, author_name: str, song_name: str) -> None:
 | 
					def add_to_archive(song_id: str, filename: str, author_name: str, song_name: str) -> None:
 | 
				
			||||||
    """ Adds song id to all time installed songs archive """
 | 
					    """ Adds song id to all time installed songs archive """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -54,6 +57,7 @@ def add_to_archive(song_id: str, filename: str, author_name: str, song_name: str
 | 
				
			|||||||
        with open(archive_path, 'w', encoding='utf-8') as file:
 | 
					        with open(archive_path, 'w', encoding='utf-8') as file:
 | 
				
			||||||
            file.write(f'{song_id}\t{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\t{author_name}\t{song_name}\t{filename}\n')
 | 
					            file.write(f'{song_id}\t{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\t{author_name}\t{song_name}\t{filename}\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_directory_song_ids(download_path: str) -> List[str]:
 | 
					def get_directory_song_ids(download_path: str) -> List[str]:
 | 
				
			||||||
    """ Gets song ids of songs in directory """
 | 
					    """ Gets song ids of songs in directory """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,6 +70,7 @@ def get_directory_song_ids(download_path: str) -> List[str]:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return song_ids
 | 
					    return song_ids
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def add_to_directory_song_ids(download_path: str, song_id: str, filename: str, author_name: str, song_name: str) -> None:
 | 
					def add_to_directory_song_ids(download_path: str, song_id: str, filename: str, author_name: str, song_name: str) -> None:
 | 
				
			||||||
    """ Appends song_id to .song_ids file in directory """
 | 
					    """ Appends song_id to .song_ids file in directory """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -75,6 +80,7 @@ def add_to_directory_song_ids(download_path: str, song_id: str, filename: str, a
 | 
				
			|||||||
    with open(hidden_file_path, 'a', encoding='utf-8') as file:
 | 
					    with open(hidden_file_path, 'a', encoding='utf-8') as file:
 | 
				
			||||||
        file.write(f'{song_id}\t{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\t{author_name}\t{song_name}\t{filename}\n')
 | 
					        file.write(f'{song_id}\t{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\t{author_name}\t{song_name}\t{filename}\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_downloaded_song_duration(filename: str) -> float:
 | 
					def get_downloaded_song_duration(filename: str) -> float:
 | 
				
			||||||
    """ Returns the downloaded file's duration in seconds """
 | 
					    """ Returns the downloaded file's duration in seconds """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -251,3 +257,26 @@ def fix_filename(name):
 | 
				
			|||||||
    True
 | 
					    True
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    return re.sub(r'[/\\:|<>"?*\0-\x1f]|^(AUX|COM[1-9]|CON|LPT[1-9]|NUL|PRN)(?![^.])|^\s|[\s.]$', "_", str(name), flags=re.IGNORECASE)
 | 
					    return re.sub(r'[/\\:|<>"?*\0-\x1f]|^(AUX|COM[1-9]|CON|LPT[1-9]|NUL|PRN)(?![^.])|^\s|[\s.]$', "_", str(name), flags=re.IGNORECASE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def fmt_seconds(secs: float) -> str:
 | 
				
			||||||
 | 
					    val = math.floor(secs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    s = math.floor(val % 60)
 | 
				
			||||||
 | 
					    val -= s
 | 
				
			||||||
 | 
					    val /= 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    m = math.floor(val % 60)
 | 
				
			||||||
 | 
					    val -= m
 | 
				
			||||||
 | 
					    val /= 60
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    h = math.floor(val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if h == 0 and m == 0 and s == 0:
 | 
				
			||||||
 | 
					        return "0"
 | 
				
			||||||
 | 
					    elif h == 0 and m == 0:
 | 
				
			||||||
 | 
					        return f'{s}'.zfill(2)
 | 
				
			||||||
 | 
					    elif h == 0:
 | 
				
			||||||
 | 
					        return f'{m}'.zfill(2) + ':' + f'{s}'.zfill(2)
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        return f'{h}'.zfill(2) + ':' + f'{m}'.zfill(2) + ':' + f'{s}'.zfill(2)
 | 
				
			||||||
 | 
				
			|||||||
@ -49,7 +49,8 @@ class ZSpotify:
 | 
				
			|||||||
                user_name = input('Username: ')
 | 
					                user_name = input('Username: ')
 | 
				
			||||||
            password = getpass()
 | 
					            password = getpass()
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
                cls.SESSION = Session.Builder().user_pass(user_name, password).create()
 | 
					                conf = Session.Configuration.Builder().set_stored_credential_file(cred_location).build()
 | 
				
			||||||
 | 
					                cls.SESSION = Session.Builder(conf).user_pass(user_name, password).create()
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
            except RuntimeError:
 | 
					            except RuntimeError:
 | 
				
			||||||
                pass
 | 
					                pass
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user