2
0
Fork 0
tidal-scraper/tidal_scraper/state.py

145 lines
4.7 KiB
Python
Raw Permalink Normal View History

2023-07-18 14:25:14 +02:00
from tidal_scraper.helper import log_error
2023-06-29 16:28:31 +02:00
import json
from datetime import datetime
2023-07-18 14:25:14 +02:00
from tidalapi import session, user, playlist, media, album, artist, Quality
2023-06-29 16:28:31 +02:00
class State:
2023-07-12 17:45:04 +02:00
def __init__(
self,
conf: dict | None = None,
2023-07-18 14:25:14 +02:00
state_dir: str | None = None,
user_id: int | None = None,
quality: str | None = None,
2023-07-12 17:45:04 +02:00
errorfile: str | None = None,
):
if conf is None:
2023-07-18 14:25:14 +02:00
assert user_id is not None
assert quality is not None
assert state_dir is not None
2023-07-12 17:45:04 +02:00
assert errorfile is not None
else:
2023-07-18 14:25:14 +02:00
user_id = user_id or conf["user_id"]
quality = quality or conf["quality"]
state_dir = state_dir or conf["state_dir"]
2023-07-12 17:45:04 +02:00
errorfile = errorfile or conf["error_log"]
2023-06-29 16:28:31 +02:00
match quality:
case "master":
2023-07-18 14:25:14 +02:00
q = Quality.master
2023-06-29 16:28:31 +02:00
case "lossless":
2023-07-18 14:25:14 +02:00
q = Quality.lossless
2023-06-29 16:28:31 +02:00
case "high":
2023-07-18 14:25:14 +02:00
q = Quality.high
2023-06-29 16:28:31 +02:00
case "low":
2023-07-18 14:25:14 +02:00
q = Quality.low
2023-06-29 16:28:31 +02:00
case _:
2023-07-05 11:50:05 +02:00
raise Exception("Bad Quality String")
2023-07-18 14:25:14 +02:00
api_config = session.Config(quality=q)
self.conf = conf
2023-06-29 16:28:31 +02:00
self.user_id = user_id
2023-07-18 14:25:14 +02:00
self.session = session.Session(api_config)
2023-07-05 11:50:05 +02:00
self.favorites = user.Favorites(self.session, user_id)
2023-07-18 14:25:14 +02:00
self.errorfile = errorfile
self._state = {
"albums": {},
"artists": {},
"playlists": {},
"tracks": {},
}
2023-06-29 16:28:31 +02:00
2023-07-05 11:50:05 +02:00
def login(self, auth_file: str | None = None) -> None:
2023-06-29 16:28:31 +02:00
s = self.session
2023-07-18 14:25:14 +02:00
if auth_file is None:
assert self.conf is not None
auth_file = self.conf["state_dir"] + "auth.json"
2023-06-29 16:28:31 +02:00
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"]),
)
2023-06-29 23:57:07 +02:00
except (FileNotFoundError, IndexError, AssertionError):
2023-06-29 16:28:31 +02:00
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:
2023-07-18 14:25:14 +02:00
json.dump(data, fp=f, indent=4)
2023-06-29 16:28:31 +02:00
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 _:
2023-07-18 14:25:14 +02:00
raise Exception("Object of incorrect type received")
2023-06-29 16:28:31 +02:00
self._state[t][obj.id] = downloaded
2023-07-18 14:25:14 +02:00
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)
2023-07-05 11:50:05 +02:00
def write_dl_state(self, statefile: str) -> None:
with open(statefile, "w") as f:
2023-07-18 14:25:14 +02:00
json.dump(self._state, fp=f, indent=4)
2023-06-29 16:28:31 +02:00
2023-07-18 14:25:14 +02:00
def load_dl_state(self, state_file: str) -> None:
try:
with open(state_file, "r") as f:
self._state = json.load(f)
2023-06-29 23:57:07 +02:00
2023-07-18 14:25:14 +02:00
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",
)