96 lines
2 KiB
Python
96 lines
2 KiB
Python
'''
|
|
Last.fm scrobbles
|
|
'''
|
|
|
|
from dataclasses import dataclass
|
|
|
|
from my.config import lastfm as user_config
|
|
from my.core import Json, Paths, get_files, make_logger
|
|
|
|
logger = make_logger(__name__)
|
|
|
|
|
|
@dataclass
|
|
class lastfm(user_config):
|
|
"""
|
|
Uses [[https://github.com/karlicoss/lastfm-backup][lastfm-backup]] outputs
|
|
"""
|
|
export_path: Paths
|
|
|
|
|
|
from my.core.cfg import make_config
|
|
|
|
config = make_config(lastfm)
|
|
|
|
|
|
import json
|
|
from collections.abc import Iterable, Sequence
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
from typing import NamedTuple
|
|
|
|
from my.core.cachew import mcachew
|
|
|
|
|
|
def inputs() -> Sequence[Path]:
|
|
return get_files(config.export_path)
|
|
|
|
|
|
# TODO memoised properties?
|
|
# TODO lazy mode and eager mode?
|
|
# lazy is a bit nicer in terms of more flexibility and less processing?
|
|
# eager is a bit more explicit for error handling
|
|
|
|
|
|
class Scrobble(NamedTuple):
|
|
raw: Json
|
|
|
|
# TODO mm, no timezone? hopefully it's UTC
|
|
@property
|
|
def dt(self) -> datetime:
|
|
ts = int(self.raw['date'])
|
|
return datetime.fromtimestamp(ts, tz=timezone.utc)
|
|
|
|
@property
|
|
def artist(self) -> str:
|
|
return self.raw['artist']
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return self.raw['name']
|
|
|
|
@property
|
|
def track(self) -> str:
|
|
return f'{self.artist} — {self.name}'
|
|
|
|
|
|
# TODO __repr__, __str__
|
|
# TODO could also be nice to make generic? maybe even depending on eagerness
|
|
|
|
|
|
@mcachew(depends_on=inputs)
|
|
def scrobbles() -> Iterable[Scrobble]:
|
|
last = max(inputs())
|
|
logger.info(f'loading data from {last}')
|
|
j = json.loads(last.read_text())
|
|
|
|
for raw in reversed(j):
|
|
yield Scrobble(raw=raw)
|
|
|
|
|
|
from my.core import Stats, stat
|
|
|
|
|
|
def stats() -> Stats:
|
|
return stat(scrobbles)
|
|
|
|
|
|
def fill_influxdb() -> None:
|
|
from my.core import influxdb
|
|
|
|
# todo needs to be more automatic
|
|
sd = ({
|
|
'dt': x.dt,
|
|
'track': x.track,
|
|
} for x in scrobbles())
|
|
influxdb.fill(sd, measurement=__name__)
|