diff --git a/levyraati.py b/levyraati.py index f2a8db1..75f10b8 100644 --- a/levyraati.py +++ b/levyraati.py @@ -1,141 +1,186 @@ +import os +import re +import datetime +import requests import spotipy from spotipy.oauth2 import SpotifyOAuth -import requests -import re -import os -import datetime from dotenv import load_dotenv -load_dotenv() +# --- KONFIGURAATIO --- -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" +def lataa_asetukset(): + """Lataa ympäristömuuttujat .env-tiedostosta.""" + load_dotenv() + return { + "SPOTIPY_CLIENT_ID": os.getenv("SPOTIPY_CLIENT_ID"), + "SPOTIPY_CLIENT_SECRET": os.getenv("SPOTIPY_CLIENT_SECRET"), + "SPOTIPY_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"), } - - 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, + +# --- WIKI-YHTEYS --- + +class WikiClient: + """Luokka MediaWiki-rajapinnan kanssa toimimiseen.""" + + def __init__(self, api_url, username, password): + self.api_url = api_url + self.username = username + self.password = password + self.session = requests.Session() + + def kirjaudu(self): + """Kirjautuu Wikiin ja palauttaa True jos onnistuu.""" + token_params = { + "action": "query", + "meta": "tokens", + "type": "login", "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 "" + try: + # Vaihe 1: Haetaan kirjautumistoken + response = self.session.get(url=self.api_url, params=token_params) + response.raise_for_status() + login_token = response.json()['query']['tokens']['logintoken'] - print("Kirjauduttu Wikiin onnistuneesti!") - - # Vaihe 3: Haetaan vihdoin itse sivun sisältö - page_params = { + # Vaihe 2: Kirjaudutaan sisään + login_params = { + "action": "login", + "lgname": self.username, + "lgpassword": self.password, + "lgtoken": login_token, + "format": "json" + } + + login_response = self.session.post(url=self.api_url, data=login_params) + login_response.raise_for_status() + + if login_response.json().get("login", {}).get("result") == "Success": + print("Kirjauduttu Wikiin onnistuneesti!") + return True + + print(f"Wikin kirjautuminen epäonnistui: {login_response.json()}") + return False + + except Exception as e: + print(f"Virhe Wiki-kirjautumisessa: {e}") + return False + + def hae_sivun_sisalto(self, otsikko): + """Hakee annetun sivun raakatekstin.""" + params = { "action": "query", "prop": "revisions", "rvprop": "content", "rvslots": "main", - "titles": WIKI_PAGE_TITLE, + "titles": otsikko, "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 "" + try: + response = self.session.get(url=self.api_url, params=params) + response.raise_for_status() + pages = response.json()["query"]["pages"] + page_id = list(pages.keys())[0] - return pages[page_id]["revisions"][0]["slots"]["main"]["*"] - - except Exception as e: - print(f"Virhe Wiki-yhteydessä: {e}") - return "" + if page_id == "-1": + print(f"Virhe: Wiki-sivua '{otsikko}' ei löytynyt!") + return "" + + return pages[page_id]["revisions"][0]["slots"]["main"]["*"] + except Exception as e: + print(f"Virhe sivun noudossa: {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() +# --- TEKSTINKÄSITTELY --- + +def erista_viikon_osio(teksti, viikko_numero): + """Eristää tietyn viikon osion koko sivun tekstistä (== Viikko X ==).""" + viikko_str = f"{viikko_numero:02d}" + pattern = rf"==\s*Viikko {viikko_str}\s*==(.*?(?===|\Z))" + match = re.search(pattern, teksti, re.IGNORECASE | re.DOTALL) + + if match: + return match.group(1).strip() + return None + +def poimi_spotify_linkit(teksti): + """Etsii tekstistä Spotify-kappaleiden linkit hakasulkeiden sisältä.""" + linkit = [] + for rivi in teksti.split('\n'): + # Etsitään [http...] muotoisia linkkejä + match = re.search(r'\[(https?://[^\s\]]+)', rivi) + if match: + url = match.group(1) + if "spotify.com" in url.lower(): + linkit.append(url) + print(f"Löydetty Spotify-linkki: {url}") + else: + print(f"Ohitettu muu linkki: {url}") + return linkit + +# --- SPOTIFY-PÄIVITYS --- + +def paivita_spotify_soittolista(linkit, asetukset): + """Päivittää Spotify-soittolistan annetuilla linkeillä.""" + if not linkit: + print("\nEi päivitettäviä linkkejä. Soittolistaa ei muutettu.") + return False + + scope = "playlist-modify-private playlist-modify-public" + try: + sp = spotipy.Spotify(auth_manager=SpotifyOAuth( + client_id=asetukset["SPOTIPY_CLIENT_ID"], + client_secret=asetukset["SPOTIPY_CLIENT_SECRET"], + redirect_uri=asetukset["SPOTIPY_REDIRECT_URI"], + scope=scope, + open_browser=False + )) + + # Spotify sallii enintään 100 kappaletta per kutsu + sp.playlist_replace_items(asetukset["PLAYLIST_ID"], linkit[:100]) + print(f"Soittolista päivitetty onnistuneesti ({len(linkit[:100])} kappaletta).") + return True + except Exception as e: + print(f"Virhe Spotifyn päivityksessä: {e}") + return False + +# --- PÄÄOHJELMA --- + +def aja_paivitys(): + """Suorittaa koko päivitysprosessin vaihe kerrallaan.""" + asetukset = lataa_asetukset() + nykyinen_viikko = datetime.date.today().isocalendar()[1] + print(f"--- Aloitetaan päivitys (Viikko {nykyinen_viikko:02d}) ---") + + # 1. Wiki-yhteys ja sisällön haku + wiki = WikiClient( + asetukset["WIKI_API_URL"], + asetukset["WIKI_USERNAME"], + asetukset["WIKI_PASSWORD"] + ) + + if not wiki.kirjaudu(): + return + + wiki_teksti = wiki.hae_sivun_sisalto(asetukset["WIKI_PAGE_TITLE"]) 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.") + # 2. Oikean viikon osion eristäminen + viikko_teksti = erista_viikon_osio(wiki_teksti, nykyinen_viikko) + if not viikko_teksti: + print(f"Huom: Viikon {nykyinen_viikko:02d} osiota ei löytynyt sivulta.") 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!") + # 3. Linkkien kerääminen ja Spotifyn päivittäminen + spotify_linkit = poimi_spotify_linkit(viikko_teksti) + paivita_spotify_soittolista(spotify_linkit, asetukset) if __name__ == '__main__': - paivita_soittolista() - + aja_paivitys()