From 6b8c7d141e73c800b1079e7dfa3c47b59129653a Mon Sep 17 00:00:00 2001 From: johnpacheco1 Date: Fri, 14 Oct 2022 06:00:58 -0500 Subject: [PATCH 1/4] Init commit --- ElectricShuffle.code-workspace | 7 --- ElectricShuffle.py | 8 +-- ElectricShuffleAll.py | 107 +++++++++++++++++++++++++++++++++ README.md | 14 ++++- requirements.txt | 34 +---------- 5 files changed, 125 insertions(+), 45 deletions(-) delete mode 100644 ElectricShuffle.code-workspace create mode 100644 ElectricShuffleAll.py diff --git a/ElectricShuffle.code-workspace b/ElectricShuffle.code-workspace deleted file mode 100644 index 362d7c2..0000000 --- a/ElectricShuffle.code-workspace +++ /dev/null @@ -1,7 +0,0 @@ -{ - "folders": [ - { - "path": "." - } - ] -} \ No newline at end of file diff --git a/ElectricShuffle.py b/ElectricShuffle.py index c963e82..3004f82 100644 --- a/ElectricShuffle.py +++ b/ElectricShuffle.py @@ -42,10 +42,11 @@ ### Now that session is handled, get playlists playlists = session.user.playlists() +print(playlists) print("Please select the playlist you wish to shuffle by intering it's number below") for idx, playlist in enumerate(playlists): - print(f" [{idx+1}] {playlist.name}") - + if("- Electrified" not in playlist.name): + print(f" [{idx+1}] {playlist.name}") # Get playlists on user account playlist_num = int(input("Playlist Number: ")) - 1 @@ -57,7 +58,6 @@ if not should_continue: exit(1) - ### Do the thing tracklist = selected_playlist.tracks() track_ids = [] @@ -69,7 +69,7 @@ shuffled_name = f"{selected_playlist.name} - Electrified" for playlist in playlists: ### Delete an existing version of this playlist if we are re-rolling - if playlist.name is shuffled_name: + if playlist.name == shuffled_name: playlist.delete() ## Create (or re create) the electric playlist diff --git a/ElectricShuffleAll.py b/ElectricShuffleAll.py new file mode 100644 index 0000000..7d802b0 --- /dev/null +++ b/ElectricShuffleAll.py @@ -0,0 +1,107 @@ +#!/bin/env python3 + +import datetime +import json +import os +from os import path +import random +from select import select +import tidalapi + + +def token_refresh(session, refresh_token): + """ + Retrieves a new access token using the specified parameters, updating the current access token + + :param refresh_token: The refresh token retrieved when using the OAuth login. + :return: True if we believe the token was successfully refreshed, otherwise False + """ + url = 'https://auth.tidal.com/v1/oauth2/token' + params = { + 'grant_type': 'refresh_token', + 'refresh_token': refresh_token, + 'client_id': session.config.client_id, + 'client_secret': session.config.client_secret + } + + request = session.request_session.post(url, params) + json = request.json() + if not request.ok: + log.warning("The refresh token has expired, a new login is required.") + return json + session.access_token = json['access_token'] + session.expiry_time = datetime.datetime.now() + datetime.timedelta(seconds=json['expires_in']) + session.token_type = json['token_type'] + return json + +dir = path.dirname(path.realpath(__file__)) +credential_file = path.join(dir, '.credentials.json') + +need_oauth = True +print("Creating Session") +session = tidalapi.Session() + +# Check if the credentials are still valid +if os.path.exists(credential_file): + with open(credential_file, "r") as open_file: + token_json = json.load(open_file) + + token_refresh(session, token_json["refresh_token"]) + + # Check tokens with tidal + session.load_oauth_session( + token_json["token_type"], + token_json["access_token"], + token_json["refresh_token"], + datetime.datetime.fromisoformat(token_json["expiry_time"]) + ) + + new_token_json = token_refresh(session, token_json["refresh_token"]) + session_dict = { + "token_type": session.token_type, + "access_token": session.access_token, + "refresh_token": session.refresh_token, + "expiry_time": session.expiry_time.isoformat() + } + with open(credential_file, "w") as out_file: + out_file.write(json.dumps(session_dict)) + +if not session.check_login(): + # Have user go log in and when this completes, we will auto continue + session.login_oauth_simple() + + session_dict = { + "token_type": session.token_type, + "access_token": session.access_token, + "refresh_token": session.refresh_token, + "expiry_time": session.expiry_time.isoformat() + } + with open(credential_file, "w") as out_file: + out_file.write(json.dumps(session_dict)) + +### Now that session is handled, get playlists +print("Getting Playlists") +playlists = session.user.playlists() + +for playlist in playlists: + ### Signify that this is a copy for the user + shuffled_name = f"{playlist.name} - Electrified" + + if "- Electrified" in playlist.name: + playlist.delete() + else: + print("Shuffling playlist " + playlist.name) + + ### Do the thing + tracklist = playlist.tracks() + track_ids = [] + + for track in tracklist: + track_ids.append(track.id) + + ## Create (or re create) the electric playlist + new_playlist = session.user.create_playlist(shuffled_name, playlist.description) + + ## Add the beautiful music after a quick shuffle + random.shuffle(track_ids) + new_playlist.add(track_ids) diff --git a/README.md b/README.md index c209022..b5172b2 100644 --- a/README.md +++ b/README.md @@ -6,18 +6,28 @@ This is a (terrible) app to shuffle your Tidal playlists since there are not (cu I'm not responsible for bricked devices, dead SD cards, thermonuclear war, or you getting fired because your entire playlist got deleted and you are now too sad to work. This _shouldn't happen_ but the app is very lightly tested. -### Running The Program +### Setting up Environment 1. Clone this repo to your computer 1. Create a venv to install packages by running `python3 -m venv .venv` 1. Start using the virtual env by running `source .venv/bin/activate` 1. Install required packages by running `pip3 install -r requirements.txt` + +### Running ElectricShuffle 1. Run the program `./ElectricShuffle.py` -1. Log in by clicking the link (or copying it to your browser). Once logged in return to the terminal. +1. Log in by clicking the link (or copying it to your browser). Once logged in return to the terminal. (Once per environment) 1. Pick a playlist to shuffle 1. Confirm that is the playlist you want 1. Go enjoy some tunes! Notice your new playlist which is a copy of your current one but with an " - Electrified" suffix. This is the shuffled one. +### Running ElectricShuffleAll +This script requires no human intervention beyond the initial login. Run it one time manually to login, then automate to your heart's content. +**Note:** On linux VMs there is an issue where the tidalapi package sends requests too fast for the tidal servers. Adding time.sleep(1) to the tidalapi playlist.py \__init__ resolves the issue. + +1. Run the program `./ElectricShuffleAll.py` +1. Log in by clicking the link (or copying it to your browser). Once logged in return to the terminal. (Once per environment) +1. Shuffles all playlists + ### Contributing Contributions are welcome, this is just a little hobby script I wrote to make my life easier. Feel free to change it and send a pull request. diff --git a/requirements.txt b/requirements.txt index 1dc71f4..f315a07 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,32 +1,2 @@ -attr==0.3.2 -brotli==1.0.9 -brotlicffi==1.0.9.2 -ConfigParser==5.3.0 -cryptography==38.0.1 -Cython==0.29.32 -dl==0.1.0 -docutils==0.19 -HTMLParser==0.0.2 -ipython==8.5.0 -ipywidgets==8.0.2 -Jinja2==3.1.2 -jnius==1.1.0 -lockfile==0.12.2 -mock==4.0.3 -numpy==1.23.2 -Pillow==9.2.0 -pluggy==1.0.0 -protobuf==4.21.5 -pyOpenSSL==22.0.0 -pytest==7.1.3 -railroad==0.5.0 -secretstorage==3.3.3 -simplejson==3.17.6 -Sphinx==5.1.1 -toml==0.10.2 -tornado==6.2 -trove_classifiers==2022.8.31 -truststore==0.5.0 -unicodedata2==14.0.0 -urllib3_secure_extra==0.1.0 -xmlrpclib==1.0.1 +tidalapi==0.7.0 + From 4be4a06aef2e3fb075f4dcbd257b08e820bed2c7 Mon Sep 17 00:00:00 2001 From: johnpacheco1 Date: Fri, 14 Oct 2022 06:01:56 -0500 Subject: [PATCH 2/4] fixing readme spacing --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b5172b2..b0e823d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,8 @@ I'm not responsible for bricked devices, dead SD cards, thermonuclear war, or yo 1. Go enjoy some tunes! Notice your new playlist which is a copy of your current one but with an " - Electrified" suffix. This is the shuffled one. ### Running ElectricShuffleAll -This script requires no human intervention beyond the initial login. Run it one time manually to login, then automate to your heart's content. +This script requires no human intervention beyond the initial login. Run it one time manually to login, then automate to your heart's content. + **Note:** On linux VMs there is an issue where the tidalapi package sends requests too fast for the tidal servers. Adding time.sleep(1) to the tidalapi playlist.py \__init__ resolves the issue. 1. Run the program `./ElectricShuffleAll.py` From f31818ac37cef27bf403a7ef72e9b6ef1e851e1f Mon Sep 17 00:00:00 2001 From: johnpacheco1 Date: Fri, 14 Oct 2022 06:27:28 -0500 Subject: [PATCH 3/4] fixing logic with electric shuffle playlist selection --- ElectricShuffle.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/ElectricShuffle.py b/ElectricShuffle.py index 3004f82..32f5b1d 100644 --- a/ElectricShuffle.py +++ b/ElectricShuffle.py @@ -41,16 +41,18 @@ out_file.write(json.dumps(session_dict)) ### Now that session is handled, get playlists -playlists = session.user.playlists() -print(playlists) + +playlists_no_e = session.user.playlists() +for playlist in playlists_no_e: + if("- Electrified" in playlist.name): + playlists_no_e.remove(playlist) print("Please select the playlist you wish to shuffle by intering it's number below") -for idx, playlist in enumerate(playlists): - if("- Electrified" not in playlist.name): - print(f" [{idx+1}] {playlist.name}") +for idx, playlist in enumerate(playlists_no_e): + print(f" [{idx+1}] {playlist.name}") # Get playlists on user account playlist_num = int(input("Playlist Number: ")) - 1 -selected_playlist = playlists[playlist_num] +selected_playlist = playlists_no_e[playlist_num] print(f"Electric shuffle {selected_playlist.name}?") should_continue = bool(input("Continue (Yes/No): ")) @@ -67,6 +69,9 @@ ### Signify that this is a copy for the user shuffled_name = f"{selected_playlist.name} - Electrified" + +# Get all playlists including - Electrified +playlists = session.user.playlists() for playlist in playlists: ### Delete an existing version of this playlist if we are re-rolling if playlist.name == shuffled_name: @@ -78,3 +83,4 @@ ## Add the beautiful music after a quick shuffle random.shuffle(track_ids) new_playlist.add(track_ids) +print("Shuffling complete") \ No newline at end of file From 5f2b753acfe0a5af8c3baa65b37935b69252f395 Mon Sep 17 00:00:00 2001 From: johnpacheco1 Date: Fri, 14 Oct 2022 06:33:54 -0500 Subject: [PATCH 4/4] updating readme for accuracy --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b0e823d..018547a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ I'm not responsible for bricked devices, dead SD cards, thermonuclear war, or yo ### Running ElectricShuffle 1. Run the program `./ElectricShuffle.py` -1. Log in by clicking the link (or copying it to your browser). Once logged in return to the terminal. (Once per environment) +1. Log in by clicking the link (or copying it to your browser). Once logged in return to the terminal. 1. Pick a playlist to shuffle 1. Confirm that is the playlist you want 1. Go enjoy some tunes! Notice your new playlist which is a copy of your current one but with an " - Electrified" suffix. This is the shuffled one.