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 mergerByM3u8_Multithreading(self, url, filepath, showprogress=False, showshell=False): """ #Func : 多线程下载并合并文件(使用M3u8的url) #Param : url [in] 链接 #Param : filepath [in] 目标文件名 #Param : showprogress [in] 是否显示进度条 #Param : showshell [in] 是否显示cmd信息 #Return : True/False """ try: # Get urllist urllist = self.__parseM3u8(url) if len(urllist) <= 0: return False # Creat tmpdir path = pathHelper.getDirName(filepath) tmpPath = pathHelper.getDiffTmpPathName(path) if pathHelper.mkdirs(tmpPath) is False: return False # Progress self.progress = None if showprogress: self.progress = ProgressTool(len(urllist)) # Download files allpath = [] self.waitCount = len(urllist) self.completeCount = 0 for i, item in enumerate(urllist): index = i + 100001 path = tmpPath + '/' + str(index) + ".ts" path = os.path.abspath(path) allpath.append(path) if os.path.exists(path): os.remove(path) self.thread.start(self.__thradfunc_dl, item, path, 3) self.thread.waitAll() ret = self.mergerByTs(tmpPath, filepath, showshell) # ret = self.mergerByFiles(allpath, filepath, showshell) shutil.rmtree(tmpPath) return ret except: return False
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 _downloadFiles(self): check = pathHelper.remove(self.tmpPath) check = pathHelper.mkdirs(self.tmpPath) if self.netVer.mainFile is None: return False if self.netVer.isZip == 0: plist = [] plist.append(self.netVer.mainFile) for item in self.netVer.elseFileList: plist.append(item) for item in plist: urlpath = self.netUrl + '//' + item topath = self.tmpPath + '\\' + item if netHelper.downloadFile(urlpath, topath) is False: return False else: urlpath = self.netUrl + '//' + self.netVer.zipFile topath = self.tmpPath + '\\' + self.netVer.zipFile if netHelper.downloadFile(urlpath, topath) is False: return False return zipHelper.unzip(topath, self.tmpPath) return True
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 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 changeSettings(): global LANG Printf.settings(CONF) choice = Printf.enter(LANG.CHANGE_START_SETTINGS) if choice == '0': return while True: choice = Printf.enter(LANG.CHANGE_DOWNLOAD_PATH) if choice == '0': choice = CONF.downloadPath elif not os.path.isdir(choice): if not mkdirs(choice): Printf.err(LANG.MSG_PATH_ERR) continue CONF.downloadPath = choice break while True: choice = Printf.enter(LANG.CHANGE_AUDIO_QUALITY) if choice != '1' and choice != '2' and choice != '3' and choice != '0': Printf.err(LANG.MSG_INPUT_ERR) continue if choice == '0': CONF.audioQuality = AudioQuality.Normal if choice == '1': CONF.audioQuality = AudioQuality.High if choice == '2': CONF.audioQuality = AudioQuality.HiFi if choice == '3': CONF.audioQuality = AudioQuality.Master break while True: choice = Printf.enter(LANG.CHANGE_VIDEO_QUALITY) if choice != '1' and choice != '2' and choice != '3' and choice != '0': Printf.err(LANG.MSG_INPUT_ERR) continue if choice == '0': CONF.videoQuality = VideoQuality.P1080 if choice == '1': CONF.videoQuality = VideoQuality.P720 if choice == '2': CONF.videoQuality = VideoQuality.P480 if choice == '3': CONF.videoQuality = VideoQuality.P360 break CONF.onlyM4a = Printf.enter(LANG.CHANGE_ONLYM4A) == '1' # CONF.addExplicitTag = Printf.enter(LANG.CHANGE_ADD_EXPLICIT_TAG) == '1' # CONF.addHyphen = Printf.enter(LANG.CHANGE_ADD_HYPHEN) == '1' # CONF.addYear = Printf.enter(LANG.CHANGE_ADD_YEAR) == '1' # CONF.useTrackNumber = Printf.enter(LANG.CHANGE_USE_TRACK_NUM) == '1' CONF.checkExist = Printf.enter(LANG.CHANGE_CHECK_EXIST) == '1' # CONF.artistBeforeTitle = Printf.enter(LANG.CHANGE_ARTIST_BEFORE_TITLE) == '1' CONF.includeEP = Printf.enter(LANG.CHANGE_INCLUDE_EP) == '1' # CONF.addAlbumIDBeforeFolder = Printf.enter(LANG.CHANGE_ALBUMID_BEFORE_FOLDER) == '1' CONF.saveCovers = Printf.enter(LANG.CHANGE_SAVE_COVERS) == '1' CONF.showProgress = Printf.enter(LANG.CHANGE_SHOW_PROGRESS) == '1' CONF.language = Printf.enter( LANG.CHANGE_LANGUAGE + "('0'-English,'1'-中文,'2'-Turkish,'3'-Italiano,'4'-Czech,'5'-Arabic,'6'-Russian,'7'-Filipino,'8'-Croatian,'9'-Spanish,'10'-Portuguese,'11'-Ukrainian,'12'-Vietnamese,'13'-French,'14'-German):" ) albumFolderFormat = Printf.enter(LANG.CHANGE_ALBUM_FOLDER_FORMAT) if albumFolderFormat == '0': albumFolderFormat = CONF.albumFolderFormat else: CONF.albumFolderFormat = albumFolderFormat trackFileFormat = Printf.enter(LANG.CHANGE_TRACK_FILE_FORMAT) if trackFileFormat == '0': trackFileFormat = CONF.trackFileFormat else: CONF.trackFileFormat = trackFileFormat LANG = setLang(CONF.language) Settings.save(CONF)
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 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 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