Posted on

Podczas pandemii jedną z form spędzania czasu było jeżdżenie samochodem po mieście. Wszystko było zamykane, straż miejska robiła łapanki ludzi spacerujących w odległości mniejszej niż 5 metrów - samochód był enklawą wolności w perspektywie domowego więzienia. W tym też czasie zdarzało mi się słuchać dużo radia ChilliZet, które raz na czas serwowało muzykę, która naprawdę mi się podobała. Dużo takich inspiracji wylądowało finalnie w mojej bibliotece, np. Kinnship:

Kinnship - Backwards [Official Lyric Video] Kinnship

Ale do rzeczy.

W radiu było na tyle dużo fajnych piosenek, że pomyślałem, że może bym sobie skombinował ich całą playlistę, i słuchał na własnych warunkach? Bo tam czasami leciała też średnia muzyka, a w swojej biblitece miałbym pełną kontrolę nad moderacją. Wbiłem do internetu, i okazuje się, że na stronie Sprawdź co graliśmy jest dokładna playlista. Pierwsze zwycięstwo.

Strona podaje piosenki w godzinowych iteracjach, żeby zmieniać godziny, wystarczy zmienić cyfrę w adresie, np. dla godziny 14:00 jest to

https://player.chillizet.pl/Sprawdz-co-gralismy/(godzina)/14

Co prawda przez to mam dostęp do muzyki tylko z ostatniej doby, ale zawsze coś, można puścić jakieś cykliczne zadanie, i niech się akumulują dane. "Napisałem" (bo tak naprawdę to Cursor napisał) super prosty skrypt w Pythonie, który pobiera wszystkie strony, parsuje HTML, i wyciąga dane. Odpalam... i klops. Dane są ładowane dynamicznie, za pomocą JavaScriptu.

Dobra, to dajcie mi ten skrypt Javy. Inspekcja strony, i cyk:

https://gfx-player.chillizet.pl/design/player/javascript/played-history.js

Otwieram plik, no i wiadomo: obfuskacja, minimalizacja, masturbacja. Przekopiowałem kod do Cursora, i sformatowałem go. Nadal koszmar. No to wklepałem do promptera: "convert this to Python script". Dwie minuty później, mam gigantyczny projekt z chyba 15 plikami, dokumentacją, testami, plikiem README mówiącym mi, jak mam żyć. Ugh, do kosza. Jeszcze raz: "analyze this JS code, understand it, and create minimal Python script to fetch data about played songs". Kolejne dwie minuty... i tym razem się udało. Nadal dużo bigosu, ale już tylko jeden plik, no i nawet elegancki interfejs:

% python3 minimal_radio.py -h
usage: minimal_radio.py [-h] [--station STATION] [--date DATE] [--time TIME] [--csv CSV]
                        [--print] [--count COUNT]

Fetch radio station history

options:
  -h, --help            show this help message and exit
  --station, -s STATION
                        Radio station (default: radiozet)
  --date, -d DATE       Date in YYYY-MM-DD format (default: current date)
  --time, -t TIME       Time in HH:MM format (default: current time)
  --csv, -c CSV         Save tracks to CSV file (default: tracks.csv)
  --print, -p           Print tracks to console
  --count, -n COUNT     Maximum number of tracks to fetch (default: unlimited)

No i tak się bawię tym, testuję sobie, ale coś mi nie gra. Wbijam konkretną stację, datę, godzinę, count, i dostaję wyniki z wybranej godziny, ale też późniejsze. Hmm. A co się stanie, jak wpiszę do count np. 1000? No proszę. Dostanę 1000 kolejnych wpisów, zaczynając od wpisanej daty i godziny. A jak wpiszę 999999? To dostanę wszystkie. Doskonale - bo teraz już nie będę musiał robić ani żadnych cronów, fetchów, scrapeów, tylko sobie ugotuję dosłownie jeden skrypt w Bashu, i dostanę wszystkie dane, jakie tylko są mi potrzebne.

Po chwili testów okazało się, że pierwszy wpis w bazie jest z dnia 5 maja 2022, z godziny 4:00. Nie należy też przesadzać z liczbą pobieranych ścieżek. Liczba, która u mnie działała to 110000, to mniej więcej liczba ścieżek odgrywanych w ciągu jednego roku, więc skrypt wystarczy ustawić na 1 stycznia na godzinę 00:00, i uruchomić dosłownie 4 razy (dla lat 2022-25), żeby mieć całą historię piosenek (i nie tylko) z Chilli Zet. Oczywiście przetworzenie tych danych to osobna bajka, ale już nie na ten wpis.

Oto finalny skrypt dla potomnych:

#!/usr/bin/env zsh

DATE=2022-05-04
TIME=10:00:00

TIMESTAMP=$(date -j -f '%Y-%m-%dT%H:%M:%SZ' "${DATE}T${TIME}Z" +%s)
EMITTER=chilli
TRACK_COUNT=20

URL="https://rds.eurozet.pl/reader/history.php?true=jsonData"

[[ -n "$TIMESTAMP" ]] && URL="$URL&startDate=$TIMESTAMP"
[[ -n "$EMITTER" ]] && URL="$URL&emitter=$EMITTER"
[[ -n "$TRACK_COUNT" ]] && URL="$URL&trackCount=$TRACK_COUNT"

curl -s "$URL" | sed 's/^jsonData(//;s/)$//' | jq . >output.json

Dobra, jeszcze na szybkości skrypt w Pythonie (wygenerowany przez Cursora oczywiście), do wyeksportowania unikalnych piosenek do pliku CSV:

#!/usr/bin/env python3

import json
import csv
import glob

# Read all JSON files and aggregate data
all_songs = []
seen_ids = set()

for file in glob.glob("*.json"):
  with open(file) as f:
    data = json.load(f)
    for song in data["messages"][0]:
      if (
        song["rds_song_id"] not in seen_ids
        and song.get("rds_title")
        and song.get("rds_artist")
      ):
        all_songs.append(song)
        seen_ids.add(song["rds_song_id"])

# Export to CSV
with open("aggregated_songs.csv", "w", newline="") as f:
  if all_songs:
    writer = csv.DictWriter(f, fieldnames=all_songs[0].keys())
    writer.writeheader()
    writer.writerows(all_songs)

print(f"Exported {len(all_songs)} unique songs to aggregated_songs.csv")