144 lines
4 KiB
Python
144 lines
4 KiB
Python
import json
|
|
import yaml
|
|
import dataclasses
|
|
from dataclasses import dataclass
|
|
from collections import OrderedDict
|
|
from typing import Optional, Dict, List
|
|
|
|
|
|
def map_ditem(d):
|
|
if type(d) != dict:
|
|
return d
|
|
|
|
do = OrderedDict()
|
|
for k, v in d.items():
|
|
if type(v) == dict:
|
|
do[k] = map_ditem(v)
|
|
elif type(v) == list:
|
|
do[k] = [map_ditem(itm) for itm in v]
|
|
elif v is not None:
|
|
do[k] = v
|
|
return do
|
|
|
|
|
|
class ExtendedEncoder(json.JSONEncoder):
|
|
|
|
def default(self, o):
|
|
if dataclasses.is_dataclass(o):
|
|
return map_ditem(dataclasses.asdict(o))
|
|
return super().default(o)
|
|
|
|
|
|
@dataclass
|
|
class GenreMetadata:
|
|
name: str
|
|
description: Optional[str] = None
|
|
parent: Optional[str] = None
|
|
language: Optional[str] = None
|
|
country: Optional[str] = None
|
|
region: Optional[str] = None
|
|
localized_name: Optional[bool] = None
|
|
wikipedia_url: Optional[str] = None
|
|
wikidata_id: Optional[int] = None
|
|
rank: Optional[int] = None
|
|
playlists: Optional[Dict[str, str]] = None
|
|
alias: Optional[str] = None
|
|
deprecated: Optional[bool] = None
|
|
metagenre: Optional[bool] = None
|
|
|
|
|
|
# Packaged genre metadata
|
|
@dataclass
|
|
class GenreMetadataDB:
|
|
name: Optional[str] = None
|
|
parent: Optional[str] = None
|
|
language: Optional[str] = None
|
|
country: Optional[str] = None
|
|
region: Optional[str] = None
|
|
rank: Optional[int] = None
|
|
playlists: Optional[Dict[str, str]] = None
|
|
alias: Optional[str] = None
|
|
deprecated: Optional[bool] = None
|
|
metagenre: Optional[bool] = None
|
|
|
|
@classmethod
|
|
def conv(cls, md: GenreMetadata, name: Optional[str] = None):
|
|
if md.alias:
|
|
return cls(alias=md.alias)
|
|
return cls(name=name or md.name,
|
|
parent=md.parent,
|
|
language=md.language,
|
|
country=md.country,
|
|
region=md.region,
|
|
rank=md.rank,
|
|
playlists=md.playlists,
|
|
deprecated=md.deprecated,
|
|
metagenre=md.metagenre)
|
|
|
|
|
|
# Genre metadata from tree
|
|
@dataclass
|
|
class GenreMetadataTree:
|
|
id: str
|
|
name: Optional[str] = None
|
|
language: Optional[str] = None
|
|
country: Optional[str] = None
|
|
region: Optional[str] = None
|
|
rank: Optional[int] = None
|
|
playlists: Optional[Dict[str, str]] = None
|
|
metagenre: Optional[bool] = None
|
|
children: Optional[List['GenreMetadataTree']] = None
|
|
|
|
@classmethod
|
|
def conv(cls, genre_id: str, md: GenreMetadata):
|
|
if md.alias:
|
|
return cls(genre_id, alias=md.alias)
|
|
return cls(genre_id,
|
|
name=md.name,
|
|
language=md.language,
|
|
country=md.country,
|
|
region=md.region,
|
|
rank=md.rank,
|
|
playlists=md.playlists,
|
|
metagenre=md.metagenre)
|
|
|
|
def __lt__(self, other):
|
|
self.id.__lt__(other.id)
|
|
|
|
|
|
def load_genre_dict(d: dict) -> Dict[str, GenreMetadata]:
|
|
return {k: GenreMetadata(**v) for k, v in d.items()}
|
|
|
|
|
|
def store_pack_json(path, data, sort=False):
|
|
with open(path, "w") as f:
|
|
json.dump(data,
|
|
f,
|
|
sort_keys=sort,
|
|
ensure_ascii=False,
|
|
cls=ExtendedEncoder)
|
|
|
|
|
|
def store_genres_json(path, genre_data: Dict[str, GenreMetadata], indent=2):
|
|
with open(path, "w") as f:
|
|
json.dump(genre_data,
|
|
f,
|
|
sort_keys=True,
|
|
ensure_ascii=False,
|
|
indent=indent,
|
|
cls=ExtendedEncoder)
|
|
if indent:
|
|
f.write("\n")
|
|
|
|
|
|
def store_genres_yaml(path, genre_data: Dict[str, GenreMetadata]):
|
|
mapped_data = {
|
|
k: map_ditem(dataclasses.asdict(v))
|
|
for k, v in genre_data.items()
|
|
}
|
|
|
|
yaml.add_representer(
|
|
OrderedDict, lambda dumper, data: dumper.represent_mapping(
|
|
'tag:yaml.org,2002:map', data.items()))
|
|
with open(path, "w") as f:
|
|
yaml.dump(mapped_data, f, sort_keys=True, allow_unicode=True)
|