From 312d87965ebfef2b94b6603db4f75ba05ff56205 Mon Sep 17 00:00:00 2001 From: RemixDev Date: Thu, 16 Apr 2020 11:53:52 +0200 Subject: [PATCH] Implemented savePlaylistAsCompilation Updated README with more information --- README.md | 41 +++++++++++++++++++++++++---------- deemix/app/default.json | 2 +- deemix/app/downloader.py | 40 +++++++++++++++++++++++++++------- deemix/app/queuemanager.py | 3 ++- deemix/app/spotify.py | 5 +++-- deemix/utils/pathtemplates.py | 21 ++++++++++-------- deemix/utils/taggers.py | 9 ++++++-- 7 files changed, 87 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 0dd2c37..d8a3a6b 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,41 @@ # WARNING -This app is not finished, some functions may not work. +The webUI is not finished, some functions may not work. -# How to use +# deemix +## What is deemix? +deemix is a deezer downloader built from the ashes of Deezloader Remix. The base library (or core) can be used as a stand alone CLI app or implemented in an UI using the API. + +## How can I use this this? +Currently there are no available builds as it's still in development.
+But you can try to run it yourself!
+ +## Running instructions NOTE: Python 3 is required for this app.

-Install the dependencies using `pip install -r requirements.txt`
+After installing python install the dependencies using `pip install -r requirements.txt`
Run `python -m deemix --help` to see how to use the app
-Run `python server.py` to start the server and then connect to 127.0.0.1:33333.Enjoy!
+Run `python server.py` to start the server and then connect to 127.0.0.1:33333.
+Enjoy!
-# TODO -Finish porting all features: -- logging -- finish the gui +## What's left to do? +Library: +- Add a log system +- Adding whatever is missing for the webUI +- Write the API Documentation +WebUI: +- Home tab +- Charts tab +- Favorites / Playlists tab +- Link Analyzer +- About Section +- Right click quality selection modal +- Track preview (might not be possible with current search layout) +- Artist, Album and Playlist pages for track selection +- Stylize and separate the options in the Settings tab +- Reset settings to default +- Animations and style polishing - ? -Settings not yet implemented: -- savePlaylistAsCompilation - # License This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/deemix/app/default.json b/deemix/app/default.json index 18f29f0..ae46576 100644 --- a/deemix/app/default.json +++ b/deemix/app/default.json @@ -31,7 +31,6 @@ "artistImageTemplate": "folder", "PNGcovers": false, "dateFormat": "Y-M-D", - "savePlaylistAsCompilation": false, "removeAlbumVersion": false, "featuredToTitle": "0", "titleCasing": "nothing", @@ -61,6 +60,7 @@ "copyright": false, "composer": false, "involvedPeople": false, + "savePlaylistAsCompilation": false, "useNullSeparator": false, "saveID3v1": true, "multitagSeparator": "default" diff --git a/deemix/app/downloader.py b/deemix/app/downloader.py index bb473cc..ee5f969 100644 --- a/deemix/app/downloader.py +++ b/deemix/app/downloader.py @@ -12,6 +12,7 @@ from concurrent.futures import ThreadPoolExecutor from Cryptodome.Cipher import Blowfish from time import sleep import re +import traceback TEMPDIR = os.path.join(gettempdir(), 'deemix-imgs') if not os.path.isdir(TEMPDIR): @@ -288,9 +289,6 @@ def getTrackData(dz, trackAPI_gw, trackAPI = None, albumAPI_gw = None, albumAPI } track['album']['genre'] = [] - if 'date' in track['album']: - track['date'] = track['album']['date'] - if not trackAPI: trackAPI = dz.get_track(track['id']) track['bpm'] = trackAPI['bpm'] @@ -467,9 +465,34 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None return result track['selectedFormat'] = format track['selectedFilesize'] = filesize - track['album']['bitrate'] = format - track['album']['picUrl'] = "https://e-cdns-images.dzcdn.net/images/cover/{}/{}x{}-000000-80-0-0.{}".format(track['album']['pic'], settings['embeddedArtworkSize'], settings['embeddedArtworkSize'], 'png' if settings['PNGcovers'] else 'jpg') track['dateString'] = formatDate(track['date'], settings['dateFormat']) + if settings['tags']['savePlaylistAsCompilation'] and "_EXTRA_PLAYLIST" in trackAPI: + track['album']['picUrl'] = trackAPI["_EXTRA_PLAYLIST"]['picture_small'].replace("56x56", f"{settings['embeddedArtworkSize']}x{settings['embeddedArtworkSize']}").replace(".jpg", '.png' if settings['PNGcovers'] else '.jpg') + track['album']['title'] = trackAPI["_EXTRA_PLAYLIST"]['title'] + track['album']['mainArtist'] = { + 'id': trackAPI["_EXTRA_PLAYLIST"]['various_artist']['id'], + 'name': trackAPI["_EXTRA_PLAYLIST"]['various_artist']['name'], + 'pic': trackAPI["_EXTRA_PLAYLIST"]['various_artist']['picture_small'][trackAPI["_EXTRA_PLAYLIST"]['various_artist']['picture_small'].find('artist/')+7:-24] + } + track['album']['artist'] = {"Main": [trackAPI["_EXTRA_PLAYLIST"]['various_artist']['name'], ]} + track['album']['artists'] = [trackAPI["_EXTRA_PLAYLIST"]['various_artist']['name'], ] + track['trackNumber'] = trackAPI["POSITION"] + track['album']['trackTotal'] = trackAPI["_EXTRA_PLAYLIST"]['nb_tracks'] + track['album']['recordType'] = "Compilation" + track['album']['barcode'] = "" + track['album']['label'] = "" + track['album']['date'] = { + 'day': trackAPI["_EXTRA_PLAYLIST"]["creation_date"][8:10], + 'month': trackAPI["_EXTRA_PLAYLIST"]["creation_date"][5:7], + 'year': trackAPI["_EXTRA_PLAYLIST"]["creation_date"][0:4] + } + track['discNumber'] = "1" + track['album']['discTotal'] = "1" + else: + if 'date' in track['album']: + track['date'] = track['album']['date'] + track['album']['picUrl'] = "https://e-cdns-images.dzcdn.net/images/cover/{}/{}x{}-000000-80-0-0.{}".format(track['album']['pic'], settings['embeddedArtworkSize'], settings['embeddedArtworkSize'], 'png' if settings['PNGcovers'] else 'jpg') + track['album']['bitrate'] = format track['album']['dateString'] = formatDate(track['album']['date'], settings['dateFormat']) # Check if user wants the feat in the title @@ -531,7 +554,7 @@ def downloadTrackObj(dz, trackAPI, settings, bitrate, queueItem, extraTrack=None # Save local album art if coverPath: result['albumURL'] = track['album']['picUrl'].replace(f"{settings['embeddedArtworkSize']}x{settings['embeddedArtworkSize']}", f"{settings['localArtworkSize']}x{settings['localArtworkSize']}") - result['albumPath'] = os.path.join(coverPath, f"{settingsRegexAlbum(settings['coverImageTemplate'], track['album'], settings)}.{'png' if settings['PNGcovers'] else 'jpg'}") + result['albumPath'] = os.path.join(coverPath, f"{settingsRegexAlbum(settings['coverImageTemplate'], track['album'], settings, trackAPI)}.{'png' if settings['PNGcovers'] else 'jpg'}") # Save artist art if artistPath: @@ -610,6 +633,7 @@ def downloadTrackObj_wrap(dz, track, settings, bitrate, queueItem, interface): try: result = downloadTrackObj(dz, track, settings, bitrate, queueItem, interface=interface) except Exception as e: + traceback.print_exc() result = {'error': { 'message': str(e), 'data': { @@ -725,5 +749,5 @@ def after_download_single(track, settings, queueItem): return track['extrasPath'] class downloadCancelled(Exception): - """Base class for exceptions in this module.""" - pass + """Base class for exceptions in this module.""" + pass diff --git a/deemix/app/queuemanager.py b/deemix/app/queuemanager.py index 78cb51b..1c4ee27 100644 --- a/deemix/app/queuemanager.py +++ b/deemix/app/queuemanager.py @@ -91,6 +91,7 @@ def generateQueueItem(dz, sp, url, settings, bitrate=None, albumAPI=None, interf elif type == "playlist": playlistAPI = dz.get_playlist(id) playlistTracksAPI = dz.get_playlist_tracks_gw(id) + playlistAPI['various_artist'] = dz.get_artist(5080) result['title'] = playlistAPI['title'] result['artist'] = playlistAPI['creator']['name'] @@ -158,7 +159,7 @@ def generateQueueItem(dz, sp, url, settings, bitrate=None, albumAPI=None, interf interface.send("toast", {'msg': f"Converting spotify tracks to deezer tracks", 'icon': 'loading', 'dismiss': False, 'id': 'spotifyplaylist_'+str(id)}) playlist = sp.convert_spotify_playlist(dz, id, settings) playlist['bitrate'] = bitrate - playlist['uuid'] = f"{result['type']}_{id}_{bitrate}" + playlist['uuid'] = f"{playlist['type']}_{id}_{bitrate}" result = playlist if interface: interface.send("toast", {'msg': f"Spotify playlist converted", 'icon': 'done', 'dismiss': True, 'id': 'spotifyplaylist_'+str(id)}) diff --git a/deemix/app/spotify.py b/deemix/app/spotify.py index fbdda29..6eb41a3 100644 --- a/deemix/app/spotify.py +++ b/deemix/app/spotify.py @@ -60,7 +60,7 @@ class SpotifyHelper: deezer_obj = { 'checksum': spotify_obj['snapshot_id'], 'collaborative': spotify_obj['collaborative'], - 'creation_date': "???-??-??", + 'creation_date': "????-00-00", 'creator': {'id': spotify_obj['owner']['id'], 'name': spotify_obj['owner']['display_name'], 'tracklist': spotify_obj['owner']['href'], 'type': "user"}, 'description': spotify_obj['description'], 'duration': 0, @@ -117,7 +117,7 @@ class SpotifyHelper: dz_album = 0 return dz_album - def convert_spotify_playlist(self, sp, dz, playlist_id, settings): + def convert_spotify_playlist(self, dz, playlist_id, settings): if not self.spotifyEnabled: raise spotifyFeaturesNotEnabled spotify_playlist = self.sp.playlist(playlist_id) @@ -137,6 +137,7 @@ class SpotifyHelper: else: result['cover'] = "https://e-cdns-images.dzcdn.net/images/cover/d41d8cd98f00b204e9800998ecf8427e/75x75-000000-80-0-0.jpg" playlistAPI = self._convert_playlist_structure(spotify_playlist) + playlistAPI['various_artist'] = dz.get_artist(5080) tracklist = spotify_playlist['tracks']['items'] result['collection'] = [] while spotify_playlist['tracks']['next']: diff --git a/deemix/utils/pathtemplates.py b/deemix/utils/pathtemplates.py index 6db8b78..1eaa2c2 100644 --- a/deemix/utils/pathtemplates.py +++ b/deemix/utils/pathtemplates.py @@ -58,15 +58,15 @@ def generateFilepath(track, trackAPI, settings): coverPath = None extrasPath = None - if settings['createPlaylistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and not settings['savePlaylistAsCompilation']: + if settings['createPlaylistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['savePlaylistAsCompilation']: filepath += antiDot(settingsRegexPlaylist(settings['playlistNameTemplate'], trackAPI['_EXTRA_PLAYLIST'], settings)) + pathSep - if '_EXTRA_PLAYLIST' in trackAPI and not settings['savePlaylistAsCompilation']: + if '_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['savePlaylistAsCompilation']: extrasPath = filepath if ( settings['createArtistFolder'] and not '_EXTRA_PLAYLIST' in trackAPI or - (settings['createArtistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and settings['savePlaylistAsCompilation']) or + (settings['createArtistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']) or (settings['createArtistFolder'] and '_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist']) ): if (int(track['id'])<0 and not 'mainArtist' in track['album']): @@ -76,19 +76,19 @@ def generateFilepath(track, trackAPI, settings): if (settings['createAlbumFolder'] and (not 'SINGLE_TRACK' in trackAPI or ('SINGLE_TRACK' in trackAPI and settings['createSingleFolder'])) and - (not '_EXTRA_PLAYLIST' in trackAPI or ('_EXTRA_PLAYLIST' in trackAPI and settings['savePlaylistAsCompilation']) or ('_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist'])) + (not '_EXTRA_PLAYLIST' in trackAPI or ('_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']) or ('_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist'])) ): - filepath += antiDot(settingsRegexAlbum(settings['albumNameTemplate'], track['album'], settings)) + pathSep + filepath += antiDot(settingsRegexAlbum(settings['albumNameTemplate'], track['album'], settings, trackAPI)) + pathSep coverPath = filepath - if not ('_EXTRA_PLAYLIST' in trackAPI and not settings['savePlaylistAsCompilation']): + if not ('_EXTRA_PLAYLIST' in trackAPI and not settings['tags']['savePlaylistAsCompilation']): extrasPath = filepath if ( int(track['album']['discTotal']) > 1 and ( (settings['createAlbumFolder'] and settings['createCDFolder']) and (not 'SINGLE_TRACK' in trackAPI or ('SINGLE_TRACK' in trackAPI and settings['createSingleFolder'])) and - (not '_EXTRA_PLAYLIST' in trackAPI or ('_EXTRA_PLAYLIST' in trackAPI and settings['savePlaylistAsCompilation']) or ('_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist'])) + (not '_EXTRA_PLAYLIST' in trackAPI or ('_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']) or ('_EXTRA_PLAYLIST' in trackAPI and settings['createStructurePlaylist'])) )): filepath += 'CD' + str(track['discNumber']) + pathSep @@ -127,8 +127,11 @@ def settingsRegex(filename, track, settings, playlist=None): filename = filename.replace('\\', pathSep).replace('/', pathSep) return antiDot(fixLongName(filename)) -def settingsRegexAlbum(foldername, album, settings): - foldername = foldername.replace("%album_id%", str(album['id'])) +def settingsRegexAlbum(foldername, album, settings, trackAPI): + if trackAPI and '_EXTRA_PLAYLIST' in trackAPI and settings['tags']['savePlaylistAsCompilation']: + foldername = foldername.replace("%album_id%", "pl_"+str(trackAPI['_EXTRA_PLAYLIST']['id'])) + else: + foldername = foldername.replace("%album_id%", str(album['id'])) foldername = foldername.replace("%album%", fixName(album['title'], settings['illegalCharacterReplacer'])) foldername = foldername.replace("%artist%", fixName(album['mainArtist']['name'], settings['illegalCharacterReplacer'])) foldername = foldername.replace("%artist_id%", str(album['mainArtist']['id'])) diff --git a/deemix/utils/taggers.py b/deemix/utils/taggers.py index 581386e..d9a61db 100644 --- a/deemix/utils/taggers.py +++ b/deemix/utils/taggers.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from mutagen.flac import FLAC, Picture from mutagen.id3 import ID3, ID3NoHeaderError, TXXX, TIT2, TPE1, TALB, TPE2, TRCK, TPOS, TCON, TYER, TDAT, TLEN, TBPM, \ - TPUB, TSRC, USLT, APIC, IPLS, TCOM, TCOP + TPUB, TSRC, USLT, APIC, IPLS, TCOM, TCOP, TCMP def tagID3(stream, track, save): @@ -42,7 +42,8 @@ def tagID3(stream, track, save): tag.add(TSRC(text=track['ISRC'])) if save['barcode']: tag.add(TXXX(desc="BARCODE", text=track['album']['barcode'])) - tag.add(TXXX(desc="ITUNESADVISORY", text="1" if track['explicit'] else "0")) + if save['explicit']: + tag.add(TXXX(desc="ITUNESADVISORY", text="1" if track['explicit'] else "0")) if save['replayGain']: tag.add(TXXX(desc="REPLAYGAIN_TRACK_GAIN", text=track['replayGain'])) if 'unsync' in track['lyrics'] and save['lyrics']: @@ -58,6 +59,8 @@ def tagID3(stream, track, save): tag.add(IPLS(people=involved_people)) if save['copyright']: tag.add(TCOP(text=track['copyright'])) + if save['savePlaylistAsCompilation']: + tag.add(TCMP(text="1")) if save['cover'] and track['album']['picPath']: with open(track['album']['picPath'], 'rb') as f: tag.add(APIC(3, 'image/jpeg' if track['album']['picPath'].endswith('jpg') else 'image/png', 3, data=f.read())) @@ -118,6 +121,8 @@ def tagFLAC(stream, track, save): tag["ORGANIZATION"] = track['contributors']['musicpublisher'] if save['copyright']: tag["COPYRIGHT"] = track['copyright'] + if save['savePlaylistAsCompilation']: + tag["COMPILATION"] = "1" if save['cover'] and track['album']['picPath']: image = Picture()