refactor initial not fully recovered

This commit is contained in:
Шурупов Илья Викторович 2026-01-04 19:25:42 +03:00
parent 2824bd1a0e
commit 72af96eada
16 changed files with 925 additions and 267 deletions

139
src/spotify/ParseSpotify.py Normal file
View file

@ -0,0 +1,139 @@
from datetime import datetime
from typing import Any, Iterable
from pathlib import Path
import json
from src.Library import *
def get_artists_str(artists_pack):
track_artists = [artist['name'] for artist in artists_pack]
artists = " ".join(track_artists)
return artists
class Parser:
def __init__(self):
# state: Spotify track ID -> Track object
self.track_map: dict[str, Track] = {}
# -------------------------
# Low-level helpers
# -------------------------
@staticmethod
def _parse_spotify_datetime(value: str | None) -> datetime | None:
if not value:
return None
return datetime.fromisoformat(value.replace("Z", "+00:00"))
@staticmethod
def _parse_spotify_artist(a: dict[str, Any]) -> SpotifyArtist:
return SpotifyArtist(
id=a["id"],
name=a["name"],
uri=a["uri"],
url=a["external_urls"]["spotify"],
)
@staticmethod
def _parse_spotify_album(a: dict[str, Any]) -> SpotifyAlbum:
return SpotifyAlbum(
id=a["id"],
name=a["name"],
release_date=a.get("release_date"),
total_tracks=a.get("total_tracks", 0),
uri=a["uri"],
url=a["external_urls"]["spotify"],
)
def _parse_spotify_data(self, track_json: dict[str, Any]) -> SpotifyData:
return SpotifyData(
id=track_json["id"],
uri=track_json["uri"],
url=track_json["external_urls"]["spotify"],
href=track_json["href"],
popularity=track_json.get("popularity"),
is_playable=track_json.get("is_playable"),
is_local=track_json.get("is_local"),
external_ids=track_json.get("external_ids", {}),
artists=[self._parse_spotify_artist(a) for a in track_json.get("artists", [])],
album=self._parse_spotify_album(track_json["album"])
if track_json.get("album")
else None,
)
# -------------------------
# Track parsers
# -------------------------
def parse_track_item(self, item: dict[str, Any]) -> Track:
"""
Parses a single track item and returns Track object.
Reuses existing Track from track_map if already parsed.
"""
t = item["track"]
track_id = t["id"]
# return existing track if already parsed
if track_id in self.track_map:
return self.track_map[track_id]
track = Track(
title=t["name"],
artists=[a["name"] for a in t.get("artists", [])],
album=t["album"]["name"] if t.get("album") else None,
duration_ms=t.get("duration_ms"),
added_at=self._parse_spotify_datetime(item.get("added_at")),
spotify=self._parse_spotify_data(t),
id=self._parse_spotify_data(t).id
)
self.track_map[track_id] = track
return track
def parse_track_items(self, items: Iterable[dict[str, Any]]) -> list[Track]:
return [self.parse_track_item(i) for i in items if i.get("track")]
# -------------------------
# Playlist parsers
# -------------------------
def parse_playlist_content(self, playlist_name: str, items: list[dict[str, Any]]) -> Playlist:
tracks = self.parse_track_items(items)
return Playlist(name=playlist_name, tracks=tracks)
def parse_all_playlist_content(self, data: dict[str, list[dict[str, Any]]]) -> list[Playlist]:
playlists: list[Playlist] = []
for name, items in data.items():
playlists.append(self.parse_playlist_content(name, items))
return playlists
def parse_all_tracks(self, items: dict[str, list[dict[str, Any]]]) -> list[Track]:
return [self.parse_track_item(i) for i in items]
# -------------------------
# Library builder
# -------------------------
def build_library_from_playlist_content(
self, playlist_content_json: dict[str, list[dict[str, Any]]],
tracks_content_json: dict[str, list[dict[str, Any]]]
) -> Library:
library = Library()
self.parse_all_tracks(tracks_content_json)
library.tracklists.append(TrackList(tracks=list((self.track_map.values()))))
library.playlists = self.parse_all_playlist_content(playlist_content_json)
library.tracks.extend(self.track_map.values())
return library
# -------------------------
# Convenience method
# -------------------------
def parse(
self,
playlist_content_file: Path,
tracks_file: Path,
) -> Library:
playlist_content = json.loads(playlist_content_file.read_text())
tracks_content = json.loads(tracks_file.read_text())
return self.build_library_from_playlist_content(playlist_content, tracks_content)