From 2ebf80869caa3ff31cb4a4b68ef648eca3888061 Mon Sep 17 00:00:00 2001 From: Richard Curzon Date: Sat, 16 Apr 2022 09:09:12 -0400 Subject: [PATCH] For playlist, remove TIDAL songs and redownload PL, the songs will be removed in the mirror image dl folder --- .gitignore | 2 ++ README.md | 16 ++++++++++++++++ TIDALDL-PY/tidal_dl/download.py | 21 ++++++++++++++++++++- TIDALDL-PY/tidal_dl/util.py | 19 ++++++++++++++----- 4 files changed, 52 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 45a571b..a3089ff 100644 --- a/.gitignore +++ b/.gitignore @@ -173,3 +173,5 @@ TIDALDL-PY/exe/resource TIDALDL-PY/exe/tidal_dl_win.tar.gz .github/workflows/continuous-integration-workflow copy.yml TIDALDL-PY/tidal-gui.spec +.history/ +.gitignore diff --git a/README.md b/README.md index 4c89911..b4d7897 100644 --- a/README.md +++ b/README.md @@ -140,3 +140,19 @@ pip3 install -r requirements.txt --user python3 setup.py install ``` +## Fork changes - Scenario and comments: +I would be content with TIDAL download feature, to give me off-line music on my device. I find playlists good for that. + +But playlist changes are not done well. If I add 1 song to a 100 song playlist, the only way I can get it on my phone is: - +delete all 100 songs - download 101 songs. It's the time, the wear and tear on my device sd card... nope... don't like it at all. + +But I see tidal-dl can create a "mirror image" of that playlist. And has some smarts: if I add a song to the playlist, download again, it will ONLY download that song. Cool. But - what if I delete a song? + +Background: I use PC MediaMonkey for my non-TIDAL mp3s... it will download a music folder to my device... with MediaMonkey for Android and my home Wifi. Of course, MM can also see tidal-dl that "mirror image" playlist. And crucially: if there are adds/deletes, MM makes just the changes on my phone. Other files are untouched. Deletes are done too, smart! + +But... now tidal-dl is the weak link. If I add 1 file to a playlist, and delete 1 file... it gives me the add... but it doesn't handle the delete. And the "mirror image" is not a mirror image any more. + +So this is to fill that gap in my scenario. But the effect seems useful for other use-cases. SUMMARY: Suppose you download a playlist, on top of a previous download. If files are deleted on the playlist, they will also be deleted in the mirror image. + +Also: some improved handling of the "onlyM4A" flag. If files have been renamed from mp4 to m4a, the SKIP download logic wasn't working. Now it's handled for the simple case at least. + diff --git a/TIDALDL-PY/tidal_dl/download.py b/TIDALDL-PY/tidal_dl/download.py index 7a284cb..8c9932a 100644 --- a/TIDALDL-PY/tidal_dl/download.py +++ b/TIDALDL-PY/tidal_dl/download.py @@ -113,12 +113,31 @@ def __playlist__(conf, obj): Printf.err(msg) return + dictNewFiles = {} for index, item in enumerate(tracks): mag, album = API.getAlbum(item.album.id) item.trackNumberOnPlaylist = index + 1 - downloadTrack(item, album, obj) + downloadTrack(item, album, obj, dictNewFiles=dictNewFiles) if conf.saveCovers and not conf.usePlaylistFolder: __downloadCover__(conf, album) + + if len(dictNewFiles) > 0: + targetDir = aigpy.path.getDirName(dictNewFiles[list(dictNewFiles.keys())[0]]) # trick to get the target directory + resFiles = aigpy.path.getFiles(targetDir) + killFiles = [] + errFiles = [] + + for fname in resFiles: + if not os.path.basename(fname) in dictNewFiles: + try: + os.remove(fname) + killFiles.append(fname) + except: + errFiles.append(fname) + + Printf.info("Orphan files deleted:\n" + "\n".join(killFiles)) + Printf.info("Orphan files failed to delete (read-only?):\n" + "\n".join(errFiles)) + for item in videos: downloadVideo(item, None) diff --git a/TIDALDL-PY/tidal_dl/util.py b/TIDALDL-PY/tidal_dl/util.py index ba3b200..8278c2e 100644 --- a/TIDALDL-PY/tidal_dl/util.py +++ b/TIDALDL-PY/tidal_dl/util.py @@ -341,8 +341,8 @@ def setCurVideoQuality(text): break Settings.save(CONF) -def skip(path, url): - if CONF.checkExist and isNeedDownload(path, url) is False: +def skip(finalpath, url): + if CONF.checkExist and isNeedDownload(finalpath, url) is False: return True return False @@ -353,7 +353,7 @@ def convert(srcPath, stream): return srcPath -def downloadTrack(track: Track, album=None, playlist=None, userProgress=None, partSize=1048576): +def downloadTrack(track: Track, album=None, playlist=None, userProgress=None, partSize=1048576, dictNewFiles=None): try: msg, stream = API.getStreamUrl(track.id, CONF.audioQuality) if not aigpy.string.isNull(msg) or stream is None: @@ -365,8 +365,16 @@ def downloadTrack(track: Track, album=None, playlist=None, userProgress=None, pa userProgress.updateStream(stream) path = getTrackPath(CONF, track, stream, album, playlist) + if CONF.onlyM4a: + finalpath = path.replace(".mp4", ".m4a") + else: + finalpath = path + + if not dictNewFiles is None: + dictNewFiles[os.path.basename(finalpath)] = finalpath # preserve full path to be used by caller + # check exist - if skip(path, stream.url): + if skip(finalpath, stream.url): Printf.success(aigpy.path.getFileName(path) + " (skip:already exists!)") return True, "" @@ -383,7 +391,8 @@ def downloadTrack(track: Track, album=None, playlist=None, userProgress=None, pa # encrypted -> decrypt and remove encrypted file encrypted(stream, path + '.part', path) - # convert + # convert to M4a if configured + # note from here to end, path will be = finalpath path = convert(path, stream) # contributors