def downloadArtistAlbum(self, includeSingles=True, artistID=None): while True: print("-------------ARTIST ALBUM--------------") if artistID is not None: sID = artistID else: sID = printChoice("Enter Artist ID(Enter '0' go back):", True, 0) if sID == 0: return array = self.tool.getArtistAlbum(sID, includeSingles) if self.tool.errmsg != "": printErr(0, "Get AlbumList Err! " + self.tool.errmsg) continue redownload = True if artistID is None: check = printChoice("Skip downloaded files?(y/n):") if not cmdHelper.isInputYes(check): redownload = False for index, item in enumerate(array): print("----Album[{0}/{1}]----".format(index + 1, len(array))) self.downloadAlbum(item['id'], redownload) if artistID is not None: # Break out of the function if we are only downloading one artist's albums return
def logIn(username="", password=""): cf = TidalConfig() if username == "" or password == "": print("----------------LogIn------------------") username = myinput("username:"******"password:"******"" and account2.errmsg != "": printErr(0, account.errmsg) return False elif account.errmsg != "": account = account2 elif account2.errmsg != "": account2 = account cf.set_account(username, password, account.session_id, account.country_code, account.user_id, account2.session_id) Curtime = time.time() cf.set_lastlogintime(str(Curtime)) return True
def logIn(username="", password=""): if username == "" or password == "": print("----------------LogIn------------------") username = myinput("username:"******"password:"******"": # printErr(0, account.errmsg) # return False # if account2.errmsg != "": # printErr(0, account2.errmsg) # return False if account.errmsg != "" and account2.errmsg != "": printErr(0, account.errmsg) return False elif account.errmsg != "": account = account2 elif account2.errmsg != "": account2 = account cf = TidalConfig() cf.set_account(username, password, account.session_id, account.country_code, account.user_id, account2.session_id) Curtime = time.time() cf.set_lastlogintime(str(Curtime)) return True
def __thradfunc_dl(self, paraList): count = 1 printRet = True pstr = paraList['title'] + "(Download Err!)" redownload = True needDl = True bIsSuccess = False albumInfo = None index = None coverpath = None if 'redownload' in paraList: redownload = paraList['redownload'] if 'retry' in paraList: count = count + paraList['retry'] if 'show' in paraList: printRet = paraList['show'] if 'album' in paraList: albumInfo = paraList['album'] if 'index' in paraList: index = paraList['index'] if 'coverpath' in paraList: coverpath = paraList['coverpath'] if redownload is False: needDl = self.__isNeedDownload(paraList['path'], paraList['url']) if needDl: try: while count > 0: count = count - 1 check = netHelper.downloadFile(paraList['url'], paraList['path']) if check is True: if paraList['key'] == '': break key, nonce = decrypt_security_token(paraList['key']) decrypt_file(paraList['path'], key, nonce) break if check: self.tool.setTrackMetadata(paraList['trackinfo'], paraList['path'], albumInfo, index, coverpath) pstr = paraList['title'] bIsSuccess = True except: pass else: pstr = paraList['title'] bIsSuccess = True if printRet: if (bIsSuccess): printSUCCESS(14, pstr) else: printErr(14, pstr) return
def downloadByFile(self, path): if not os.path.exists(path): return arr = self.tool.parseFile(path) print("----------------FILE------------------") print("[Number of albums] %s" % (len(arr['album']))) print("[Number of artists] %s" % (len(arr['artist']))) print("[Number of tracks] %s" % (len(arr['track']))) print("[Number of videos] %s" % (len(arr['video']))) print("[Number of URLs] %s" % (len(arr['url']))) if len(arr['album']) > 0: redownload = True check = printChoice("Skip downloaded files?(y/n):") if not cmdHelper.isInputYes(check): redownload = False for index, item in enumerate(arr['album']): print("----Album[{0}/{1}]----".format(index + 1, len(arr['album']))) print("[ID] %s" % (item)) self.downloadAlbum(item, redownload) for index, item in enumerate(arr['artist']): print(index) print("----Artist[{0}/{1}]----".format(index + 1, len(arr['artist']))) print("[ID] %s" % (item)) includeSingles = self.config.includesingle == "True" self.downloadArtistAlbum(includeSingles, item) for index, item in enumerate(arr['track']): print("----Track[{0}/{1}]----".format(index + 1, len(arr['track']))) print("[ID] %s" % (item)) self.downloadTrack(item) for index, item in enumerate(arr['video']): print("----Video[{0}/{1}]----".format(index + 1, len(arr['video']))) print("[ID] %s" % (item)) self.downloadVideo(item) for index, item in enumerate(arr['url']): print("----Url[{0}/{1}]----".format(index + 1, len(arr['url']))) print("[link] %s" % (item)) stype, sid = self.tool.parseLink(item) if stype is None or sid is None: printErr(14, 'Link can`t parse!') continue print("[ID] %s" % (sid)) if stype == "album": print("[Type] %s" % ("album")) self.downloadAlbum(sid) if stype == "track": print("[Type] %s" % ("track")) self.downloadTrack(sid) if stype == "video": print("[Type] %s" % ("video")) self.downloadVideo(sid)
def logIn(username = "", password = ""): if username == "" or password == "": print("----------------LogIn------------------") username = myinput("username:"******"password:"******"": printErr(0, account.errmsg) return False cf = TidalConfig() cf.set_account(username, password, account.session_id, account.country_code, account.user_id) return True
def setAccessToken(): print("------------setAccessToken------------------") account = TidalMobileSession() cf = TidalConfig() while True: account.access_token = myinput("AccessToken(Enter '0' cancel):") if account.access_token == '0': break if account.valid() is True: account.getCountryCode() cf.set_account2(cf.username, cf.password, account.access_token, account.country_code, account.user_id) break else: printErr(0, "AccessToken is not valid!")
def downloadArtistAlbum(self): while True: print("-------------ARTIST ALBUM--------------") sID = printChoice("Enter ArtistID(Enter '0' go back):", True, 0) if sID == 0: return array = self.tool.getArtistAlbum(sID) if self.tool.errmsg != "": printErr(0, "Get AlbumList Err! " + self.tool.errmsg) continue for index, item in enumerate(array): print("----Album[{0}/{1}]----".format(index, len(array))) self.downloadAlbum(item['id'])
def downloadArtistAlbum(self): while True: print("-------------ARTIST ALBUM--------------") sID = printChoice("Enter ArtistID(Enter '0' go back):", True, 0) if sID == 0: return array = self.tool.getArtistAlbum(sID) if self.tool.errmsg != "": printErr(0, "Get AlbumList Err! " + self.tool.errmsg) continue redownload = True check = printChoice("Skip downloaded files?(y/n):") if not cmdHelper.isInputYes(check): redownload = False for index, item in enumerate(array): print("----Album[{0}/{1}]----".format(index + 1, len(array))) self.downloadAlbum(item['id'], redownload)
def downloadByFile(self, path): if not os.path.exists(path): return arr = self.tool.parseFile(path) print("----------------FILE------------------") print("[NumOfAlbum] %s" % (len(arr['album']))) print("[NumOfTrack] %s" % (len(arr['track']))) print("[NumOfVideo] %s" % (len(arr['video']))) print("[NumOfUrl] %s" % (len(arr['url']))) for index, item in enumerate(arr['album']): print("----Album[{0}/{1}]----".format(index + 1, len(arr['album']))) print("[ID] %s" % (item)) self.downloadAlbum(item) for index, item in enumerate(arr['track']): print("----Track[{0}/{1}]----".format(index + 1, len(arr['track']))) print("[ID] %s" % (item)) self.downloadTrack(item) for index, item in enumerate(arr['video']): print("----Video[{0}/{1}]----".format(index + 1, len(arr['video']))) print("[ID] %s" % (item)) self.downloadVideo(item) for index, item in enumerate(arr['url']): print("----Url[{0}/{1}]----".format(index + 1, len(arr['url']))) print("[link] %s" % (item)) stype, sid = self.tool.parseLink(item) if stype is None or sid is None: printErr(14, 'Link can`t parse!') continue print("[ID] %s" % (sid)) if stype == "album": print("[Type] %s" % ("album")) self.downloadAlbum(sid) if stype == "track": print("[Type] %s" % ("track")) self.downloadTrack(sid) if stype == "video": print("[Type] %s" % ("video")) self.downloadVideo(sid)
def downloadTrack(self): while True: targetDir = self.config.outputdir + "/Track/" print("----------------TRACK------------------") sID = printChoice("Enter TrackID(Enter '0' go back):", True, 0) if sID == 0: return aTrackInfo = self.tool.getTrack(sID) if self.tool.errmsg != "": printErr(0, "Get TrackInfo Err! " + self.tool.errmsg) return print("[TrackTitle ] %s" % (aTrackInfo['title'])) print("[Duration ] %s" % (aTrackInfo['duration'])) print("[TrackNumber] %s" % (aTrackInfo['trackNumber'])) print("[Version ] %s\n" % (aTrackInfo['version'])) # download streamInfo = self.tool.getStreamUrl(sID, self.config.quality) if self.tool.errmsg != "": printErr( 14, aTrackInfo['title'] + "(Get Stream Url Err!" + self.tool.errmsg + ")") continue fileType = self._getSongExtension(streamInfo['url']) filePath = targetDir + "/" + pathHelper.replaceLimitChar( aTrackInfo['title'], '-') + fileType paraList = { 'title': aTrackInfo['title'], 'trackinfo': aTrackInfo, 'url': streamInfo['url'], 'path': filePath, 'retry': 3, 'key': streamInfo['encryptionKey'] } self.thread.start(self.__thradfunc_dl, paraList) # wait all download thread self.thread.waitAll() self.tool.removeTmpFile(targetDir) return
def downloadFavorite(self): targetDir = self.config.outputdir + "/Favorite/" pathHelper.mkdirs(targetDir) trackList, videoList = self.tool.getFavorite(self.config.userid) if self.tool.errmsg != "": printErr(0, "Get FavoriteList Err! " + self.tool.errmsg) return print("[NumberOfTracks] %s" % (len(trackList))) print("[NumberOfVideos] %s" % (len(videoList))) # download track for item in trackList: item = item['item'] streamInfo = self.tool.getStreamUrl(str(item['id']), self.config.quality) if self.tool.errmsg != "" or not streamInfo: printErr( 14, item['title'] + "(Get Stream Url Err!!" + self.tool.errmsg + ")") continue fileType = self._getSongExtension(streamInfo['url']) filePath = targetDir + '/' + pathHelper.replaceLimitChar( item['title'], '-') + fileType aAlbumInfo = self.tool.getAlbum(item['album']['id']) paraList = { 'album': aAlbumInfo, 'title': item['title'], 'trackinfo': item, 'url': streamInfo['url'], 'path': filePath, 'retry': 3, 'key': streamInfo['encryptionKey'], 'codec': streamInfo['codec'] } self.thread.start(self.__thradfunc_dl, paraList) self.thread.waitAll() # download video for item in videoList: item = item['item'] filePath = targetDir + '/' + pathHelper.replaceLimitChar( item['title'], '-') + ".mp4" filePath = os.path.abspath(filePath) if os.access(filePath, 0): os.remove(filePath) resolutionList, urlList = self.tool.getVideoResolutionList( item['id']) selectIndex = self.__getVideoResolutionIndex(resolutionList) if self.ffmpeg.mergerByM3u8_Multithreading2( urlList[int(selectIndex)], filePath, showprogress=self.showpro): printSUCCESS(14, item['title']) else: printErr(14, item['title']) return
def downloadTrack(self, track_id=None): while_count = 9999 while while_count > 0: while_count -= 1 if track_id is not None: while_count = 0 sID = track_id else: print("----------------TRACK------------------") sID = printChoice("Enter TrackID(Enter '0' go back):", True, 0) if sID == 0: return aTrackInfo = self.tool.getTrack(sID) if self.tool.errmsg != "": printErr(0,"Get TrackInfo Err! " + self.tool.errmsg) return aAlbumInfo = self.tool.getAlbum(aTrackInfo['album']['id']) if self.tool.errmsg != "": printErr(0,"Get TrackInfo Err! " + self.tool.errmsg) return # t = self.tool.getTrackContributors(sID) print("[AlbumTitle ] %s" % (aAlbumInfo['title'])) print("[TrackTitle ] %s" % (aTrackInfo['title'])) print("[Duration ] %s" % (aTrackInfo['duration'])) print("[TrackNumber] %s" % (aTrackInfo['trackNumber'])) print("[Version ] %s\n" % (aTrackInfo['version'])) # Creat OutputDir targetDir = self.__creatAlbumDir(aAlbumInfo) # download cover coverPath = targetDir + '/' + pathHelper.replaceLimitChar(aAlbumInfo['title'], '-') + '.jpg' coverUrl = self.tool.getAlbumArtworkUrl(aAlbumInfo['cover']) netHelper.downloadFile(coverUrl, coverPath) # download streamInfo = self.tool.getStreamUrl(sID, self.config.quality) if self.tool.errmsg != "": printErr(14, aTrackInfo['title'] + "(Get Stream Url Err!" + self.tool.errmsg + ")") continue fileType = self._getSongExtension(streamInfo['url']) filePath = self.__getAlbumSongSavePath(targetDir, aAlbumInfo, aTrackInfo, fileType) # filePath = targetDir + "/" + pathHelper.replaceLimitChar(aTrackInfo['title'],'-') + fileType paraList = {'album':aAlbumInfo, 'title': aTrackInfo['title'], 'trackinfo':aTrackInfo, 'url': streamInfo['url'], 'path': filePath, 'retry': 3, 'key':streamInfo['encryptionKey'], 'coverpath': coverPath} self.thread.start(self.__thradfunc_dl, paraList) # wait all download thread self.thread.waitAll() self.tool.removeTmpFile(targetDir) return
def downloadVideo(self): while True: targetDir = self.config.outputdir + "/Video/" print("----------------VIDEO------------------") sID = printChoice("Enter VideoID(Enter '0' go back):", True, 0) if sID == 0: return aVideoInfo = self.tool.getVideo(sID) if self.tool.errmsg != "": printErr(0, "Get VideoInfo Err! " + self.tool.errmsg) continue print("[Title ] %s" % (aVideoInfo['title'])) print("[Duration ] %s" % (aVideoInfo['duration'])) print("[TrackNumber] %s" % (aVideoInfo['trackNumber'])) print("[Type ] %s\n" % (aVideoInfo['type'])) # get resolution index = 0 resolutionList, urlList = self.tool.getVideoResolutionList(sID) if self.tool.errmsg != "": printErr(14, self.tool.errmsg) continue # print("-Index--Resolution--") # for item in resolutionList: # print(' ' + str(index) + " " + resolutionList[index]) # index = index + 1 # print("--------------------") # while True: # index = printChoice("Enter ResolutionIndex:", True, 0) # if index == '' or index == None or int(index) >= len(resolutionList): # printErr(0, "ResolutionIndex is err") # continue # break index = self.__getVideoResolutionIndex(resolutionList) path = targetDir + "/" + pathHelper.replaceLimitChar( aVideoInfo['title'], '-') + ".mp4" path = os.path.abspath(path) if os.access(path, 0): os.remove(path) if self.ffmpeg.mergerByM3u8_Multithreading(urlList[int(index)], path, True): printSUCCESS(14, aVideoInfo['title']) else: printErr(14, aVideoInfo['title']) return
def downloadVideo(self, video_id=None): flag = True while flag: targetDir = self.config.outputdir + "/Video/" if video_id is None: print("----------------VIDEO------------------") sID = printChoice("Enter VideoID(Enter '0' go back):", True, 0) if sID == 0: return else: flag = False sID = video_id aVideoInfo = self.tool.getVideo(sID) if self.tool.errmsg != "": printErr(0, "Get VideoInfo Err! " + self.tool.errmsg) continue print("[Title ] %s" % (aVideoInfo['title'])) print("[Duration ] %s" % (aVideoInfo['duration'])) print("[TrackNumber] %s" % (aVideoInfo['trackNumber'])) print("[Type ] %s\n" % (aVideoInfo['type'])) # get resolution index = 0 resolutionList, urlList = self.tool.getVideoResolutionList(sID) if self.tool.errmsg != "": printErr(14, self.tool.errmsg) continue index = self.__getVideoResolutionIndex(resolutionList) path = targetDir + "/" + pathHelper.replaceLimitChar( aVideoInfo['title'], '-') + ".mp4" path = os.path.abspath(path) if os.access(path, 0): os.remove(path) if self.ffmpeg.mergerByM3u8_Multithreading2( urlList[int(index)], path, True): printSUCCESS(14, aVideoInfo['title']) else: printErr(14, aVideoInfo['title']) return
def downloadAlbum(self): while True: print("----------------ALBUM------------------") sID = printChoice("Enter AlbumID(Enter '0' go back):", True, 0) if sID == 0: return aAlbumInfo = self.tool.getAlbum(sID) if self.tool.errmsg != "": printErr(0, "Get AlbumInfo Err! " + self.tool.errmsg) continue print("[Title] %s" % (aAlbumInfo['title'])) print("[SongNum] %s\n" % (aAlbumInfo['numberOfTracks'])) # Get Tracks aAlbumTracks = self.tool.getAlbumTracks(sID) if self.tool.errmsg != "": printErr(0, "Get AlbumTracks Err!" + self.tool.errmsg) return # Creat OutputDir targetDir = self.__creatAlbumDir(aAlbumInfo) # write msg string = self.tool.convertAlbumInfoToString( aAlbumInfo, aAlbumTracks) with open(targetDir + "/AlbumInfo.txt", 'w', encoding='utf-8') as fd: fd.write(string) # download cover coverPath = targetDir + '/' + pathHelper.replaceLimitChar( aAlbumInfo['title'], '-') + '.jpg' coverUrl = self.tool.getAlbumArtworkUrl(aAlbumInfo['cover']) netHelper.downloadFile(coverUrl, coverPath) # check exist files redownload = True existFiles = pathHelper.getDirFiles(targetDir) for item in existFiles: if '.txt' in item: continue if '.jpg' in item: continue check = printChoice( "Some TrackFile Exist.Is Redownload?(y/n):") if check != 'y' and check != 'yes': redownload = False break # download album tracks for item in aAlbumTracks['items']: streamInfo = self.tool.getStreamUrl(str(item['id']), self.config.quality) if self.tool.errmsg != "": printErr( 14, item['title'] + "(Get Stream Url Err!" + self.tool.errmsg + ")") continue fileType = self._getSongExtension(streamInfo['url']) filePath = self.__getAlbumSongSavePath(targetDir, aAlbumInfo, item, fileType) paraList = { 'album': aAlbumInfo, 'redownload': redownload, 'title': item['title'], 'trackinfo': item, 'url': streamInfo['url'], 'path': filePath, 'retry': 3, 'key': streamInfo['encryptionKey'] } self.thread.start(self.__thradfunc_dl, paraList) # wait all download thread self.thread.waitAll() self.tool.removeTmpFile(targetDir) return
def setting(): cf = TidalConfig() print("----------------Setting----------------") print("OutputDir :\t" + cf.outputdir) print("SoundQuality :\t" + cf.quality) print("Resolution :\t" + cf.resolution) print("ThreadNum :\t" + cf.threadnum) while True: outputdir = myinput("Outputdir(Enter '0' Unchanged):".ljust(12)) if outputdir == '0': outputdir = cf.outputdir break if os.path.isdir(outputdir) == False: printErr(0, "Path is Err!") continue break while True: index = myinputInt("Quality(0-LOW,1-HIGH,2-LOSSLESS):".ljust(12), 999) if index > 2 or index < 0: printErr(0, "Quality Err!") continue if index == 0: quality = 'LOW' if index == 1: quality = 'HIGH' if index == 2: quality = 'LOSSLESS' break while True: index = myinputInt( "Resolution(0-1080,1-720,2-480,3-360,4-240):".ljust(12), 99) if index > 4 or index < 0: printErr(0, "ThreadNum Err") continue if index == 0: resolution = '1080' if index == 1: resolution = '720' if index == 2: resolution = '480' if index == 3: resolution = '360' if index == 4: resolution = '240' break while True: threadnum = myinput("ThreadNum:".ljust(12)) if cf.valid_threadnum(threadnum) == False: printErr(0, "ThreadNum Err") continue break cf.set_outputdir(outputdir) cf.set_quality(quality) cf.set_resolution(resolution) cf.set_threadnum(threadnum) pathHelper.mkdirs(outputdir + "/Album/") pathHelper.mkdirs(outputdir + "/Playlist/") pathHelper.mkdirs(outputdir + "/Video/") pathHelper.mkdirs(outputdir + "/Favorite/") return
def setting(): cf = TidalConfig() print("----------------Settings----------------") print("Output directory :\t" + cf.outputdir) print("Sound Quality :\t" + cf.quality) print("Video Resolution :\t" + cf.resolution) print("Download Threads :\t" + cf.threadnum) print("Only M4a :\t" + cf.onlym4a) print("Show download progress :\t" + cf.showprogress + "(enable when threadnum=1)") print("Use hyphens :\t" + cf.addhyphen + "(between number and title)") print("Add year :\t" + cf.addyear + "(in album title)") print("Add explicit tag :\t" + cf.addexplicit) print("Playlist songs in artist folders :\t" + cf.plfile2arfolder + "(organized with artist folder)") print("Include singles :\t" + cf.includesingle + "(download artist album)") print("Save covers :\t" + cf.savephoto) print("ArtistName Before Track-Title :\t" + cf.artistbeforetitle) print("Add ID Before AlbumFolderName :\t" + cf.addAlbumidbeforefolder) while True: outputdir = myinput("Output directory(Enter '0' Unchanged):".ljust(12)) if outputdir == '0': outputdir = cf.outputdir break if os.path.isdir(outputdir) == False: printErr(0, "Path is Err!") continue break while True: index = myinputInt( "Download Quality(0-LOW,1-HIGH,2-LOSSLESS,3-HI_RES):".ljust(12), 999) if index > 3 or index < 0: printErr(0, "Quality Err!") continue if index == 0: quality = 'LOW' if index == 1: quality = 'HIGH' if index == 2: quality = 'LOSSLESS' if index == 3: quality = 'HI_RES' break while True: index = myinputInt( "Video resolution(0-1080,1-720,2-480,3-360,4-240):".ljust(12), 99) if index > 4 or index < 0: printErr(0, "Resolution Err") continue if index == 0: resolution = '1080' if index == 1: resolution = '720' if index == 2: resolution = '480' if index == 3: resolution = '360' if index == 4: resolution = '240' break while True: threadnum = myinput("Number of download threads:".ljust(12)) if cf.valid_threadnum(threadnum) == False: printErr(0, "ThreadNum Err") continue break status = myinputInt("Convert Mp4 to M4a(0-No, 1-Yes):".ljust(12), 0) status2 = myinputInt( "Show download progress (only available on single thread)(0-No, 1-Yes):" .ljust(12), 0) status3 = myinputInt( "Use hyphens instead of spaces in file names(0-No, 1-Yes):".ljust(12), 0) while True: index = myinputInt( "Add year to album folder names(0-No, 1-Before, 2-After):".ljust( 12), 99) if index > 2 or index < 0: printErr(0, "Addyear input Err") continue if index == 0: addyear = 'No' if index == 1: addyear = 'Before' if index == 2: addyear = 'After' break status5 = myinputInt( "Download playlist songs in artist folder structure? (0-No,1-Yes):". ljust(12), 0) status6 = myinputInt( "Add explicit tag to file names(0-No, 1-Yes):".ljust(12), 0) status7 = myinputInt( "Include singles and EPs when downloading an artist's albums (0-No, 1-Yes):" .ljust(12), 0) status8 = myinputInt("Save covers(0-No, 1-Yes):".ljust(12), 0) status9 = myinputInt( "Add artistName before trackTitle(0-No, 1-Yes):".ljust(12), 0) status10 = myinputInt( "Add ID Before AlbumFolderName(0-No, 1-Yes):".ljust(12), 0) cf.set_outputdir(outputdir) cf.set_quality(quality) cf.set_resolution(resolution) cf.set_threadnum(threadnum) cf.set_onlym4a(status) cf.set_showprogress(status2) cf.set_addhyphen(status3) cf.set_addyear(addyear) cf.set_plfile2arfolder(status5) cf.set_addexplicit(status6) cf.set_includesingle(status7) cf.set_savephoto(status8) cf.set_artistbeforetitle(status9) cf.set_addAlbumidbeforefolder(status10) pathHelper.mkdirs(outputdir + "/Album/") pathHelper.mkdirs(outputdir + "/Playlist/") pathHelper.mkdirs(outputdir + "/Video/") pathHelper.mkdirs(outputdir + "/Favorite/") return
def __thradfunc_dl(self, paraList): count = 1 printRet = True pstr = paraList['title'] + "(Download Err!)" redownload = True needDl = True bIsSuccess = False albumInfo = None index = None coverpath = None if 'redownload' in paraList: redownload = paraList['redownload'] if 'retry' in paraList: count = count + paraList['retry'] if 'show' in paraList: printRet = paraList['show'] if 'album' in paraList: albumInfo = paraList['album'] if 'index' in paraList: index = paraList['index'] if 'coverpath' in paraList: coverpath = paraList['coverpath'] if redownload is False: needDl = self.__isNeedDownload(paraList['path'], paraList['url']) # DEBUG # self.tool.setTrackMetadata(paraList['trackinfo'], paraList['path'], albumInfo, index, coverpath) showprogress = False if int(self.config.threadnum) <= 1 and self.showpro: showprogress = True Contributors = self.tool.getTrackContributors( paraList['trackinfo']['id']) if needDl: try: while count > 0: count = count - 1 check = netHelper.downloadFile(paraList['url'], paraList['path'] + '.part', showprogress=showprogress, stimeout=20) if check is True: if paraList['key'] == '': # unencrypted -> just move into place os.replace(paraList['path'] + '.part', paraList['path']) break else: # encrypted -> decrypt and remove encrypted file key, nonce = decrypt_security_token( paraList['key']) decrypt_file(paraList['path'] + '.part', paraList['path'], key, nonce) os.remove(paraList['path'] + '.part') break if check: bIsSuccess = True paraList['path'] = self.tool.covertMp4toM4a( paraList['path']) self.tool.setTrackMetadata(paraList['trackinfo'], paraList['path'], albumInfo, index, coverpath, Contributors) pstr = paraList['title'] except Exception as e: printErr(14, str(e) + " while downloading " + paraList['url']) else: pstr = paraList['title'] bIsSuccess = True if printRet: if (bIsSuccess): printSUCCESS(14, pstr) else: printErr(14, pstr) return
def downloadPlaylist(self, playlist_id=None): while True: targetDir = self.config.outputdir + "/Playlist/" if playlist_id is None: print("--------------PLAYLIST-----------------") sID = printChoice("Enter PlayListID(Enter '0' go back):") if sID == '0': return else: sID = playlist_id aPlaylistInfo, aItemInfo = self.tool.getPlaylist(sID) if self.tool.errmsg != "": printErr(0, "Get PlaylistInfo Err! " + self.tool.errmsg) return print("[Title] %s" % (aPlaylistInfo['title'])) print("[Type] %s" % (aPlaylistInfo['type'])) print("[NumberOfTracks] %s" % (aPlaylistInfo['numberOfTracks'])) print("[NumberOfVideos] %s" % (aPlaylistInfo['numberOfVideos'])) print("[Duration] %s\n" % (aPlaylistInfo['duration'])) # Creat OutputDir targetDir = targetDir + pathHelper.replaceLimitChar( aPlaylistInfo['title'], '-') targetDir = os.path.abspath(targetDir).strip() pathHelper.mkdirs(targetDir) # write msg string = self.tool.convertPlaylistInfoToString( aPlaylistInfo, aItemInfo) with codecs.open(targetDir + "/PlaylistInfo.txt", 'w', 'utf-8') as fd: fd.write(string) # download cover coverPath = targetDir + '/' + pathHelper.replaceLimitChar( aPlaylistInfo['title'], '-') + '.jpg' coverUrl = self.tool.getPlaylistArtworkUrl(aPlaylistInfo['uuid']) check = netHelper.downloadFile(coverUrl, coverPath) # download track bBreakFlag = False bFirstTime = True errIndex = [] index = 0 while bBreakFlag is False: self.check.clear() index = 0 tmpcoverpath = [] for item in aItemInfo: type = item['type'] item = item['item'] if type != 'track': continue index = index + 1 if bFirstTime is False: if self.check.isInErr(index - 1, errIndex) == False: continue streamInfo = self.tool.getStreamUrl( str(item['id']), self.config.quality) # streamInfo = self.tool.getStreamUrl(str(item['id']), 'DOLBY_ATMOS') if self.tool.errmsg != "" or not streamInfo: printErr( 14, item['title'] + "(Get Stream Url Err!!" + self.tool.errmsg + ")") continue aAlbumInfo = self.tool.getAlbum(item['album']['id']) fileType = self._getSongExtension(streamInfo['url']) # change targetDir targetDir2 = targetDir if self.config.plfile2arfolder == "True": targetDir2 = self.__creatAlbumDir(aAlbumInfo) filePath = self.__getAlbumSongSavePath( targetDir2, aAlbumInfo, item, fileType) paraList = { 'album': aAlbumInfo, 'title': item['title'], 'trackinfo': item, 'url': streamInfo['url'], 'path': filePath, 'retry': 3, 'key': streamInfo['encryptionKey'] } else: seq = self.tool.getIndexStr(index, len(aItemInfo)) filePath = targetDir2 + '/' + seq + " " + pathHelper.replaceLimitChar( item['title'], '-') + fileType paraList = { 'album': aAlbumInfo, 'index': index, 'title': item['title'], 'trackinfo': item, 'url': streamInfo['url'], 'path': filePath, 'retry': 3, 'key': streamInfo['encryptionKey'] } try: coverPath = targetDir2 + '/' + pathHelper.replaceLimitChar( aAlbumInfo['title'], '-') + '.jpg' coverUrl = self.tool.getAlbumArtworkUrl( aAlbumInfo['cover']) netHelper.downloadFile(coverUrl, coverPath) paraList['coverpath'] = coverPath tmpcoverpath.append(coverPath) except: cmdHelper.myprint( "Could not download artwork for '{}'".format( item['title']), cmdHelper.TextColor.Red, None) if self.config.onlym4a == "True": self.check.addPath(filePath.replace(".mp4", ".m4a")) else: self.check.addPath(filePath) self.thread.start(self.__thradfunc_dl, paraList) self.thread.waitAll() self.tool.removeTmpFile(targetDir) # remove cover if self.config.savephoto != 'True': for item in tmpcoverpath: pathHelper.remove(item) bBreakFlag = True bFirstTime = False # check isErr, errIndex = self.check.checkPaths() if isErr: check = printChoice( "[Err]\t\t" + str(len(errIndex)) + " Tracks Download Failed.Try Again?(y/n):") if check == 'y' or check == 'Y': bBreakFlag = False # download video for item in aItemInfo: type = item['type'] item = item['item'] if type != 'video': continue filePath = targetDir + '/' + pathHelper.replaceLimitChar( item['title'], '-') + ".mp4" filePath = os.path.abspath(filePath) if os.access(filePath, 0): os.remove(filePath) videoID = item['id'] resolutionList, urlList = self.tool.getVideoResolutionList( videoID) if urlList is None: printErr(14, item['title'] + '(' + self.tool.errmsg + ')') else: selectIndex = self.__getVideoResolutionIndex( resolutionList) if self.ffmpeg.mergerByM3u8_Multithreading2( urlList[int(selectIndex)], filePath, showprogress=self.showpro): printSUCCESS(14, item['title']) else: printErr(14, item['title'] + "(Download Or Merger Err!)") if playlist_id is not None: return return
def downloadPlaylist(self): while True: targetDir = self.config.outputdir + "/Playlist/" print("--------------PLAYLIST-----------------") sID = printChoice("Enter PlayListID(Enter '0' go back):") if sID == '0': return aPlaylistInfo, aItemInfo = self.tool.getPlaylist(sID) if self.tool.errmsg != "": printErr(0, "Get PlaylistInfo Err! " + self.tool.errmsg) return print("[Title] %s" % (aPlaylistInfo['title'])) print("[Type] %s" % (aPlaylistInfo['type'])) print("[NumberOfTracks] %s" % (aPlaylistInfo['numberOfTracks'])) print("[NumberOfVideos] %s" % (aPlaylistInfo['numberOfVideos'])) print("[Duration] %s\n" % (aPlaylistInfo['duration'])) # Creat OutputDir targetDir = targetDir + pathHelper.replaceLimitChar( aPlaylistInfo['title'], '-') targetDir = os.path.abspath(targetDir) pathHelper.mkdirs(targetDir) # write msg string = self.tool.convertPlaylistInfoToString( aPlaylistInfo, aItemInfo) with open(targetDir + "/PlaylistInfo.txt", 'w', encoding='utf-8') as fd: fd.write(string) # download track bBreakFlag = False bFirstTime = True errIndex = [] index = 0 while bBreakFlag is False: self.check.clear() index = 0 for item in aItemInfo: type = item['type'] item = item['item'] if type != 'track': continue index = index + 1 if bFirstTime is False: if self.check.isInErr(index - 1, errIndex) == False: continue streamInfo = self.tool.getStreamUrl( str(item['id']), self.config.quality) if self.tool.errmsg != "": printErr( 14, item['title'] + "(Get Stream Url Err!!" + self.tool.errmsg + ")") continue fileType = self._getSongExtension(streamInfo['url']) filePath = targetDir + '/' + pathHelper.replaceLimitChar( item['title'], '-') + fileType paraList = { 'index': index, 'title': item['title'], 'trackinfo': item, 'url': streamInfo['url'], 'path': filePath, 'retry': 3, 'key': streamInfo['encryptionKey'] } self.check.addPath(filePath) if not os.path.isfile(filePath): self.thread.start(self.__thradfunc_dl, paraList) self.thread.waitAll() self.tool.removeTmpFile(targetDir) bBreakFlag = True bFirstTime = False # check isErr, errIndex = self.check.checkPaths() if isErr: check = printChoice( "[Err]\t\t" + len(errIndex) + " Tracks Download Failed.Try Again?(y/n):") if check == 'y' or check == 'Y': bBreakFlag = False # download video for item in aItemInfo: type = item['type'] item = item['item'] if type != 'video': continue filePath = targetDir + '/' + pathHelper.replaceLimitChar( item['title'], '-') + ".mp4" filePath = os.path.abspath(filePath) if os.access(filePath, 0): os.remove(filePath) videoID = item['id'] resolutionList, urlList = self.tool.getVideoResolutionList( videoID) if urlList is None: printErr(14, item['title'] + '(' + self.tool.errmsg + ')') else: selectIndex = self.__getVideoResolutionIndex( resolutionList) if self.ffmpeg.mergerByM3u8_Multithreading( urlList[selectIndex], filePath, showprogress=False): printSUCCESS(14, item['title']) else: printErr(14, item['title'] + "(Download Or Merger Err!)") return
def setting(): cf = TidalConfig() print("----------------Settings----------------") print("OutputDir :\t" + cf.outputdir) print("SoundQuality :\t" + cf.quality) print("Resolution :\t" + cf.resolution) print("ThreadNum :\t" + cf.threadnum) print("OnlyM4a :\t" + cf.onlym4a) print("ShowProgress :\t" + cf.showprogress + "(enable when threadnum=1)") print("AddHyphen :\t" + cf.addhyphen + "(between number and title)") while True: outputdir = myinput("Outputdir(Enter '0' Unchanged):".ljust(12)) if outputdir == '0': outputdir = cf.outputdir break if os.path.isdir(outputdir) == False: printErr(0, "Path is Err!") continue break while True: index = myinputInt( "Quality(0-LOW,1-HIGH,2-LOSSLESS,3-HI_RES):".ljust(12), 999) if index > 3 or index < 0: printErr(0, "Quality Err!") continue if index == 0: quality = 'LOW' if index == 1: quality = 'HIGH' if index == 2: quality = 'LOSSLESS' if index == 3: quality = 'HI_RES' break while True: index = myinputInt( "Resolution(0-1080,1-720,2-480,3-360,4-240):".ljust(12), 99) if index > 4 or index < 0: printErr(0, "Resolution Err") continue if index == 0: resolution = '1080' if index == 1: resolution = '720' if index == 2: resolution = '480' if index == 3: resolution = '360' if index == 4: resolution = '240' break while True: threadnum = myinput("ThreadNum:".ljust(12)) if cf.valid_threadnum(threadnum) == False: printErr(0, "ThreadNum Err") continue break status = myinputInt("ConvertMp4toM4a(0-False,1-True):".ljust(12), 0) status2 = myinputInt("ShowProgress(0-False,1-True):".ljust(12), 0) status3 = myinputInt("AddHyphen(0-False,1-True):".ljust(12), 0) cf.set_outputdir(outputdir) cf.set_quality(quality) cf.set_resolution(resolution) cf.set_threadnum(threadnum) cf.set_onlym4a(status) cf.set_showprogress(status2) cf.set_addhyphen(status3) pathHelper.mkdirs(outputdir + "/Album/") pathHelper.mkdirs(outputdir + "/Playlist/") pathHelper.mkdirs(outputdir + "/Video/") pathHelper.mkdirs(outputdir + "/Favorite/") return
def downloadAlbum(self, album_id=None, redl_flag=None): while_count = 9999 while while_count > 0: while_count -= 1 if album_id is not None: while_count = 0 sID = album_id else: print("----------------ALBUM------------------") sID = printChoice("Enter AlbumID(Enter '0' go back):", True, 0) if sID == 0: return aAlbumInfo = self.tool.getAlbum(sID) if self.tool.errmsg != "": printErr(0, "Get AlbumInfo Err! " + self.tool.errmsg) continue print("[Title] %s" % (aAlbumInfo['title'])) print("[SongNum] %s\n" % (aAlbumInfo['numberOfTracks'])) # Get Tracks aAlbumTracks = self.tool.getAlbumTracks(sID) if self.tool.errmsg != "": printErr(0, "Get AlbumTracks Err!" + self.tool.errmsg) continue aAlbumVideos = self.tool.getAlbumVideos(sID) # Creat OutputDir targetDir = self.__creatAlbumDir(aAlbumInfo) # write msg string = self.tool.convertAlbumInfoToString( aAlbumInfo, aAlbumTracks) with codecs.open(targetDir + "/AlbumInfo.txt", 'w', 'utf-8') as fd: fd.write(string) # download cover coverPath = targetDir + '/' + pathHelper.replaceLimitChar( aAlbumInfo['title'], '-') + '.jpg' if aAlbumInfo['cover'] is not None: coverUrl = self.tool.getAlbumArtworkUrl(aAlbumInfo['cover']) netHelper.downloadFile(coverUrl, coverPath) # check exist files redownload = True if redl_flag is None: existFiles = pathHelper.getDirFiles(targetDir) for item in existFiles: if '.txt' in item: continue if '.jpg' in item: continue check = printChoice( "Some tracks already exist. Redownload?(y/n):") if not cmdHelper.isInputYes(check): redownload = False break else: redownload = redl_flag # download album tracks for item in aAlbumTracks['items']: streamInfo = self.tool.getStreamUrl(str(item['id']), self.config.quality) if self.tool.errmsg != "" or not streamInfo: printErr( 14, item['title'] + "(Get Stream Url Err!" + self.tool.errmsg + ")") continue fileType = self._getSongExtension(streamInfo['url']) filePath = self.__getAlbumSongSavePath(targetDir, aAlbumInfo, item, fileType) paraList = { 'album': aAlbumInfo, 'redownload': redownload, 'title': item['title'], 'trackinfo': item, 'url': streamInfo['url'], 'path': filePath, 'retry': 3, 'key': streamInfo['encryptionKey'], 'coverpath': coverPath } self.thread.start(self.__thradfunc_dl, paraList) # wait all download thread self.thread.waitAll() self.tool.removeTmpFile(targetDir) # remove cover if self.config.savephoto != 'True': pathHelper.remove(coverPath) # download video for item in aAlbumVideos: item = item['item'] filePath = targetDir + '/' + pathHelper.replaceLimitChar( item['title'], '-') + ".mp4" filePath = os.path.abspath(filePath) if os.access(filePath, 0): os.remove(filePath) try: resolutionList, urlList = self.tool.getVideoResolutionList( item['id']) selectIndex = self.__getVideoResolutionIndex( resolutionList) if self.ffmpeg.mergerByM3u8_Multithreading2( urlList[int(selectIndex)], filePath, showprogress=self.showpro): printSUCCESS(14, item['title']) else: printErr(14, item['title']) except: printErr(14, item['title']) # return return