From 04da522b64e32a10a32243f807a797bafe25fbd1 Mon Sep 17 00:00:00 2001
From: Ilya Shurupov <163508118+elushaX@users.noreply.github.com>
Date: Mon, 8 Jul 2024 23:14:12 +0300
Subject: [PATCH 1/6] Initial commit
---
README.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 README.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..508172a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# spotifyFetcher
+loads all data through web api
From 506258f9b12ad3ddccdeb637ebdc3b84d111133c Mon Sep 17 00:00:00 2001
From: Ilya Shurupov <163508118+elushaX@users.noreply.github.com>
Date: Mon, 8 Jul 2024 23:14:12 +0300
Subject: [PATCH 2/6] Initial commit
---
README.md | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 README.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..508172a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# spotifyFetcher
+loads all data through web api
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 3/6] 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'
Authorization code received. You can close this window.')
+
+ retrieve_web_api_token(authorization_code)
+
+ threading.Thread(target=self.server.shutdown).start()
+
+ else:
+ self.send_response(400)
+ self.send_header('Content-type', 'text/html')
+ self.end_headers()
+ self.wfile.write(b'Authorization code not found.')
+
+
+def run_redirect_server():
+ httpd = HTTPServer(('localhost', 8888), RequestHandler)
+ print('Starting HTTP redirection server')
+ httpd.serve_forever()
+
+
+def open_authenticate_page():
+ authorize_url = 'https://accounts.spotify.com/authorize'
+
+ params = {
+ 'client_id': client_id,
+ 'response_type': 'code',
+ 'redirect_uri': 'http://localhost:8888',
+ "scope": "user-top-read playlist-read-collaborative user-read-email user-library-read user-read-private playlist-read-private",
+ }
+
+ auth_url = f"{authorize_url}?client_id={params['client_id']}&response_type={params['response_type']}&redirect_uri={params['redirect_uri']}&scope={params['scope']}"
+ webbrowser.open(auth_url)
+
+
+def authenticate():
+ resolve_client_secret()
+
+ redirect_server = threading.Thread(target=run_redirect_server)
+ redirect_server.start()
+
+ time.sleep(1)
+ open_authenticate_page()
+
+ redirect_server.join()
+
+ return token
+
+
+if __name__ == "__main__":
+ authenticate()
+
diff --git a/spotifyFetch.py b/spotifyFetch.py
new file mode 100644
index 0000000..ec590bc
--- /dev/null
+++ b/spotifyFetch.py
@@ -0,0 +1,147 @@
+import requests
+from loadArtwork import load
+from spotifyAuth import authenticate
+from common import *
+
+FETCH_STEP = 50
+token = ''
+
+
+def fetch(endpoint, method='GET', body=None):
+ url = f'https://api.spotify.com/{endpoint}'
+ headers = {
+ 'Authorization': f'Bearer {token}',
+ }
+
+ response = requests.request(method, url, headers=headers, json=body)
+ response.raise_for_status()
+ return response.json()
+
+
+def fetch_playlists(user_id):
+ print("Fetching playlists")
+ endpoint = f"v1/me/playlists"
+ pl_total = fetch(f'{endpoint}?limit=1')['total']
+ pl_count = 0
+ playlists = []
+
+ while pl_count < pl_total:
+ res = fetch(f'{endpoint}?limit={FETCH_STEP}&offset={pl_count}')
+ playlists += res['items']
+ pl_count += FETCH_STEP
+
+ return playlists
+
+
+def fetch_playlist_items(playlist_id):
+ endpoint = f"v1/playlists/{playlist_id}/tracks"
+ items = fetch(f'{endpoint}?fields=total')['total']
+ loaded = 0
+ tracks = []
+
+ while loaded < items:
+ res = fetch(f'{endpoint}?limit={FETCH_STEP}&offset={loaded}')
+
+ tracks += res['items']
+ loaded += FETCH_STEP
+
+ return tracks
+
+
+def fetch_tracks(user_id):
+ print("Fetching tracks")
+ endpoint = f"v1/me/tracks"
+ total = fetch(f"{endpoint}?limit=1&market=ES")['total']
+ current = 0
+ tracks = []
+
+ while total > current:
+ res = fetch(f"{endpoint}?limit={FETCH_STEP}&offset={current}&market=ES")
+ tracks += res['items']
+ current += FETCH_STEP
+
+ save_data(tracks, "tracks")
+ return tracks
+
+
+def fetch_tracks_top():
+ print("Fetching top tracks")
+ total = fetch(f'v1/me/top/tracks?fields=total')['total']
+ current = 0
+ tracks = []
+
+ while current < total:
+ res = fetch(f'v1/me/top/tracks?limit={FETCH_STEP}&offset={current}&time_range=long_term')
+ tracks += res['items']
+ current += FETCH_STEP
+
+ save_data(tracks, "top_tracks")
+ return tracks
+
+
+def fetch_artists_top():
+ print("Fetching top artists")
+
+ total = fetch(f'v1/me/top/artists?fields=total')['total']
+ current = 0
+ artists = []
+
+ while current < total:
+ res = fetch(f'v1/me/top/artists?limit={FETCH_STEP}&offset={current}&time_range=long_term')
+ artists += res['items']
+ current += FETCH_STEP
+
+ save_data(artists, "top_artists")
+ return artists
+
+
+def fetch_all_playlist_data(user_id):
+ playlists = fetch_playlists(user_id)
+
+ save_data(playlists, 'playlists')
+
+ print(len(playlists))
+
+ playlist_items = {}
+
+ print("Fetching playlists items")
+
+ index = 0
+ for pl in playlists:
+ pl_id = pl['id']
+ name = pl['name']
+ playlist_items[name] = fetch_playlist_items(pl_id)
+ print(f"{index} - {len(playlist_items[name])}")
+ index += 1
+
+ save_data(playlist_items, "playlistTracks")
+
+
+def get_user_data():
+ print("Fetching user data")
+
+ user_profile = fetch(f'v1/me')
+ user_id = user_profile['id']
+ user_data = fetch(f'v1/users/{user_id}')
+
+ save_data(user_profile, "user_profile")
+ save_data(user_data, "user_data")
+ return user_id
+
+
+def fetch_data():
+ global token
+ token = authenticate()
+
+ user_id = get_user_data()
+
+ fetch_tracks(user_id)
+ fetch_all_playlist_data(user_id)
+ fetch_tracks_top()
+ fetch_artists_top()
+
+ load()
+
+
+if __name__ == "__main__":
+ fetch_data()
From 6703fdd9681e80ca07adcbc7f28a98a107cd0086 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 4/6] 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..4a45af3 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 - 4ced4e4575094b749834a60996680c6c
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..529c702
--- /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 = '3b4db45bb66b45e9a2532989c8034332'
+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'Authorization code received. You can close this window.')
+
+ retrieve_web_api_token(authorization_code)
+
+ threading.Thread(target=self.server.shutdown).start()
+
+ else:
+ self.send_response(400)
+ self.send_header('Content-type', 'text/html')
+ self.end_headers()
+ self.wfile.write(b'Authorization code not found.')
+
+
+def run_redirect_server():
+ httpd = HTTPServer(('localhost', 8888), RequestHandler)
+ print('Starting HTTP redirection server')
+ httpd.serve_forever()
+
+
+def open_authenticate_page():
+ authorize_url = 'https://accounts.spotify.com/authorize'
+
+ params = {
+ 'client_id': client_id,
+ 'response_type': 'code',
+ 'redirect_uri': 'http://localhost:8888',
+ "scope": "user-top-read playlist-read-collaborative user-read-email user-library-read user-read-private playlist-read-private",
+ }
+
+ auth_url = f"{authorize_url}?client_id={params['client_id']}&response_type={params['response_type']}&redirect_uri={params['redirect_uri']}&scope={params['scope']}"
+ webbrowser.open(auth_url)
+
+
+def authenticate():
+ resolve_client_secret()
+
+ redirect_server = threading.Thread(target=run_redirect_server)
+ redirect_server.start()
+
+ time.sleep(1)
+ open_authenticate_page()
+
+ redirect_server.join()
+
+ return token
+
+
+if __name__ == "__main__":
+ authenticate()
+
diff --git a/spotifyFetch.py b/spotifyFetch.py
new file mode 100644
index 0000000..ec590bc
--- /dev/null
+++ b/spotifyFetch.py
@@ -0,0 +1,147 @@
+import requests
+from loadArtwork import load
+from spotifyAuth import authenticate
+from common import *
+
+FETCH_STEP = 50
+token = ''
+
+
+def fetch(endpoint, method='GET', body=None):
+ url = f'https://api.spotify.com/{endpoint}'
+ headers = {
+ 'Authorization': f'Bearer {token}',
+ }
+
+ response = requests.request(method, url, headers=headers, json=body)
+ response.raise_for_status()
+ return response.json()
+
+
+def fetch_playlists(user_id):
+ print("Fetching playlists")
+ endpoint = f"v1/me/playlists"
+ pl_total = fetch(f'{endpoint}?limit=1')['total']
+ pl_count = 0
+ playlists = []
+
+ while pl_count < pl_total:
+ res = fetch(f'{endpoint}?limit={FETCH_STEP}&offset={pl_count}')
+ playlists += res['items']
+ pl_count += FETCH_STEP
+
+ return playlists
+
+
+def fetch_playlist_items(playlist_id):
+ endpoint = f"v1/playlists/{playlist_id}/tracks"
+ items = fetch(f'{endpoint}?fields=total')['total']
+ loaded = 0
+ tracks = []
+
+ while loaded < items:
+ res = fetch(f'{endpoint}?limit={FETCH_STEP}&offset={loaded}')
+
+ tracks += res['items']
+ loaded += FETCH_STEP
+
+ return tracks
+
+
+def fetch_tracks(user_id):
+ print("Fetching tracks")
+ endpoint = f"v1/me/tracks"
+ total = fetch(f"{endpoint}?limit=1&market=ES")['total']
+ current = 0
+ tracks = []
+
+ while total > current:
+ res = fetch(f"{endpoint}?limit={FETCH_STEP}&offset={current}&market=ES")
+ tracks += res['items']
+ current += FETCH_STEP
+
+ save_data(tracks, "tracks")
+ return tracks
+
+
+def fetch_tracks_top():
+ print("Fetching top tracks")
+ total = fetch(f'v1/me/top/tracks?fields=total')['total']
+ current = 0
+ tracks = []
+
+ while current < total:
+ res = fetch(f'v1/me/top/tracks?limit={FETCH_STEP}&offset={current}&time_range=long_term')
+ tracks += res['items']
+ current += FETCH_STEP
+
+ save_data(tracks, "top_tracks")
+ return tracks
+
+
+def fetch_artists_top():
+ print("Fetching top artists")
+
+ total = fetch(f'v1/me/top/artists?fields=total')['total']
+ current = 0
+ artists = []
+
+ while current < total:
+ res = fetch(f'v1/me/top/artists?limit={FETCH_STEP}&offset={current}&time_range=long_term')
+ artists += res['items']
+ current += FETCH_STEP
+
+ save_data(artists, "top_artists")
+ return artists
+
+
+def fetch_all_playlist_data(user_id):
+ playlists = fetch_playlists(user_id)
+
+ save_data(playlists, 'playlists')
+
+ print(len(playlists))
+
+ playlist_items = {}
+
+ print("Fetching playlists items")
+
+ index = 0
+ for pl in playlists:
+ pl_id = pl['id']
+ name = pl['name']
+ playlist_items[name] = fetch_playlist_items(pl_id)
+ print(f"{index} - {len(playlist_items[name])}")
+ index += 1
+
+ save_data(playlist_items, "playlistTracks")
+
+
+def get_user_data():
+ print("Fetching user data")
+
+ user_profile = fetch(f'v1/me')
+ user_id = user_profile['id']
+ user_data = fetch(f'v1/users/{user_id}')
+
+ save_data(user_profile, "user_profile")
+ save_data(user_data, "user_data")
+ return user_id
+
+
+def fetch_data():
+ global token
+ token = authenticate()
+
+ user_id = get_user_data()
+
+ fetch_tracks(user_id)
+ fetch_all_playlist_data(user_id)
+ fetch_tracks_top()
+ fetch_artists_top()
+
+ load()
+
+
+if __name__ == "__main__":
+ fetch_data()
From 366860a1ad3efd32572eb16e6c71d3a85cfd6425 Mon Sep 17 00:00:00 2001
From: IlyaShurupov
Date: Mon, 8 Jul 2024 23:24:51 +0300
Subject: [PATCH 5/6] atmop
---
main.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/main.py b/main.py
index c88776b..49af365 100644
--- a/main.py
+++ b/main.py
@@ -93,6 +93,14 @@ class Interpreter(cmd.Cmd):
"""Fetch all spotify data"""
try:
fetch_data()
+
+ global playlists, top_artists, top_tracks, user_tracks
+
+ playlists = get_data('playlistTracks')
+ top_artists = get_data('top_artists')
+ top_tracks = get_data('top_tracks')
+ user_tracks = get_data('tracks')
+
except ValueError:
print("Can not fetch the data.")
From 6eea2ec040c53f86ac907651567c1394fc5fb96e Mon Sep 17 00:00:00 2001
From: IlyaShurupov
Date: Mon, 8 Jul 2024 23:24:51 +0300
Subject: [PATCH 6/6] atmop
---
main.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/main.py b/main.py
index c88776b..49af365 100644
--- a/main.py
+++ b/main.py
@@ -93,6 +93,14 @@ class Interpreter(cmd.Cmd):
"""Fetch all spotify data"""
try:
fetch_data()
+
+ global playlists, top_artists, top_tracks, user_tracks
+
+ playlists = get_data('playlistTracks')
+ top_artists = get_data('top_artists')
+ top_tracks = get_data('top_tracks')
+ user_tracks = get_data('tracks')
+
except ValueError:
print("Can not fetch the data.")