Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
.venv
.idea
qr_codes
qr_codes
.env
.cache
.spotify_token_cache
hitster.pdf
overview.pdf
songs.json
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@ If you find this DIY Hitster project useful, consider giving it a star :) !
export CLIENT_SECRET="your_client_secret"
export PLAYLIST_ID="your_playlist_id"
```
- If your playlist is private/collaborative, use user auth mode:
```bash
export SPOTIFY_AUTH_MODE="user"
export SPOTIFY_REDIRECT_URI="http://127.0.0.1:8888/callback"
```
Then add that redirect URI in your Spotify app settings.

## ▶️ Usage

1. Run the `main.py` script:
1. Run the `main.py` script (put the exports into a .env fie first):
```bash
poetry run python main.py
source .env && poetry run python main.py
```
2. Print the `hitster.pdf` file:
- Open the `hitster.pdf` file and duplex print it.
Expand All @@ -62,3 +68,10 @@ If you find this DIY Hitster project useful, consider giving it a star :) !
- Spotify Playlist: Change the `PLAYLIST_ID` environment variable to use a different Spotify playlist.
- Card Dimensions: Change the `card_size`, `rows`, and `cols` variables in `hitster.typ` to adjust the card dimensions and page layout.
- Marking Size: Change the `marking_padding` to adjust the space for cutting.

## Troubleshooting

- `403 Forbidden` while reading a playlist usually means your playlist is not publicly accessible to client-credentials auth.
- Fix options:
- make the playlist public, or
- use `SPOTIFY_AUTH_MODE="user"` with `SPOTIFY_REDIRECT_URI` and log in once in the browser.
66 changes: 54 additions & 12 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
from matplotlib.backends.backend_pdf import PdfPages

import qrcode
import requests
import spotipy
import typst
from spotipy import SpotifyClientCredentials
from spotipy.oauth2 import SpotifyOAuth
import qrcode.image.svg
import logging
import calendar
Expand All @@ -25,6 +27,42 @@ def get_env_var(key):
return value


def get_env_var_optional(key):
return os.getenv(key)


def create_spotify_client():
auth_mode = get_env_var_optional("SPOTIFY_AUTH_MODE") or "client_credentials"

if auth_mode == "user":
# User auth can access private/collaborative playlists.
client_id = get_env_var("CLIENT_ID")
client_secret = get_env_var("CLIENT_SECRET")
redirect_uri = get_env_var("SPOTIFY_REDIRECT_URI")
scope = (
get_env_var_optional("SPOTIFY_SCOPE")
or "playlist-read-private playlist-read-collaborative"
)

return spotipy.Spotify(
auth_manager=SpotifyOAuth(
client_id=client_id,
client_secret=client_secret,
redirect_uri=redirect_uri,
scope=scope,
cache_path=".spotify_token_cache",
open_browser=True,
)
)

return spotipy.Spotify(
auth_manager=SpotifyClientCredentials(
client_id=get_env_var("CLIENT_ID"),
client_secret=get_env_var("CLIENT_SECRET"),
)
)


def resolve_date(date_str):
date_parts = date_str.split("-")[::-1]
parts = [""] * (3 - len(date_parts)) + date_parts
Expand All @@ -38,12 +76,21 @@ def resolve_date(date_str):

def get_playlist_songs(sp, playlist_id):
songs = []
results = sp.playlist_tracks(playlist_id)
next_url = f"https://api.spotify.com/v1/playlists/{playlist_id}/items?limit=100&additional_types=track"

while next_url:
token = sp.auth_manager.get_access_token(as_dict=False)
response = requests.get(
next_url,
headers={"Authorization": f"Bearer {token}"},
timeout=30,
)
response.raise_for_status()
results = response.json()

while results:
for item in results["items"]:
track = item["track"]
if track:
for item in results.get("items", []):
track = item.get("track") or item.get("item")
if track and track.get("type") == "track":
day, month, year = resolve_date(track["album"]["release_date"])
songs.append(
{
Expand All @@ -58,7 +105,7 @@ def get_playlist_songs(sp, playlist_id):
}
)

results = sp.next(results) if results["next"] else None
next_url = results.get("next")

random.shuffle(songs)
return songs
Expand Down Expand Up @@ -93,12 +140,7 @@ def generate_overview_pdf(songs, output_pdf):


def main():
sp = spotipy.Spotify(
auth_manager=SpotifyClientCredentials(
client_id=get_env_var("CLIENT_ID"),
client_secret=get_env_var("CLIENT_SECRET"),
)
)
sp = create_spotify_client()

logging.info("Starting Spotify song retrieval")
songs = get_playlist_songs(sp, get_env_var("PLAYLIST_ID"))
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version = "0.1.0"
description = ""
authors = ["fjlein <fjlein@gmail.com>"]
readme = "README.md"
package-mode = false

[tool.poetry.dependencies]
python = "^3.12"
Expand Down