Compare commits

...

7 Commits

8 changed files with 42 additions and 10 deletions

1
.gitignore vendored
View File

@@ -3,4 +3,3 @@ todo
env env
__pycache__ __pycache__
redis-data redis-data
docker-compose.yml

View File

@@ -18,13 +18,23 @@ class PlaylistUpdater:
def _update_tracks(self): def _update_tracks(self):
self._tracks = [] self._tracks = []
headers = { 'Authorization': f"Bearer {self._auth_token}" }
rnext = f"{self._spotify_api_endpoint}/me/tracks?limit=50" rnext = f"{self._spotify_api_endpoint}/me/tracks?limit=50"
while rnext is not None: while rnext is not None:
r = rq.get(rnext, headers = headers).json() r = self._fetch(rnext);
self._tracks += r['items'] self._tracks += r['items']
rnext = r['next'] rnext = r['next']
def _fetch(self, url):
headers = { 'Authorization': f"Bearer {self._auth_token}" }
while True:
r = rq.get(f"{url}", headers=headers)
if 200 <= r.status_code <= 299:
return r.json()
# slow down if spotify is applying rate limiting
if r.status_code == 429:
time.sleep(30)
# don't try again in a crazy fast loop to avoid rate limits
time.sleep(1)
def update_playlist(self, playlist_id): def update_playlist(self, playlist_id):
pltracks = [] pltracks = []
@@ -34,12 +44,12 @@ class PlaylistUpdater:
headers = { 'Authorization': f"Bearer {self._auth_token}" } headers = { 'Authorization': f"Bearer {self._auth_token}" }
added_at_dict = {} added_at_dict = {}
r = rq.get(f"{self._spotify_api_endpoint}/playlists/{playlist_id}?fields=tracks,snapshot_id", headers = headers).json() r = self._fetch(f"{self._spotify_api_endpoint}/playlists/{playlist_id}?fields=tracks,snapshot_id")
snapshot_id = r['snapshot_id'] snapshot_id = r['snapshot_id']
pltracks = [t['track']['id'] for t in r['tracks']['items']] pltracks = [t['track']['id'] for t in r['tracks']['items']]
rnext = r['tracks']['next'] rnext = r['tracks']['next']
while rnext is not None: while rnext is not None:
r = rq.get(rnext, headers = headers).json() r = self._fetch(rnext)
for track in r['items']: for track in r['items']:
pltracks.append(track['track']['id']) pltracks.append(track['track']['id'])
rnext = r['next'] rnext = r['next']

View File

@@ -2,6 +2,7 @@ version: '3.9'
services: services:
web: web:
hostname: heartbeats-web
build: build:
context: . context: .
dockerfile: ./web/Dockerfile dockerfile: ./web/Dockerfile
@@ -14,12 +15,14 @@ services:
- BASE_URL=http://localhost:8464 - BASE_URL=http://localhost:8464
redis: redis:
hostname: heartbeats-redis
image: redis image: redis
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- './redis-data:/data' - './redis-data:/data'
populater: populater:
hostname: heartbeats-populater
build: build:
context: . context: .
dockerfile: ./populater/Dockerfile dockerfile: ./populater/Dockerfile

2
populater/src/app.py Executable file → Normal file
View File

@@ -16,7 +16,7 @@ CLIENT_SECRET = os.environ.get('CLIENT_SECRET', None)
if CLIENT_SECRET is None: if CLIENT_SECRET is None:
raise ValueError("CLIENT_SECRET cannot be None, set using environment variable") raise ValueError("CLIENT_SECRET cannot be None, set using environment variable")
db = Db('redis', 6379, 0, CLIENT_ID, CLIENT_SECRET) db = Db('heartbeats-redis', 6379, 0, CLIENT_ID, CLIENT_SECRET)
def get_args(): def get_args():

16
readme.md Normal file
View File

@@ -0,0 +1,16 @@
# :musical_note: heartbeats :saxophone:
> Maintain a (potentially) public list of your recently liked songs so people know how cool you are
# Setup
0. Create a spotify developer account and create a new app
0. Create a `.env` file with your spotify secrets:
```
CLIENT_ID=<client_id>
CLIENT_SECRET=<client_secret>
```
0. Run `docker compose up -d` to start

View File

@@ -28,7 +28,7 @@ BASE_URL = os.environ.get('BASE_URL', None)
if BASE_URL is None: if BASE_URL is None:
raise ValueError("BASE_URL cannot be None, set using environtment variable") raise ValueError("BASE_URL cannot be None, set using environtment variable")
db = Db('redis', 6379, 0, CLIENT_ID, CLIENT_SECRET) db = Db('heartbeats-redis', 6379, 0, CLIENT_ID, CLIENT_SECRET)
SPOTIFY_AUTHORIZE_ENDPOINT = os.environ.get('SPOTIFY_AUTHORIZE_ENDPOINT', 'https://accounts.spotify.com/authorize?') SPOTIFY_AUTHORIZE_ENDPOINT = os.environ.get('SPOTIFY_AUTHORIZE_ENDPOINT', 'https://accounts.spotify.com/authorize?')
SPOTIFY_TOKEN_ENDPOINT = os.environ.get('SPOTIFY_AUTHORIZE_ENDPOINT', 'https://accounts.spotify.com/api/token') SPOTIFY_TOKEN_ENDPOINT = os.environ.get('SPOTIFY_AUTHORIZE_ENDPOINT', 'https://accounts.spotify.com/api/token')

View File

@@ -3,12 +3,16 @@
<style> <style>
@import url("https://styles.alv.cx/colors/gruvbox.css"); @import url("https://styles.alv.cx/colors/gruvbox.css");
@import url("https://styles.alv.cx/base.css"); @import url("https://styles.alv.cx/base.css");
@import url("https://styles.alv.cx/modules/darkmode.css");
:root { :root {
--light: var(--colorscheme-light); --colorscheme-dark: #000;
--fg: var(--colorscheme-light);
--fg-lc: var(--colorscheme-light-darker);
--bg: var(--colorscheme-dark);
--bg-lc: var(--colorscheme-dark-lighter);
} }
.messagetypeinfo { background-color: var(--blue); } .messagetypeinfo { background-color: var(--blue); }
.messagetypeerror { background-color: var(--red); } .messagetypeerror { background-color: var(--red); }
.messagetypewarning { background-color: var(--yellow); } .messagetypewarning { background-color: var(--yellow); }

View File

@@ -12,7 +12,7 @@
</select></label></label> <br><br> </select></label></label> <br><br>
<label for="pname">name: </label> <label for="pname">name: </label>
<input type="text" id="pname" name="pname" placeholder="playlist_name"><br><br>number of tracks/days <input type="text" id="pname" name="pname" placeholder="playlist_name"><br><br>number of tracks/days
<input type="number" id="count" name="count" value="25" style="width: 5em;"> <input type="number" id="count" name="count" value="28" style="width: 5em;">
<select id="counttype" name="counttype"> <select id="counttype" name="counttype">
<option value="days">days</option> <option value="days">days</option>
<option value="tracks">tracks</option> <option value="tracks">tracks</option>