from tidal_scraper.helper import log_error import json from datetime import datetime from tidalapi import session, user, playlist, media, album, artist, Quality class State: def __init__( self, conf: dict | None = None, state_dir: str | None = None, user_id: int | None = None, quality: str | None = None, errorfile: str | None = None, ): if conf is None: assert user_id is not None assert quality is not None assert state_dir is not None assert errorfile is not None else: user_id = user_id or conf["user_id"] quality = quality or conf["quality"] state_dir = state_dir or conf["state_dir"] errorfile = errorfile or conf["error_log"] match quality: case "master": q = Quality.master case "lossless": q = Quality.lossless case "high": q = Quality.high case "low": q = Quality.low case _: raise Exception("Bad Quality String") api_config = session.Config(quality=q) self.conf = conf self.user_id = user_id self.session = session.Session(api_config) self.favorites = user.Favorites(self.session, user_id) self.errorfile = errorfile self._state = { "albums": {}, "artists": {}, "playlists": {}, "tracks": {}, } def login(self, auth_file: str | None = None) -> None: s = self.session if auth_file is None: assert self.conf is not None auth_file = self.conf["state_dir"] + "auth.json" try: assert auth_file with open(auth_file, "r") as f: a = json.load(f) s.load_oauth_session( a["token_type"], a["access_token"], a["refresh_token"], datetime.fromtimestamp(a["expiry_time"]), ) except (FileNotFoundError, IndexError, AssertionError): s.login_oauth_simple() if ( s.token_type and s.access_token and s.refresh_token and s.expiry_time and auth_file ): data = { "token_type": s.token_type, "access_token": s.access_token, "refresh_token": s.refresh_token, "expiry_time": s.expiry_time.timestamp(), } with open(auth_file, "w") as f: json.dump(data, fp=f, indent=4) assert self.session.check_login() def set_dl_state( self, obj: playlist.Playlist | media.Track | album.Album | artist.Artist, downloaded: bool, ) -> None: match type(obj): case album.Album: t = "albums" case artist.Artist: t = "artists" case playlist.Playlist: t = "playlists" case media.Track: t = "tracks" case _: raise Exception("Object of incorrect type received") self._state[t][obj.id] = downloaded def state_downloaded( self, obj: playlist.Playlist | media.Track | album.Album | artist.Artist ) -> bool: match type(obj): case album.Album: t = "albums" case artist.Artist: t = "artists" case playlist.Playlist: t = "playlists" case media.Track: t = "tracks" case _: raise Exception("Object of incorrect type received") return self._state[t].get(str(obj.id), False) def write_dl_state(self, statefile: str) -> None: with open(statefile, "w") as f: json.dump(self._state, fp=f, indent=4) def load_dl_state(self, state_file: str) -> None: try: with open(state_file, "r") as f: self._state = json.load(f) for t in self._state.values(): for k, v in t.items(): assert isinstance(k, (str, type(None))) assert isinstance(v, (bool, type(None))) except (FileNotFoundError, IndexError, KeyError): log_error( self.errorfile or "error.log", f"Could not find state file at {state_file}", ) except (json.decoder.JSONDecodeError, AssertionError): log_error( self.errorfile or "error.log", f"Statefile at {state_file} is malformed", )