def start(user, conf, string): __loadAPI__(user) if isNull(string): Printf.err('Please enter something.') return strings = string.split(" ") for item in strings: if isNull(item): continue if os.path.exists(item): __file__(user, conf, item) return msg, etype, obj = API.getByString(item) if etype == Type.Null or not isNull(msg): Printf.err(msg + " [" + item + "]") return if etype == Type.Album: __album__(conf, obj) if etype == Type.Track: __track__(conf, obj) if etype == Type.Video: __loadVideoAPI__(user) __video__(conf, obj) if etype == Type.Artist: __artist__(conf, obj) if etype == Type.Playlist: __playlist__(conf, obj)
def searchTrack(user, lang, field, song, conf): __loadAPI__(user) msg, obj = API.searchSong(field, song, limit=10) for item in obj: if field == 'track': print( green(f"Enter [{obj.index(item)}]: ") + f"{item.title} - {item.artist.name} - {item.album.title}") elif field == 'album': print( green(f"Enter [{obj.index(item)}]: ") + f"{item.title} - {item.artist.name}") else: print( green(f"Enter [{obj.index(item)}]: ") + f"{item.title} - {item.numberOfTracks} Songs") try: choice = int(Printf.enter(lang)) if (choice >= 0 and choice < len(obj)): if field == 'track': __track__(conf, obj[choice]) elif field == 'album': __album__(conf, obj[choice]) else: __playlist__(conf, obj[choice]) else: os.system('clear') Printf.err("Invalid option!") except ValueError: os.system('clear') Printf.err("Invalid option!")
def main(): if len(sys.argv) > 1: mainCommand() return Printf.logo() Printf.settings(CONF) checkLogin() onlineVer = getLastVersion('tidal-dl') if not isNull(onlineVer): icmp = cmpVersion(onlineVer, VERSION) if icmp > 0: Printf.info(LANG.PRINT_LATEST_VERSION + ' ' + onlineVer) while True: Printf.choices() choice = Printf.enter(LANG.PRINT_ENTER_CHOICE) if choice == "0": return elif choice == "1": checkLogin() elif choice == "2": changeSettings() elif choice == "3": checkLogout() elif choice == "4": song = Printf.enter("Enter the song name: ") searchTrack(TOKEN, LANG.PRINT_ENTER_CHOICE, song, CONF) else: start(TOKEN, CONF, choice)
def mainCommand(): try: opts, args = getopt.getopt(sys.argv[1:], "ho:l:v", ["help", "output=", "link=", "version"]) link = None for opt, val in opts: if opt in ('-h', '--help'): Printf.usage() return if opt in ('-v', '--version'): Printf.logo() return if opt in ('-l', '--link'): link = val if opt in ('-o', '--output'): CONF.downloadPath = val if link is None: Printf.err( "Please enter the link(url/id/path)! Enter 'tidal-dl -h' for help!" ) return if not mkdirs(CONF.downloadPath): Printf.err(LANG.MSG_PATH_ERR + CONF.downloadPath) return checkLogin() start(USER, CONF, link) return except getopt.GetoptError: Printf.err("Argv error! Enter 'tidal -h' for help!")
def loginByWeb(): start = time.time() elapsed = 0 while elapsed < API.key.authCheckTimeout: elapsed = time.time() - start msg, check = API.checkAuthStatus() if not check: if msg == "pending": time.sleep(API.key.authCheckInterval + 1) continue return False if check: Printf.success( LANG.MSG_VALID_ACCESSTOKEN.format( displayTime(int(API.key.expiresIn)))) TOKEN.userid = API.key.userId TOKEN.countryCode = API.key.countryCode TOKEN.accessToken = API.key.accessToken TOKEN.refreshToken = API.key.refreshToken TOKEN.expiresAfter = time.time() + int(API.key.expiresIn) TokenSettings.save(TOKEN) return True Printf.err(LANG.AUTH_TIMEOUT) return False
def __artist__(conf, obj): msg, albums = API.getArtistAlbums(obj.id, conf.includeEP) Printf.artist(obj, len(albums)) if not isNull(msg): Printf.err(msg) return for item in albums: __album__(conf, item)
def __mix__(conf, obj: Mix): Printf.mix(obj) for index, item in enumerate(obj.tracks): mag, album = API.getAlbum(item.album.id) item.trackNumberOnPlaylist = index + 1 downloadTrack(item, album) if conf.saveCovers and not conf.usePlaylistFolder: __downloadCover__(conf, album) for item in obj.videos: downloadVideo(item, None)
def main(): if len(sys.argv) > 1: mainCommand() return Printf.logo() Printf.settings(CONF) checkLogin() autoGetAccessToken() onlineVer = getLastVersion('tidal-dl') if not isNull(onlineVer): icmp = cmpVersion(onlineVer, VERSION) if icmp > 0: Printf.info(LANG.PRINT_LATEST_VERSION + ' ' + onlineVer) while True: Printf.choices() choice = Printf.enter(LANG.PRINT_ENTER_CHOICE) if choice == "0": return elif choice == "1": login() elif choice == "2": changeSettings() elif choice == "3": setAccessToken() else: start(USER, CONF, choice)
def checkLogin(): if not isNull(USER.assesstoken): mag, check = API.loginByAccessToken(USER.assesstoken) if check == False: Printf.err(LANG.MSG_INVAILD_ACCESSTOKEN) if not isNull(USER.sessionid1) and not API.isValidSessionID(USER.userid, USER.sessionid1): USER.sessionid1 = "" if not isNull(USER.sessionid2) and API.isValidSessionID(USER.userid, USER.sessionid2): USER.sessionid2 = "" if isNull(USER.sessionid1) or isNull(USER.sessionid2): login(USER.username, USER.password)
def login(): print(LANG.AUTH_START_LOGIN) msg, check = API.getDeviceCode() if not check: Printf.err(msg) return # print(LANG.AUTH_LOGIN_CODE.format(green(API.key.userCode))) print(LANG.AUTH_NEXT_STEP.format(green("http://" + API.key.verificationUrl + "/" + API.key.userCode), yellow(displayTime(API.key.authCheckTimeout)))) print(LANG.AUTH_WAITING) loginByWeb() return
def __playlist__(conf, obj): Printf.playlist(obj) msg, tracks, videos = API.getItems(obj.id, Type.Playlist) if not isNull(msg): Printf.err(msg) return for item in tracks: mag, album = API.getAlbum(item.album.id) __downloadTrack__(conf, item, album) for item in videos: __downloadVideo__(conf, item, None)
def __album__(conf, obj): Printf.album(obj) msg, tracks, videos = API.getItems(obj.id, Type.Album) if not isNull(msg): Printf.err(msg) return if conf.saveCovers: __downloadCover__(conf, obj) for item in tracks: __downloadTrack__(conf, item, obj) for item in videos: __downloadVideo__(conf, item, obj)
def autoGetAccessToken(): array = API.tryGetAccessToken(USER.userid) if len(array) <= 0: return for item in array: msg, check = API.loginByAccessToken(item, USER.userid) if check == False: continue if item != USER.assesstoken: USER.assesstoken = item UserSettings.save(USER) Printf.info("Auto get accesstoken from tidal cache success!") return
def __album__(conf, obj): Printf.album(obj) msg, tracks, videos = API.getItems(obj.id, Type.Album) if not isNull(msg): Printf.err(msg) return # if conf.saveCovers: # __downloadCover__(conf, obj) for item in tracks: if (Blueberry.should_break()): Blueberry.set_break_status(False) break __downloadTrack__(conf, item, obj)
def __playlist__(conf, obj): Printf.playlist(obj) msg, tracks, videos = API.getItems(obj.uuid, Type.Playlist) if not isNull(msg): Printf.err(msg) return for index, item in enumerate(tracks): mag, album = API.getAlbum(item.album.id) item.trackNumberOnPlaylist = index + 1 __downloadTrack__(conf, item, album, obj) for item in videos: __downloadVideo__(conf, item, None)
def __downloadTrack__(conf, track, album=None, playlist=None): try: msg, stream = API.getStreamUrl(track.id, conf.audioQuality) if not isNull(msg) or stream is None: Printf.err(track.title + "." + msg) return path = __getTrackPath__(conf, track, stream, album, playlist) # Printf.info("Download \"" + track.title + "\" Codec: " + stream.codec) check, err = downloadFileMultiThread(stream.url, path + '.part', stimeout=20, showprogress=True) if not check: Printf.err("Download failed!" + getFileName(path) + ' (' + str(err) + ')') return # encrypted -> decrypt and remove encrypted file if isNull(stream.encryptionKey): os.replace(path + '.part', path) else: key, nonce = decrypt_security_token(stream.encryptionKey) decrypt_file(path + '.part', path, key, nonce) os.remove(path + '.part') path = __convertToM4a__(path, stream.codec) __setMetaData__(track, album, path) Printf.success(getFileName(path)) except Exception as e: Printf.err("Download failed!" + track.title + ' (' + str(e) + ')')
def login(): print(LANG.AUTH_START_LOGIN) msg, check = API.getDeviceCode() if check == False: Printf.err(msg) return print(LANG.AUTH_LOGIN_CODE.format(green(API.key.userCode))) print(LANG.AUTH_NEXT_STEP.format(green(API.key.verificationUrl), yellow(displayTime(API.key.authCheckTimeout)))) print(LANG.AUTH_WAITING) start = time.time() elapsed = 0 while elapsed < API.key.authCheckTimeout: elapsed = time.time() - start # print("Check auth status...") msg, check = API.checkAuthStatus() if check == False: if msg == "pending": time.sleep(API.key.authCheckInterval + 1) continue Printf.err(msg) break if check == True: Printf.success(LANG.MSG_VALID_ACCESSTOKEN.format(displayTime(int(API.key.expiresIn)))) TOKEN.userid = API.key.userId TOKEN.countryCode = API.key.countryCode TOKEN.accessToken = API.key.accessToken TOKEN.refreshToken = API.key.refreshToken TOKEN.expiresAfter = time.time() + int(API.key.expiresIn) TokenSettings.save(TOKEN) break if elapsed >= API.key.authCheckTimeout: Printf.err(LANG.AUTH_TIMEOUT) return
def __playlist__(conf, obj): Printf.playlist(obj) msg, tracks, videos = API.getItems(obj.uuid, Type.Playlist) if not isNull(msg): Printf.err(msg) return for index, item in enumerate(tracks): if (Blueberry.should_break()): Blueberry.set_break_status(False) break mag, album = API.getAlbum(item.album.id) item.trackNumberOnPlaylist = index + 1 __downloadTrack__(conf, item, album, obj)
def __album__(conf, obj): Printf.album(obj) msg, tracks, videos = API.getItems(obj.id, Type.Album) if not aigpy.string.isNull(msg): Printf.err(msg) return if conf.saveAlbumInfo: __saveAlbumInfo__(conf, obj, tracks) if conf.saveCovers: __downloadCover__(conf, obj) for item in tracks: downloadTrack(item, obj) for item in videos: downloadVideo(item, obj)
def file(user, conf, string): txt = aigpy.file.getContent(string) if aigpy.string.isNull(txt): Printf.err("Nothing can read!") return array = txt.split('\n') for item in array: if aigpy.string.isNull(item): continue if item[0] == '#': continue if item[0] == '[': continue start(user, conf, item)
def setAccessToken(): while True: print("-------------AccessToken---------------") token = Printf.enter("accessToken('0' go back):") if token == '0': return msg, check = API.loginByAccessToken(token, USER.userid) if check == False: Printf.err(msg) continue break USER.assesstoken = token UserSettings.save(USER)
def checkLogin(): if not isNull(USER.accessToken): #print('Checking Access Token...') #add to translations msg, check = API.verifyAccessToken(USER.accessToken) if check == True: Printf.info( LANG.MSG_VALID_ACCESSTOKEN.format( displayTime(int(USER.expiresAfter - time.time())))) return else: Printf.info(LANG.MSG_INVAILD_ACCESSTOKEN) msg, check = API.refreshAccessToken(USER.refreshToken) if check == True: Printf.success( LANG.MSG_VALID_ACCESSTOKEN.format( displayTime(int(API.key.expiresIn)))) USER.userid = API.key.userId USER.countryCode = API.key.countryCode USER.accessToken = API.key.accessToken USER.expiresAfter = time.time() + int(API.key.expiresIn) UserSettings.save(USER) return else: Printf.err(msg) tmp = UserSettings() #clears saved tokens UserSettings.save(tmp) login() return
def __file__(user, conf, string): txt = getFileContent(string) if isNull(txt): Printf.err("Nothing can read!") return array = txt.split('\n') for item in array: if isNull(item): continue if item[0] == '#': continue if item[0] == '[': continue start(user, conf, item)
def __playlist__(conf, obj): Printf.playlist(obj) msg, tracks, videos = API.getItems(obj.uuid, Type.Playlist) if not aigpy.string.isNull(msg): Printf.err(msg) return for index, item in enumerate(tracks): mag, album = API.getAlbum(item.album.id) item.trackNumberOnPlaylist = index + 1 downloadTrack(item, album, obj) if conf.saveCovers and not conf.usePlaylistFolder: __downloadCover__(conf, album) for item in videos: downloadVideo(item, None)
def mainCommand(): try: opts, args = getopt.getopt(sys.argv[1:], "ho:l:v:u:p:a:q:r", [ "help", "output=", "link=", "version", "username", "password", "accessToken", "quality", "resolution" ]) link = None for opt, val in opts: if opt in ('-h', '--help'): Printf.usage() return if opt in ('-v', '--version'): Printf.logo() return if opt in ('-l', '--link'): link = val if opt in ('-o', '--output'): CONF.downloadPath = val if opt in ('-u', '--username'): USER.username = val UserSettings.save(USER) if opt in ('-p', '--password'): USER.password = val UserSettings.save(USER) if opt in ('-a', '--accessToken'): USER.assesstoken = val UserSettings.save(USER) if opt in ('-q', '--quality'): CONF.audioQuality = Settings.getAudioQuality(val) if opt in ('-r', '--resolution'): CONF.videoQuality = Settings.getVideoQuality(val) if link is None: Printf.err( "Please enter the link(url/id/path)! Enter 'tidal-dl -h' for help!" ) return if not mkdirs(CONF.downloadPath): Printf.err(LANG.MSG_PATH_ERR + CONF.downloadPath) return checkLogin() start(USER, CONF, link) return except getopt.GetoptError: Printf.err("Argv error! Enter 'tidal -h' for help!")
def __downloadVideo__(conf, video:Video, album=None, playlist=None): if video.allowStreaming is False: Printf.err("Download failed! " + video.title + ' not allow streaming.') return msg, stream = API.getVideoStreamUrl(video.id, conf.videoQuality) Printf.video(video, stream) if not aigpy.string.isNull(msg): Printf.err(video.title + "." + msg) return path = __getVideoPath__(conf, video, album, playlist) logging.info("[DL Video] name=" + aigpy.path.getFileName(path) + "\nurl=" + stream.m3u8Url) check, msg = aigpy.m3u8.download(stream.m3u8Url, path) if check is True: Printf.success(aigpy.path.getFileName(path)) else: Printf.err("\nDownload failed!" + msg + '(' + aigpy.path.getFileName(path) + ')')
def start(user, conf, string): __loadAPI__(user) msg, etype, obj = API.getByString(string) if etype == Type.Null or not isNull(msg): Printf.err(msg) return if etype == Type.Album: __album__(conf, obj) if etype == Type.Track: __track__(conf, obj) if etype == Type.Video: __loadVideoAPI__(user) __video__(conf, obj) if etype == Type.Artist: __artist__(conf, obj) if etype == Type.Playlist: __playlist__(conf, obj)
def mainCommand(): try: opts, args = getopt.getopt( sys.argv[1:], "hvl:o:q:r:", ["help", "version", "link=", "output=", "quality", "resolution"]) except getopt.GetoptError as errmsg: Printf.err(vars(errmsg)['msg'] + ". Use 'tidal-dl -h' for useage.") return link = None for opt, val in opts: if opt in ('-h', '--help'): Printf.usage() continue if opt in ('-v', '--version'): Printf.logo() continue if opt in ('-l', '--link'): checkLogin() link = val continue if opt in ('-o', '--output'): CONF.downloadPath = val Settings.save(CONF) continue if opt in ('-q', '--quality'): CONF.audioQuality = Settings.getAudioQuality(val) Settings.save(CONF) continue if opt in ('-r', '--resolution'): CONF.videoQuality = Settings.getVideoQuality(val) Settings.save(CONF) continue if not mkdirs(CONF.downloadPath): Printf.err(LANG.MSG_PATH_ERR + CONF.downloadPath) return if link is not None: Printf.info(LANG.SETTING_DOWNLOAD_PATH + ':' + CONF.downloadPath) start(TOKEN, CONF, link)
def setAccessToken(): while True: print("-------------AccessToken---------------") token = Printf.enter("accessToken('0' go back):") if token == '0': return msg, check = API.loginByAccessToken(token, TOKEN.userid) if check == False: Printf.err(msg) continue break print("-------------RefreshToken---------------") refreshToken = Printf.enter("refreshToken('0' to skip):") if refreshToken == '0': refreshToken = TOKEN.refreshToken TOKEN.assesstoken = token TOKEN.refreshToken = refreshToken TOKEN.expiresAfter = 0 TokenSettings.save(TOKEN)
def loginByConfig(): if aigpy.stringHelper.isNull(TOKEN.accessToken): return False msg, check = API.verifyAccessToken(TOKEN.accessToken) if check: Printf.info( LANG.MSG_VALID_ACCESSTOKEN.format( displayTime(int(TOKEN.expiresAfter - time.time())))) API.key.countryCode = TOKEN.countryCode API.key.userId = TOKEN.userid API.key.accessToken = TOKEN.accessToken return True Printf.info(LANG.MSG_INVAILD_ACCESSTOKEN) msg, check = API.refreshAccessToken(TOKEN.refreshToken) if check: Printf.success( LANG.MSG_VALID_ACCESSTOKEN.format( displayTime(int(API.key.expiresIn)))) TOKEN.userid = API.key.userId TOKEN.countryCode = API.key.countryCode TOKEN.accessToken = API.key.accessToken TOKEN.expiresAfter = time.time() + int(API.key.expiresIn) TokenSettings.save(TOKEN) return True else: tmp = TokenSettings() # clears saved tokens TokenSettings.save(tmp) return False