From bb5453b59eb87d2d8c4d6b9a46852247019b7cf4 Mon Sep 17 00:00:00 2001
From: Ilya Shurupov <163508118+elushaX@users.noreply.github.com>
Date: Mon, 8 Jul 2024 23:14:29 +0300
Subject: [PATCH 1/2] initial
---
.gitignore | 4 ++
README.md | 3 +-
common.py | 25 ++++++++
loadArtwork.py | 45 +++++++++++++++
main.py | 106 ++++++++++++++++++++++++++++++++++
spotifyAuth.py | 122 ++++++++++++++++++++++++++++++++++++++++
spotifyFetch.py | 147 ++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 451 insertions(+), 1 deletion(-)
create mode 100644 .gitignore
create mode 100644 common.py
create mode 100644 loadArtwork.py
create mode 100644 main.py
create mode 100644 spotifyAuth.py
create mode 100644 spotifyFetch.py
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2803b17
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+prefetched*
+__pycache__
+.idea
+secret
diff --git a/README.md b/README.md
index 508172a..68b9ba5 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,3 @@
# spotifyFetcher
-loads all data through web api
+loads all data through web api
+client secret - ***REDACTED***
diff --git a/common.py b/common.py
new file mode 100644
index 0000000..bb08d3e
--- /dev/null
+++ b/common.py
@@ -0,0 +1,25 @@
+import json
+import os
+
+data_dir = 'prefetched'
+
+
+def get_data(name):
+ file_path = os.path.join(data_dir, f"{name}.json")
+ try:
+ with open(file_path, 'r') as file:
+ data = json.load(file)
+ return data
+ except FileNotFoundError:
+ return f"File {name}.json not found."
+ except json.JSONDecodeError:
+ return f"Error decoding JSON from {name}.json."
+
+
+def save_data(data, name):
+ os.makedirs(data_dir, exist_ok=True)
+
+ file_path = os.path.join(data_dir, f"{name}.json")
+ with open(file_path, 'w') as file:
+ json.dump(data, file, indent=4)
+ return f"Data saved to {name}.json."
\ No newline at end of file
diff --git a/loadArtwork.py b/loadArtwork.py
new file mode 100644
index 0000000..4d2dc38
--- /dev/null
+++ b/loadArtwork.py
@@ -0,0 +1,45 @@
+import requests
+import os
+from PIL import Image
+from io import BytesIO
+
+from common import *
+
+
+def save_image_from_url(url, name, image_dir="playlist_covers"):
+ response = requests.get(url)
+
+ if response.status_code != 200:
+ raise "Cannot fetch the playlist cover"
+
+ image = Image.open(BytesIO(response.content))
+
+ directory = os.path.join(data_dir, image_dir)
+ os.makedirs(directory, exist_ok=True)
+ file_path = os.path.join(directory, f"{name}.jpg")
+ print(f"Image saved {file_path}")
+ image.save(file_path)
+
+
+def load_playlist_covers():
+ playlists = get_data('playlists')
+
+ for pl in playlists:
+ if len(pl['images']):
+ url = pl['images'][0]['url']
+ save_image_from_url(url, pl['name'])
+
+
+def load_user_cover():
+ user_data = get_data("user_data")
+ url = user_data['images'][1]['url']
+ save_image_from_url(url, "user", ".")
+
+
+def load():
+ load_user_cover()
+ load_playlist_covers()
+
+
+if __name__ == "__main__":
+ load()
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..c88776b
--- /dev/null
+++ b/main.py
@@ -0,0 +1,106 @@
+from common import *
+from spotifyFetch import fetch_data
+import cmd
+
+playlists = get_data('playlistTracks')
+top_artists = get_data('top_artists')
+top_tracks = get_data('top_tracks')
+user_tracks = get_data('tracks')
+
+
+def get_playlist_names():
+ return [name for name, items in playlists.items()]
+
+
+def get_artists_str(artists_pack):
+ track_artists = [artist['name'] for artist in artists_pack]
+ artists = " ".join(track_artists)
+ return artists
+
+
+def print_playlist_tracks(name):
+ print(name)
+ for track in playlists[name]:
+ track_data = track['track']
+ track_name = track_data['name']
+ artists = get_artists_str(track_data['artists'])
+ print(f" '{track_name}' - '{artists}' ")
+
+
+def print_top_artists():
+ for artist in top_artists:
+ name = artist['name']
+ print(f" '{name}'")
+
+
+def print_top_tracks():
+ for track in top_tracks:
+ track_name = track['name']
+ artists = get_artists_str(track['artists'])
+ print(f" '{track_name}' - '{artists}' ")
+
+
+def print_stats():
+ print(f" tracks - {len(user_tracks)}")
+ print(f" playlists - {len(playlists)}")
+
+ for name, tracks in playlists.items():
+ print(f" '{name}' - {len(tracks)}")
+
+ print(f" top tracks - {len(top_tracks)}")
+ print(f" top artists - {len(top_artists)}")
+
+
+def print_tracks():
+ for trackItem in user_tracks:
+ track = trackItem['track']
+ print(f" '{track['name']}' - {get_artists_str(track['artists'])}")
+
+
+class Interpreter(cmd.Cmd):
+ intro = "Welcome to the interactive command loop. Type help or ? to list commands.\n"
+ prompt = "(spotify) "
+
+ def do_playlists(self, arg):
+ """prints user playlists"""
+ print("\n".join(get_playlist_names()))
+
+ def do_playlist_tracks(self, arg):
+ """prints playlist [name]"""
+ try:
+ name = arg.split()[0]
+ print_playlist_tracks(name)
+ except IndexError:
+ print("Invalid input")
+
+ def do_top_artists(self, arg):
+ """Top artists"""
+ print_top_artists()
+
+ def do_top_tracks(self, arg):
+ """Top tracks"""
+ print_top_tracks()
+
+ def do_stat(self, arg):
+ """Print stats"""
+ print_stats()
+
+ def do_tracks(self, arg):
+ """Print tracks"""
+ print_tracks()
+
+ def do_fetch(self, arg):
+ """Fetch all spotify data"""
+ try:
+ fetch_data()
+ except ValueError:
+ print("Can not fetch the data.")
+
+ def do_exit(self, arg):
+ """Exit the command loop: exit"""
+ print("Goodbye!")
+ return True
+
+
+if __name__ == '__main__':
+ Interpreter().cmdloop()
diff --git a/spotifyAuth.py b/spotifyAuth.py
new file mode 100644
index 0000000..64a4b2c
--- /dev/null
+++ b/spotifyAuth.py
@@ -0,0 +1,122 @@
+import base64
+
+import requests
+import webbrowser
+from http.server import BaseHTTPRequestHandler, HTTPServer
+import urllib.parse
+import threading
+import time
+import os.path
+
+client_id = '***REDACTED***'
+client_secret = None
+token = None
+
+
+def resolve_client_secret():
+ global client_secret
+ if os.path.exists('secret'):
+ with open('secret', 'r') as file:
+ client_secret = file.readline().strip()
+ else:
+ client_secret = input("Enter the application secret: ")
+ with open('secret', 'w') as file:
+ file.write(client_secret)
+
+
+def retrieve_web_api_token(authorization_code):
+ global token
+
+ token_url = 'https://accounts.spotify.com/api/token'
+
+ token_data = {
+ "grant_type": "authorization_code",
+ "client_secret": client_secret,
+ "client_id": client_id,
+ "code": authorization_code,
+ 'redirect_uri': 'http://localhost:8888',
+ }
+
+ print("\nAuthenticating WEB API\n")
+
+ auth_string = f'{client_id}:{client_secret}'
+ b64_auth_string = base64.b64encode(auth_string.encode()).decode()
+
+ # Define the headers
+ headers = {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ 'Authorization': f'Basic {b64_auth_string}'
+ }
+
+ token_response = requests.post(token_url, data=token_data, headers=headers)
+
+ if token_response.status_code not in range(200, 299):
+ print(f"Failed to retrieve access token: {token_response.status_code}")
+ print(token_response.json())
+ raise ValueError
+
+ token = token_response.json()['access_token']
+
+
+class RequestHandler(BaseHTTPRequestHandler):
+ def do_GET(self):
+
+ parsed_url = urllib.parse.urlparse(self.path)
+ query_params = urllib.parse.parse_qs(parsed_url.query)
+
+ if 'code' in query_params:
+ authorization_code = query_params['code'][0]
+
+ self.send_response(200)
+ self.send_header('Content-type', 'text/html')
+ self.end_headers()
+ self.wfile.write(b'