Exemple #1
0
    def getByString(self, string):
        etype = Type.Null
        obj = None

        if isNull(string):
            return "Please enter something.", etype, obj
        etype, sid = self.parseUrl(string)
        if isNull(sid):
            sid = string

        if obj is None and (etype == Type.Null or etype == Type.Album):
            msg, obj = self.getAlbum(sid)
        if obj is None and (etype == Type.Null or etype == Type.Artist):
            msg, obj = self.getArtist(sid)
        if obj is None and (etype == Type.Null or etype == Type.Track):
            msg, obj = self.getTrack(sid)
        if obj is None and (etype == Type.Null or etype == Type.Video):
            msg, obj = self.getVideo(sid)
        if obj is None and (etype == Type.Null or etype == Type.Playlist):
            msg, obj = self.getPlaylist(sid)

        if obj is None or etype != Type.Null:
            return msg, etype, obj
        if obj.__class__ == Album:
            etype = Type.Album
        if obj.__class__ == Artist:
            etype = Type.Artist
        if obj.__class__ == Track:
            etype = Type.Track
        if obj.__class__ == Video:
            etype = Type.Video
        if obj.__class__ == Playlist:
            etype = Type.Playlist
        return msg, etype, obj
Exemple #2
0
def __downloadTrack__(conf, track, album=None, playlist=None):
    msg, stream = API.getStreamUrl(track.id, conf.audioQuality)
    if not isNull(msg):
        Printf.err(track.title + "." + msg)
        return
    path = __getTrackPath__(conf, track, stream, album, playlist)

    # Printf.info("Download \"" + track.title + "\" Codec: " + stream.codec)
    check, err = downloadFileRetErr(stream.url,
                                    path + '.part',
                                    showprogress=True,
                                    stimeout=20)
    if not check:
        Printf.err("\n Download failed!" + getFileName(path))
        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))
Exemple #3
0
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 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)
Exemple #5
0
 def checkLogin(self):
     if not isNull(self.user.assesstoken):
         mag, check = self.api.loginByAccessToken(self.user.assesstoken)
         if check == False:
             print("Invalid accesstoken")
     if not isNull(self.user.sessionid1) and not self.api.isValidSessionID(
             self.user.userid, self.user.sessionid1):
         self.user.sessionid1 = ""
     if not isNull(self.user.sessionid2) and self.api.isValidSessionID(
             self.user.userid, self.user.sessionid2):
         self.user.sessionid2 = ""
     if isNull(self.user.sessionid1) or isNull(self.user.sessionid2):
         self.login(self.user.username, self.user.password)
Exemple #6
0
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)
Exemple #7
0
def __getAlbumPath__(conf: Settings, album):
    base = conf.downloadPath + '/Album/'
    artist = replaceLimitChar(album.artists[0].name, '-')
    # album folder pre: [ME][ID]
    flag = API.getFlag(album, Type.Album, True, "")
    if conf.audioQuality != AudioQuality.Master:
        flag = flag.replace("M", "")
    if not conf.addExplicitTag:
        flag = flag.replace("E", "")
    if not isNull(flag):
        flag = "[" + flag + "] "

    sid = str(album.id)
    #album and addyear
    albumname = replaceLimitChar(album.title, '-')
    year = getSubOnlyEnd(album.releaseDate, '-')
    # retpath
    retpath = conf.albumFolderFormat
    if retpath is None or len(retpath) <= 0:
        retpath = Settings.getDefualtAlbumFolderFormat()
    retpath = retpath.replace(R"{ArtistName}", artist)
    retpath = retpath.replace(R"{Flag}", flag)
    retpath = retpath.replace(R"{AlbumID}", sid)
    retpath = retpath.replace(R"{AlbumYear}", year)
    retpath = retpath.replace(R"{AlbumTitle}", albumname)
    retpath = retpath.strip()
    return base + retpath
Exemple #8
0
def __getTrackPath2__(conf, track, stream, album=None, playlist=None):
    if album is not None:
        base = __getAlbumPath__(conf, album)
        if album.numberOfVolumes > 1:
            base += 'CD' + str(track.volumeNumber) + '/'
    if playlist is not None:
        base = __getPlaylistPath__(conf, playlist)

    # hyphen
    hyphen = ' - ' if conf.addHyphen else ' '
    # get number
    number = ''
    if conf.useTrackNumber:
        number = __getIndexStr__(track.trackNumber) + hyphen
        if playlist is not None:
            number = __getIndexStr__(track.trackNumberOnPlaylist) + hyphen
    # get artist
    artist = ''
    if conf.artistBeforeTitle:
        artist = replaceLimitChar(track.artists[0].name, '-') + hyphen
    # get explicit
    explicit = "(Explicit)" if conf.addExplicitTag and track.explicit else ''
    # title
    title = track.title
    if not isNull(track.version):
        title += ' - ' + track.version
    title = replaceLimitChar(title, '-')
    # extension
    extension = __getExtension__(stream.url)
    return base + number + artist + title + explicit + extension
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()
        else:
            start(USER, CONF, choice)
    def __get__(self, path, params={}, retry=3, urlpre=__URL_PRE__):
        # deprecate the sessionId
        # header = {'X-Tidal-SessionId': self.key.sessionId}T
        header = {}
        if not isNull(self.key.accessToken):
            header = {
                'authorization': 'Bearer {}'.format(self.key.accessToken)
            }
        params['countryCode'] = self.key.countryCode

        result = None
        respond = None
        for index in range(0, retry):
            try:
                respond = requests.get(urlpre + path,
                                       headers=header,
                                       params=params)
                result = self.__toJson__(respond.text)
                break
            except:
                continue

        if result is None:
            return "Get operation err!" + respond.text, None
        if 'status' in result:
            if 'userMessage' in result and result['userMessage'] is not None:
                return result['userMessage'], None
            else:
                logging.error("[Get operation err] path=" + path +
                              ". respon=" + respond.text)
                return "Get operation err!", None
        return None, result
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
Exemple #12
0
 def downloadVideo(self, video, quality):
     #msg, video = self.api.__get__(f'videos/{video}')
     self.api.key.accessToken = self.user.assesstoken
     self.api.key.userId = self.user.userid
     self.api.key.countryCode = self.user.countryCode
     self.api.key.sessionId = self.user.sessionid2 if not isNull(
         self.user.sessionid2) else self.user.sessionid1
     msg, stream = self.getVideoStreamUrl(video['id'], quality)
     if not isNull(msg):
         print(video['title'] + "." + msg)
         return
     # CHANGE THIS LINE TO CHANGE THE FILENAME
     videofile = library + "/" + replaceLimitChar(
         f"{artist} - {video['title']} ({video['id']})", "_") + ".mkv"
     path = library + "/" + replaceLimitChar(
         f"{video['title']} ({video['id']})", "_") + ".mp4"
     filename = str(video['id'])
     filtetitle = str(video['title'])
     if not os.path.exists("/config/logs/tidal/" + replaceLimitChar(
             f"{artist} - {video['title']} ({video['id']})", "_")):
         if not m3u8Helper.download(stream['m3u8Url'], path):
             print("\nDownload failed!")
         else:
             subprocess.call([
                 'ffmpeg', '-y', '-ss', '10', '-i', path, '-frames:v', '1',
                 '-vf',
                 str("scale=640:-2"), 'cover.jpg'
             ])
             subprocess.call([
                 'ffmpeg', '-i', path, '-c', 'copy', '-metadata',
                 'title=' + filtetitle, '-metadata', 'ARTIST=' + artist,
                 '-metadata', 'ENCODED_BY=AMVD', '-metadata',
                 'CONTENT_TYPE=Music Video', '-attach', 'cover.jpg',
                 '-metadata:s:t', 'mimetype=image/jpeg', '-y', videofile
             ])
             subprocess.call(
                 ['mkvpropedit', videofile, '--add-track-statistics-tags'])
             subprocess.call(['rm', path])
             subprocess.call(['rm', 'cover.jpg'])
             f = open(
                 "/config/logs/tidal/" + replaceLimitChar(
                     f"{artist} - {video['title']} ({video['id']})", "_"),
                 "w")
             f.write("Download Success")
             f.close()
     else:
         print("Already Downloaded!!!")
Exemple #13
0
def __downloadTrack__(conf: Settings, 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)

        # check exist
        if conf.checkExist and __isNeedDownload__(path, stream.url) == False:
            playSong(track, path)
            Printf.success(getFileName(path) + " (skip:already exists!)")
            return

        # Printf.info("Download \"" + track.title + "\" Codec: " + stream.codec)
        if conf.multiThreadDownload:
            check, err = downloadFileMultiThread(
                stream.url,
                path + '.part',
                stimeout=20,
                showprogress=conf.showProgress)
        else:
            check, err = downloadFile(stream.url,
                                      path + '.part',
                                      stimeout=20,
                                      showprogress=conf.showProgress)
        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)

        # contributors
        contributors = API.getTrackContributors(track.id)
        __setMetaData__(track, album, path, contributors)
        # Printf.success(getFileName())
        playSong(track, path)
    except Exception as e:
        Printf.err("Download failed! " + track.title + ' (' + str(e) + ')')
Exemple #14
0
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)
Exemple #15
0
def __downloadVideo__(conf, video, album=None, playlist=None):
    msg, stream = API.getVideoStreamUrl(video.id, conf.videoQuality)
    if not isNull(msg):
        Printf.err(video.title + "." + msg)
        return
    path = __getVideoPath__(conf, video, album, playlist)
    if m3u8Helper.download(stream.m3u8Url, path):
        Printf.success(getFileName(path))
    else:
        Printf.err("\nDownload failed!" + getFileName(path))
Exemple #16
0
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)
Exemple #17
0
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)
Exemple #18
0
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()
        raw = Printf.enter(LANG.PRINT_ENTER_CHOICE).strip()

        choice = int(re.sub('[^0-9]', '', raw))
        if choice == 0:
            return
        elif choice == 1:
            checkLogin()
        elif choice == 2:
            changeSettings()
        elif choice == 3:
            checkLogout()
        elif choice == 4:
            Printf.searchTypes()

            searchRaw = Printf.enter(LANG.PRINT_ENTER_CHOICE).strip()

            searchType = int(re.sub('[^0-9]', '', searchRaw))

            if (searchType == 3):
                searchRaw = Printf.enter("Enter a url or id: ").strip()
                start(TOKEN, CONF, searchRaw)
                return

            field = "track" if searchType == 0 else 'album' if searchType == 1 else 'playlist'

            if searchType >= 0 and searchType <= 2:
                song = Printf.enter("Enter the song name: ")
                searchTrack(TOKEN, LANG.PRINT_ENTER_CHOICE, field, song, CONF)
            else:
                os.system('clear')
                Printf.err("Invalid option!")
        else:
            os.system('clear')
            Printf.err("Invalid option!")
Exemple #19
0
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)
Exemple #20
0
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)
Exemple #21
0
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)
Exemple #22
0
    def __get__(self, path, params={}, retry=3):
        #deprecate the sessionId
        #header = {'X-Tidal-SessionId': self.key.sessionId}
        if not isNull(self.key.accessToken):
            header = {'authorization': 'Bearer {}'.format(self.key.accessToken)}

        params['countryCode'] = self.key.countryCode
        result = requests.get(__URL_PRE__ + path,  headers=header, params=params).json()
        if 'status' in result:
            if 'userMessage' in result and result['userMessage'] is not None:
                return result['userMessage'], None
            else:
                return "Get operation err!", None
        return None, result
Exemple #23
0
    def loginByAccessToken(self, accessToken, userid=None):
        header = {'authorization': 'Bearer {}'.format(accessToken)}
        result = requests.get('https://api.tidal.com/v1/sessions', headers=header).json()
        if 'status' in result and result['status'] != 200:
            return "Login failed!", False

        if not isNull(userid):
            if str(result['userId']) != str(userid):
                return "User mismatch! Please use your own accesstoken.", False

        self.key.userId = result['userId']
        self.key.countryCode = result['countryCode']
        self.key.accessToken = accessToken
        return None, True
def login(username="", password=""):
    while True:
        if isNull(username) or isNull(password):
            print("---------------" + LANG.CHOICE_LOGIN + "-----------------")
            username = Printf.enter(LANG.PRINT_USERNAME)
            password = Printf.enter(LANG.PRINT_PASSWORD)
        msg, check = API.login(username, password, TOKEN1)
        if check == False:
            Printf.err(msg)
            username = ""
            password = ""
            continue
        api2 = TidalAPI()
        msg, check = api2.login(username, password, TOKEN2)
        break

    USER.username = username
    USER.password = password
    USER.userid = API.key.userId
    USER.countryCode = API.key.countryCode
    USER.sessionid1 = API.key.sessionId
    USER.sessionid2 = api2.key.sessionId
    UserSettings.save(USER)
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":
            setAccessToken()
        elif choice == '5':
            if setAPIKey():
                checkLogout()
        elif choice == "10":  # test track
            start(TOKEN, CONF, '70973230')
        elif choice == "11":  # test video
            start(TOKEN, CONF, '188932980')
        elif choice == "12":  # test album
            start(TOKEN, CONF, '58138532')
        elif choice == "13":  # test playlist
            start(TOKEN, CONF, '98235845-13e8-43b4-94e2-d9f8e603cee7')
        elif choice == "14":  # test playlist
            start(TOKEN, CONF, '01453963b7dbd41c8b82ccb678d127')
        else:
            start(TOKEN, CONF, choice)
Exemple #26
0
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 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()
        elif choice == "4":
            #batch processing
            try:
                with open('batch.txt') as batchfile:
                    line = batchfile.readline()
                    while line:
                        if len(line.strip()) > 0:
                            print('\033[1m' + f"Batching {line.strip()}" + '\033[0m')
                            start(USER, CONF, line.strip())
                            line = batchfile.readline()
            except:
                traceback.print_exc()
        else:
            start(USER, CONF, choice)
def __getAlbumPath__(conf, album):
    # outputdir/Album/artist/
    artist = replaceLimitChar(album.artists[0].name, '-')
    base = conf.downloadPath + '/Album/' + artist + '/'

    #album folder pre: [ME][ID]
    flag = API.getFlag(album, Type.Album, True, "")
    if conf.audioQuality != AudioQuality.Master:
        flag = flag.replace("M", "")
    if not isNull(flag):
        flag = "[" + flag + "] "

    sid = "[" + str(album.id) + "] " if conf.addAlbumIDBeforeFolder else ""

    #album and addyear
    albumname = replaceLimitChar(album.title, '-')
    year = ""
    if conf.addYear:
        year = "[" + getSubOnlyEnd(album.releaseDate, '-') + "] "
    return base + flag + sid + year + albumname + '/'
Exemple #29
0
def __getTrackPath__(conf: Settings, track, stream, album=None, playlist=None):
    if album is not None:
        base = __getAlbumPath__(conf, album) + '/'
        if album.numberOfVolumes > 1:
            base += 'CD' + str(track.volumeNumber) + '/'
    if playlist is not None:
        base = __getPlaylistPath__(conf, playlist)
    # number
    number = __getIndexStr__(track.trackNumber)
    if playlist is not None:
        number = __getIndexStr__(track.trackNumberOnPlaylist)
    # artist
    artist = replaceLimitChar(track.artists[0].name, '-')
    # title
    title = track.title
    if not isNull(track.version):
        title += ' (' + track.version + ')'
    title = replaceLimitChar(title, '-')
    # get explicit
    explicit = "(Explicit)" if conf.addExplicitTag and track.explicit else ''
    #album and addyear
    albumname = replaceLimitChar(album.title, '-')
    year = ""
    if album.releaseDate is not None:
        year = getSubOnlyEnd(album.releaseDate, '-')
    # extension
    extension = __getExtension__(stream.url)
    retpath = conf.trackFileFormat
    if retpath is None or len(retpath) <= 0:
        retpath = Settings.getDefaultTrackFileFormat()
    retpath = retpath.replace(R"{TrackNumber}", number)
    retpath = retpath.replace(R"{ArtistName}", artist.strip())
    retpath = retpath.replace(R"{TrackTitle}", title)
    retpath = retpath.replace(R"{ExplicitFlag}", explicit)
    retpath = retpath.replace(R"{AlbumYear}", year)
    retpath = retpath.replace(R"{AlbumTitle}", albumname.strip())
    retpath = retpath.strip()
    return base + retpath + extension
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' or isNull(albumFolderFormat):
        pass
    elif albumFolderFormat.lower() == 'default':
        CONF.albumFolderFormat = Settings.getDefaultAlbumFolderFormat()
    else:
        CONF.albumFolderFormat = albumFolderFormat
    trackFileFormat = Printf.enter(LANG.CHANGE_TRACK_FILE_FORMAT)
    if trackFileFormat == '0' or isNull(trackFileFormat):
        pass
    elif trackFileFormat.lower() == "default":
        CONF.trackFileFormat = Settings.getDefaultAlbumFolderFormat()
    else:
        CONF.trackFileFormat = trackFileFormat

    LANG = setLang(CONF.language)
    Settings.save(CONF)