commit 00c0fd5189574009295f2c54294884849bb5278e Author: Toni Fadjukoff Date: Sun Mar 8 14:16:17 2026 +0000 Ensimmäinen versio: Spotify-päivittäjä diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ec794d0 --- /dev/null +++ b/.env.example @@ -0,0 +1,10 @@ +SPOTIPY_CLIENT_ID="From Spotify Developer" +SPOTIPY_CLIENT_SECRET="Acquire from Spotify Developer Console" +SPOTIPY_REDIRECT_URI="http://localhost:8888/callback" +PLAYLIST_ID="Spotify playlist ID" + +WIKI_API_URL="https://example.com/wiki/api.php" +WIKI_PAGE_TITLE="Levyraati" +WIKI_USERNAME="wiki user" +WIKI_PASSWORD="correct horse battery staple" + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..208bf25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Salaisuudet ja keksit +.env +.cache + +# Pythonin virtuaaliympäristö ja välimuisti +.venv/ +__pycache__/ + +# Vimin tiedostot +*.swp + +# Mahdolliset lokitiedostot +skripti_loki.log diff --git a/README.md b/README.md new file mode 100644 index 0000000..f141638 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# Wiki-to-Spotify Soittolistan Päivittäjä 🎵 + +Tämä Python-skripti hakee automaattisesti kuluvan viikon biisivalinnat MediaWiki-sivulta ja päivittää ne haluttuun Spotifyn soittolistaan. Skripti on suunniteltu ajettavaksi automaattisesti taustalla (esim. Cronilla). + +## Ominaisuudet +* Kirjautuu MediaWiki-rajapintaan ja lukee kuluvan viikon (esim. `== Viikko 10 ==`) sisällön. +* Etsii tekstistä suorat Spotify-linkit. +* Jättää huomiotta muiden palveluiden (esim. YouTube) linkit. +* Korvaa valitun Spotify-soittolistan sisällön löydetyillä kappaleilla. + +## Vaatimukset +* Python 3 +* [uv](https://github.com/astral-sh/uv) (suositeltu) tai perinteinen `pip` paketinhallintaan. +* Spotify Developer -sovelluksen tunnukset (Client ID & Client Secret). +* Tunnukset kohde-MediaWikiin. + +## Asennus ja käyttöönotto + +**1. Lataa koodi ja luo virtuaaliympäristö** + +```bash +# Luo virtuaaliympäristö uv:lla +uv venv + +# Aktivoi ympäristö (Mac/Linux) +source .venv/bin/activate +# TAI (Windows) +.venv\Scripts\activate + +# Asenna riippuvuudet +uv pip install spotipy requests python-dotenv +``` + +**2. Asetukset** + +Luo projektin juureen tiedosto nimeltä `.env` (voit kopioida `.env.example` -tiedoston pohjaksi) ja täytä sinne omat tietosi: + +```env +SPOTIPY_CLIENT_ID="sinun_spotify_client_id" +SPOTIPY_CLIENT_SECRET="sinun_spotify_client_secret" +SPOTIPY_REDIRECT_URI="http://localhost:8888/callback" +PLAYLIST_ID="soittolistan_id_jota_paivitetaan" + +WIKI_USERNAME="wiki_tunnuksesi" +WIKI_PASSWORD="wiki_salasanasi" +WIKI_API_URL="https://sinun-wikisi.fi/api.php" +WIKI_PAGE_TITLE="Sivun_Nimi" +``` + +**3. Ensimmäinen käynnistys ja Spotifyn luvitus** + +Ensimmäisellä ajokerralla skriptin täytyy saada sinulta lupa muokata soittolistaasi. + +Aja skripti terminaalissa: +```bash +python skripti.py +``` +* Skripti tulostaa terminaaliin pitkän Spotify-linkin. +* Kopioi linkki ja avaa se selaimessasi. +* Kirjaudu sisään Spotifyhin ja hyväksy luvat. +* Selaimesi ohjautuu tyhjälle sivulle (localhost). Kopioi tuon sivun osoiterivillä oleva koko URL ja liitä se takaisin terminaaliin. +* Projektikansioosi syntyy `.cache` -tiedosto, joka pitää sinut jatkossa kirjautuneena. **Älä jaa tätä tiedostoa eteenpäin!** + +## Automatisointi (Linux/Mac) + +Voit asettaa skriptin pyörimään automaattisesti esimerkiksi kerran päivässä Cron-työkalulla: + +```bash +crontab -e +``` +Lisää tiedoston loppuun (muista muuttaa oikea polku): +```bash +0 4 * * * cd /polku/projektiin && .venv/bin/python skripti.py >> skripti_loki.log 2>&1 +``` +Tämä ajaa päivityksen joka aamu klo 04:00 ja tallentaa tulosteet `skripti_loki.log` -tiedostoon. diff --git a/levyraati.py b/levyraati.py new file mode 100644 index 0000000..f2a8db1 --- /dev/null +++ b/levyraati.py @@ -0,0 +1,141 @@ +import spotipy +from spotipy.oauth2 import SpotifyOAuth +import requests +import re +import os +import datetime +from dotenv import load_dotenv + +load_dotenv() + +CLIENT_ID = os.getenv("SPOTIPY_CLIENT_ID") +CLIENT_SECRET = os.getenv("SPOTIPY_CLIENT_SECRET") +REDIRECT_URI = os.getenv("SPOTIPY_REDIRECT_URI") +PLAYLIST_ID = os.getenv("PLAYLIST_ID") + +WIKI_API_URL = os.getenv("WIKI_API_URL") +WIKI_PAGE_TITLE = os.getenv("WIKI_PAGE_TITLE") +WIKI_USERNAME = os.getenv("WIKI_USERNAME") +WIKI_PASSWORD = os.getenv("WIKI_PASSWORD") + +def hae_wiki_sisalto(): + """Hakee MediaWiki-sivun raakatekstin APIn kautta.""" + + # Käytetään Session-oliota, jotta kirjautumiskeksit (cookies) pysyvät tallessa + session = requests.Session() + + # Vaihe 1: Haetaan kirjautumistoken (login token) + token_params = { + "action": "query", + "meta": "tokens", + "type": "login", + "format": "json" + } + + try: + response = session.get(url=WIKI_API_URL, params=token_params) + data = response.json() + login_token = data['query']['tokens']['logintoken'] + + # Vaihe 2: Kirjaudutaan sisään tunnuksilla ja tokenilla (POST-pyyntö) + login_params = { + "action": "login", + "lgname": WIKI_USERNAME, + "lgpassword": WIKI_PASSWORD, + "lgtoken": login_token, + "format": "json" + } + + login_response = session.post(url=WIKI_API_URL, data=login_params) + login_data = login_response.json() + + if login_data.get("login", {}).get("result") != "Success": + print(f"Wikin kirjautuminen epäonnistui: {login_data}") + return "" + + print("Kirjauduttu Wikiin onnistuneesti!") + + # Vaihe 3: Haetaan vihdoin itse sivun sisältö + page_params = { + "action": "query", + "prop": "revisions", + "rvprop": "content", + "rvslots": "main", + "titles": WIKI_PAGE_TITLE, + "format": "json" + } + + page_response = session.get(url=WIKI_API_URL, params=page_params) + page_data = page_response.json() + + pages = page_data["query"]["pages"] + page_id = list(pages.keys())[0] + + if page_id == "-1": + print("Virhe: Wiki-sivua ei löytynyt!") + return "" + + return pages[page_id]["revisions"][0]["slots"]["main"]["*"] + + except Exception as e: + print(f"Virhe Wiki-yhteydessä: {e}") + return "" + +def paivita_soittolista(): + # 1. Haetaan teksti Wikistä + nykyinen_viikko_int = datetime.date.today().isocalendar()[1] + nykyinen_viikko = f"{nykyinen_viikko_int:02d}" + print(f"Kuluva viikko on {nykyinen_viikko}") + wiki_teksti = hae_wiki_sisalto() + if not wiki_teksti: + return + + # 3. Eristetään kuluvan viikon osio tekstistä säännöllisellä lausekkeella (regex) + # Etsii esim. "== Viikko 10 ==" ja nappaa kaiken tekstin sen jälkeen + # aina seuraavaan "==" merkkiin asti tai sivun loppuun. + viikko_pattern = rf"==\s*Viikko {nykyinen_viikko}\s*==(.*?(?===|\Z))" + match = re.search(viikko_pattern, wiki_teksti, re.IGNORECASE | re.DOTALL) + + if not match: + print(f"\nWiki-sivulta ei löytynyt otsikkoa '== Viikko {nykyinen_viikko} =='. Soittolistaa ei päivitetty.") + return + + kuluvan_viikon_teksti = match.group(1) + + # 4. Poimitaan suorat Spotify-linkit koodista + track_urls = [] + + # Käydään wiki-teksti läpi rivi riviltä + for rivi in kuluvan_viikon_teksti.split('\n'): + # Etsitään vain rivit, joissa lukee if '' in rivi: + # Säännöllinen lauseke (regex), joka etsii http- tai https-alkuisen + # linkin heti avaavan hakasulkeen [ jälkeen + match = re.search(r'\[(https?://[^\s\]]+)', rivi) + + if match: + url = match.group(1) # Napataan pelkkä linkki + if "spotify.com" in url.lower(): + track_urls.append(url) + print(f"Löydettiin linkki: {url}") + else: + print(f"Ohitettiin muun palvelun linkki: {url}") + + # 5. Tarkistetaan löytyikö mitään + if not track_urls: + print("\nYhtään Spotify-linkkiä ei löytynyt Wiki-sivulta. Soittolistaa ei päivitetty.") + return + + # 6. Kirjaudutaan sisään + scope = "playlist-modify-private playlist-modify-public" + sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id=CLIENT_ID, + client_secret=CLIENT_SECRET, + redirect_uri=REDIRECT_URI, + scope=scope, open_browser=False)) + + # 7. Päivitetään soittolista + sp.playlist_replace_items(PLAYLIST_ID, track_urls[:100]) + print("Soittolista päivitetty onnistuneesti!") + +if __name__ == '__main__': + paivita_soittolista() +