1
1
import os
2
2
import re
3
+ from threading import Thread
3
4
import time
4
5
import uuid
5
6
from typing import Any , Tuple , List
8
9
from librespot .metadata import TrackId
9
10
from ffmpy import FFmpeg
10
11
11
- from const import TRACKS , ALBUM , NAME , ITEMS , DISC_NUMBER , TRACK_NUMBER , IS_PLAYABLE , ARTISTS , IMAGES , URL , \
12
+ from const import TRACKS , ALBUM , GENRES , GENRE , NAME , ITEMS , DISC_NUMBER , TRACK_NUMBER , IS_PLAYABLE , ARTISTS , IMAGES , URL , \
12
13
RELEASE_DATE , ID , TRACKS_URL , SAVED_TRACKS_URL , TRACK_STATS_URL , CODEC_MAP , EXT_MAP , DURATION_MS
13
14
from termoutput import Printer , PrintChannel
14
15
from utils import fix_filename , set_audio_tags , set_music_thumbnail , create_download_directory , \
15
16
get_directory_song_ids , add_to_directory_song_ids , get_previously_downloaded , add_to_archive , fmt_seconds
16
17
from zspotify import ZSpotify
17
18
import traceback
18
19
20
+ from utils import Loader
21
+
19
22
def get_saved_tracks () -> list :
20
23
""" Returns user's saved tracks """
21
24
songs = []
@@ -33,17 +36,32 @@ def get_saved_tracks() -> list:
33
36
return songs
34
37
35
38
36
- def get_song_info (song_id ) -> Tuple [List [str ], str , str , Any , Any , Any , Any , Any , Any , int ]:
39
+ def get_song_info (song_id ) -> Tuple [List [str ], List [ str ], str , str , Any , Any , Any , Any , Any , Any , int ]:
37
40
""" Retrieves metadata for downloaded songs """
38
- (raw , info ) = ZSpotify .invoke_url (f'{ TRACKS_URL } ?ids={ song_id } &market=from_token' )
41
+ with Loader ("Fetching track information..." ):
42
+ (raw , info ) = ZSpotify .invoke_url (f'{ TRACKS_URL } ?ids={ song_id } &market=from_token' )
39
43
40
44
if not TRACKS in info :
41
45
raise ValueError (f'Invalid response from TRACKS_URL:\n { raw } ' )
42
46
43
47
try :
44
48
artists = []
49
+ genres = []
45
50
for data in info [TRACKS ][0 ][ARTISTS ]:
46
51
artists .append (data [NAME ])
52
+ # query artist genres via href, which will be the api url
53
+ with Loader ("Fetching artist information..." ):
54
+ (raw , artistInfo ) = ZSpotify .invoke_url (f'{ data ["href" ]} ' )
55
+ if ZSpotify .CONFIG .get_allGenres () and len (artistInfo [GENRES ]) > 0 :
56
+ for genre in artistInfo [GENRES ]:
57
+ genres .append (genre )
58
+ elif len (artistInfo [GENRES ]) > 0 :
59
+ genres .append (artistInfo [GENRES ][0 ])
60
+
61
+ if len (genres ) == 0 :
62
+ Printer .print (PrintChannel .SKIPS , '### No Genre found.' )
63
+ genres .append ('' )
64
+
47
65
album_name = info [TRACKS ][0 ][ALBUM ][NAME ]
48
66
name = info [TRACKS ][0 ][NAME ]
49
67
image_url = info [TRACKS ][0 ][ALBUM ][IMAGES ][0 ][URL ]
@@ -54,7 +72,7 @@ def get_song_info(song_id) -> Tuple[List[str], str, str, Any, Any, Any, Any, Any
54
72
is_playable = info [TRACKS ][0 ][IS_PLAYABLE ]
55
73
duration_ms = info [TRACKS ][0 ][DURATION_MS ]
56
74
57
- return artists , album_name , name , image_url , release_year , disc_number , track_number , scraped_song_id , is_playable , duration_ms
75
+ return artists , genres , album_name , name , image_url , release_year , disc_number , track_number , scraped_song_id , is_playable , duration_ms
58
76
except Exception as e :
59
77
raise ValueError (f'Failed to parse TRACKS_URL response: { str (e )} \n { raw } ' )
60
78
@@ -82,9 +100,12 @@ def download_track(mode: str, track_id: str, extra_keys={}, disable_progressbar=
82
100
try :
83
101
output_template = ZSpotify .CONFIG .get_output (mode )
84
102
85
- (artists , album_name , name , image_url , release_year , disc_number ,
103
+ (artists , genres , album_name , name , image_url , release_year , disc_number ,
86
104
track_number , scraped_song_id , is_playable , duration_ms ) = get_song_info (track_id )
87
-
105
+
106
+ prepareDownloadLoader = Loader ("Preparing download..." );
107
+ prepareDownloadLoader .start ()
108
+
88
109
song_name = fix_filename (artists [0 ]) + ' - ' + fix_filename (name )
89
110
90
111
for k in extra_keys :
@@ -131,12 +152,15 @@ def download_track(mode: str, track_id: str, extra_keys={}, disable_progressbar=
131
152
else :
132
153
try :
133
154
if not is_playable :
155
+ prepareDownloadLoader .stop ();
134
156
Printer .print (PrintChannel .SKIPS , '\n ### SKIPPING: ' + song_name + ' (SONG IS UNAVAILABLE) ###' + "\n " )
135
157
else :
136
158
if check_id and check_name and ZSpotify .CONFIG .get_skip_existing_files ():
159
+ prepareDownloadLoader .stop ();
137
160
Printer .print (PrintChannel .SKIPS , '\n ### SKIPPING: ' + song_name + ' (SONG ALREADY EXISTS) ###' + "\n " )
138
161
139
162
elif check_all_time and ZSpotify .CONFIG .get_skip_previously_downloaded ():
163
+ prepareDownloadLoader .stop ();
140
164
Printer .print (PrintChannel .SKIPS , '\n ### SKIPPING: ' + song_name + ' (SONG ALREADY DOWNLOADED ONCE) ###' + "\n " )
141
165
142
166
else :
@@ -147,6 +171,8 @@ def download_track(mode: str, track_id: str, extra_keys={}, disable_progressbar=
147
171
create_download_directory (filedir )
148
172
total_size = stream .input_stream .size
149
173
174
+ prepareDownloadLoader .stop ();
175
+
150
176
time_start = time .time ()
151
177
downloaded = 0
152
178
with open (filename_temp , 'wb' ) as file , Printer .progress (
@@ -170,7 +196,7 @@ def download_track(mode: str, track_id: str, extra_keys={}, disable_progressbar=
170
196
time_downloaded = time .time ()
171
197
172
198
convert_audio_format (filename_temp )
173
- set_audio_tags (filename_temp , artists , name , album_name , release_year , disc_number , track_number )
199
+ set_audio_tags (filename_temp , artists , genres , name , album_name , release_year , disc_number , track_number )
174
200
set_music_thumbnail (filename_temp , image_url )
175
201
176
202
if filename_temp != filename :
@@ -196,8 +222,8 @@ def download_track(mode: str, track_id: str, extra_keys={}, disable_progressbar=
196
222
Printer .print (PrintChannel .ERRORS , "" .join (traceback .TracebackException .from_exception (e ).format ()) + "\n " )
197
223
if os .path .exists (filename_temp ):
198
224
os .remove (filename_temp )
199
-
200
-
225
+
226
+ prepareDownloadLoader . stop ()
201
227
def convert_audio_format (filename ) -> None :
202
228
""" Converts raw audio into playable file """
203
229
temp_filename = f'{ os .path .splitext (filename )[0 ]} .tmp'
@@ -224,6 +250,9 @@ def convert_audio_format(filename) -> None:
224
250
inputs = {temp_filename : None },
225
251
outputs = {filename : output_params }
226
252
)
227
- ff_m .run ()
253
+
254
+ with Loader ("Converting file..." ):
255
+ ff_m .run ()
256
+
228
257
if os .path .exists (temp_filename ):
229
258
os .remove (temp_filename )
0 commit comments