tmp
This commit is contained in:
parent
99cd872227
commit
b6c59e81ad
5 changed files with 226 additions and 104 deletions
13
Flow.py
13
Flow.py
|
|
@ -13,6 +13,8 @@ from src.backends.spotify.ParseSpotify import Parser
|
||||||
|
|
||||||
from src.backends.local.LocalLibraryIndexer import update_local_library
|
from src.backends.local.LocalLibraryIndexer import update_local_library
|
||||||
from src.backends.local.ParseLocal import LocalParser
|
from src.backends.local.ParseLocal import LocalParser
|
||||||
|
from src.backends.itunes.ParseItunesXml import ParseItunesXml
|
||||||
|
|
||||||
|
|
||||||
from src.mappers.RemoteLibraryResolver import resolve_remote_tracks
|
from src.mappers.RemoteLibraryResolver import resolve_remote_tracks
|
||||||
from src.StatGenerator import log_stats
|
from src.StatGenerator import log_stats
|
||||||
|
|
@ -43,6 +45,17 @@ class Flow:
|
||||||
|
|
||||||
LibrarySaver(library_parsed).save(work_dir / "spotify_library.json")
|
LibrarySaver(library_parsed).save(work_dir / "spotify_library.json")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def parse_itunes_library():
|
||||||
|
parser = ParseItunesXml()
|
||||||
|
|
||||||
|
library_parsed = parser.parse(
|
||||||
|
work_dir / "itunes" / "Library.xml",
|
||||||
|
work_dir / "itunes" / "Library.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
LibrarySaver(library_parsed).save(work_dir / "itunes_library.json")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_local_library():
|
def parse_local_library():
|
||||||
parser = LocalParser()
|
parser = LocalParser()
|
||||||
|
|
|
||||||
7
main.py
7
main.py
|
|
@ -1,7 +1,6 @@
|
||||||
|
|
||||||
|
|
||||||
import Flow
|
import Flow
|
||||||
|
|
||||||
|
|
||||||
def test1():
|
def test1():
|
||||||
flow = Flow.Flow()
|
flow = Flow.Flow()
|
||||||
|
|
||||||
|
|
@ -14,13 +13,15 @@ def test1():
|
||||||
|
|
||||||
# flow.fetch_and_save_lyrics()
|
# flow.fetch_and_save_lyrics()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
flow = Flow.Flow()
|
flow = Flow.Flow()
|
||||||
|
|
||||||
# flow.fetch_spotify()
|
# flow.fetch_spotify()
|
||||||
flow.fetch_local()
|
# flow.fetch_local()
|
||||||
|
|
||||||
flow.parse_spotify_library()
|
flow.parse_spotify_library()
|
||||||
|
flow.parse_itunes_library()
|
||||||
flow.parse_local_library()
|
flow.parse_local_library()
|
||||||
|
|
||||||
flow.load_libraries()
|
flow.load_libraries()
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,23 @@ from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional, Any
|
from typing import List, Optional, Any
|
||||||
|
|
||||||
|
class Entity:
|
||||||
|
def __init__(self):
|
||||||
|
self.id: str = None
|
||||||
|
self.mbid : str = None
|
||||||
|
|
||||||
|
self.title: str = None
|
||||||
|
|
||||||
|
self.fav: bool = False
|
||||||
|
self.rating: int = 0
|
||||||
|
self.play_count : int = 0
|
||||||
|
self.date_added : datetime = None
|
||||||
|
self.date_released : datetime = None
|
||||||
|
self.date_last_play : datetime = None
|
||||||
|
self.auto_score: float = 0
|
||||||
|
|
||||||
|
self.local_id: str = None
|
||||||
|
self.resolved_percentage : float = 0
|
||||||
|
|
||||||
class Artist:
|
class Artist:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
import xmltodict
|
|
||||||
import json
|
|
||||||
|
|
||||||
|
|
||||||
def flatten_dict(d):
|
|
||||||
|
|
||||||
out = []
|
|
||||||
for song in d:
|
|
||||||
newSong = {}
|
|
||||||
|
|
||||||
strCount = 0
|
|
||||||
intCount = 0
|
|
||||||
dateCount = 0
|
|
||||||
|
|
||||||
def getStr(id):
|
|
||||||
nonlocal strCount
|
|
||||||
if id not in song["key"]:
|
|
||||||
return "undef"
|
|
||||||
if len(song["string"]) <= strCount:
|
|
||||||
return "error"
|
|
||||||
strCount += 1
|
|
||||||
return song["string"][strCount - 1]
|
|
||||||
|
|
||||||
def getInt(id):
|
|
||||||
nonlocal intCount
|
|
||||||
if id not in song["key"]:
|
|
||||||
return -1
|
|
||||||
if len(song["integer"]) <= intCount:
|
|
||||||
return -1
|
|
||||||
intCount += 1
|
|
||||||
return song["integer"][intCount - 1]
|
|
||||||
|
|
||||||
def getDate(id):
|
|
||||||
nonlocal dateCount
|
|
||||||
if id not in song["key"]:
|
|
||||||
return "undef"
|
|
||||||
if len(song["date"]) <= dateCount:
|
|
||||||
return "error"
|
|
||||||
dateCount += 1
|
|
||||||
return song["date"][dateCount - 1]
|
|
||||||
|
|
||||||
newSong["Track ID"] = getInt("Track ID")
|
|
||||||
newSong["Name"] = getStr("Name")
|
|
||||||
newSong["Artist"] = getStr("Artist")
|
|
||||||
newSong["Album Artist"] = getStr("Album Artist")
|
|
||||||
newSong["Composer"] = getStr("Composer")
|
|
||||||
newSong["Album"] = getStr("Album")
|
|
||||||
newSong["Genre"] = getStr("Genre")
|
|
||||||
newSong["Kind"] = getStr("Kind")
|
|
||||||
newSong["Size"] = getInt("Size")
|
|
||||||
newSong["Total Time"] = getInt("Total Time")
|
|
||||||
newSong["Disc Number"] = getInt("Disc Number")
|
|
||||||
newSong["Disc Count"] = getInt("Disc Count")
|
|
||||||
newSong["Track Number"] = getInt("Track Number")
|
|
||||||
newSong["Track Count"] = getInt("Track Count")
|
|
||||||
newSong["Year"] = getInt("Year")
|
|
||||||
newSong["Date Modified"] = getDate("Date Modified")
|
|
||||||
newSong["Date Added"] = getDate("Date Added")
|
|
||||||
newSong["Bit Rate"] = getInt("Bit Rate")
|
|
||||||
newSong["Sample Rate"] = getInt("Sample Rate")
|
|
||||||
newSong["Play Count"] = getInt("Play Count")
|
|
||||||
newSong["Play Date"] = getInt("Play Date")
|
|
||||||
newSong["Play Date UTC"] = getDate("Play Date UTC")
|
|
||||||
newSong["Skip Count"] = getInt("Skip Count")
|
|
||||||
newSong["Skip Date"] = getDate("Skip Date")
|
|
||||||
newSong["Release Date"] = getDate("Release Date")
|
|
||||||
newSong["Album Rating"] = getInt("Album Rating")
|
|
||||||
newSong["Album Rating Computed"] = "Album Rating Computed" in song["key"]
|
|
||||||
newSong["Loved"] = "Loved" in song["key"]
|
|
||||||
newSong["Album Loved"] = "Album Loved" in song["key"]
|
|
||||||
newSong["Explicit"] = "Explicit" in song["key"]
|
|
||||||
newSong["Compilation"] = "Compilation" in song["key"]
|
|
||||||
newSong["Artwork Count"] = getInt("Artwork Count")
|
|
||||||
newSong["Sort Album"] = getStr("Sort Album")
|
|
||||||
newSong["Sort Artist"] = getStr("Sort Artist")
|
|
||||||
newSong["Sort Name"] = getStr("Sort Name")
|
|
||||||
newSong["Persistent ID"] = getStr("Persistent ID")
|
|
||||||
newSong["Track Type"] = getStr("Track Type")
|
|
||||||
|
|
||||||
out.append(newSong)
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
def convert(filename, out_path):
|
|
||||||
with open(filename, 'r', encoding='utf-8') as xml_file:
|
|
||||||
data_dict = xmltodict.parse(xml_file.read())
|
|
||||||
|
|
||||||
# Extract the "Tracks" dictionary to be flattened
|
|
||||||
tracks_dict = data_dict['plist']['dict']['dict']
|
|
||||||
|
|
||||||
flat_tracks_dict = flatten_dict(tracks_dict["dict"])
|
|
||||||
|
|
||||||
json_data = json.dumps(flat_tracks_dict, indent=2)
|
|
||||||
|
|
||||||
with open(out_path, 'w', encoding='utf-8') as json_file:
|
|
||||||
json_file.write(json_data)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
convert('./prefetched/ItunesLibrary.xml', './prefetched/ItunesLibrary.json')
|
|
||||||
192
src/backends/itunes/ParseItunesXml.py
Executable file
192
src/backends/itunes/ParseItunesXml.py
Executable file
|
|
@ -0,0 +1,192 @@
|
||||||
|
import xmltodict
|
||||||
|
import json
|
||||||
|
import xmltodict
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
|
||||||
|
from src.Library import *
|
||||||
|
|
||||||
|
class XmlToJson:
|
||||||
|
def flatten_dict(self, d):
|
||||||
|
|
||||||
|
out = []
|
||||||
|
for song in d:
|
||||||
|
newSong = {}
|
||||||
|
|
||||||
|
strCount = 0
|
||||||
|
intCount = 0
|
||||||
|
dateCount = 0
|
||||||
|
|
||||||
|
def getStr(id):
|
||||||
|
nonlocal strCount
|
||||||
|
if id not in song["key"]:
|
||||||
|
return "undef"
|
||||||
|
if len(song["string"]) <= strCount:
|
||||||
|
return "error"
|
||||||
|
strCount += 1
|
||||||
|
return song["string"][strCount - 1]
|
||||||
|
|
||||||
|
def getInt(id):
|
||||||
|
nonlocal intCount
|
||||||
|
if id not in song["key"]:
|
||||||
|
return -1
|
||||||
|
if len(song["integer"]) <= intCount:
|
||||||
|
return -1
|
||||||
|
intCount += 1
|
||||||
|
return song["integer"][intCount - 1]
|
||||||
|
|
||||||
|
def getDate(id):
|
||||||
|
nonlocal dateCount
|
||||||
|
if id not in song["key"]:
|
||||||
|
return "undef"
|
||||||
|
if len(song["date"]) <= dateCount:
|
||||||
|
return "error"
|
||||||
|
dateCount += 1
|
||||||
|
return song["date"][dateCount - 1]
|
||||||
|
|
||||||
|
newSong["Track ID"] = getInt("Track ID")
|
||||||
|
newSong["Name"] = getStr("Name")
|
||||||
|
newSong["Artist"] = getStr("Artist")
|
||||||
|
newSong["Album Artist"] = getStr("Album Artist")
|
||||||
|
newSong["Composer"] = getStr("Composer")
|
||||||
|
newSong["Album"] = getStr("Album")
|
||||||
|
newSong["Genre"] = getStr("Genre")
|
||||||
|
newSong["Kind"] = getStr("Kind")
|
||||||
|
newSong["Size"] = getInt("Size")
|
||||||
|
newSong["Total Time"] = getInt("Total Time")
|
||||||
|
newSong["Disc Number"] = getInt("Disc Number")
|
||||||
|
newSong["Disc Count"] = getInt("Disc Count")
|
||||||
|
newSong["Track Number"] = getInt("Track Number")
|
||||||
|
newSong["Track Count"] = getInt("Track Count")
|
||||||
|
newSong["Year"] = getInt("Year")
|
||||||
|
newSong["Date Modified"] = getDate("Date Modified")
|
||||||
|
newSong["Date Added"] = getDate("Date Added")
|
||||||
|
newSong["Bit Rate"] = getInt("Bit Rate")
|
||||||
|
newSong["Sample Rate"] = getInt("Sample Rate")
|
||||||
|
newSong["Play Count"] = getInt("Play Count")
|
||||||
|
newSong["Play Date"] = getInt("Play Date")
|
||||||
|
newSong["Play Date UTC"] = getDate("Play Date UTC")
|
||||||
|
newSong["Skip Count"] = getInt("Skip Count")
|
||||||
|
newSong["Skip Date"] = getDate("Skip Date")
|
||||||
|
newSong["Release Date"] = getDate("Release Date")
|
||||||
|
newSong["Album Rating"] = getInt("Album Rating")
|
||||||
|
newSong["Album Rating Computed"] = "Album Rating Computed" in song["key"]
|
||||||
|
newSong["Loved"] = "Loved" in song["key"]
|
||||||
|
newSong["Album Loved"] = "Album Loved" in song["key"]
|
||||||
|
newSong["Explicit"] = "Explicit" in song["key"]
|
||||||
|
newSong["Compilation"] = "Compilation" in song["key"]
|
||||||
|
newSong["Artwork Count"] = getInt("Artwork Count")
|
||||||
|
newSong["Sort Album"] = getStr("Sort Album")
|
||||||
|
newSong["Sort Artist"] = getStr("Sort Artist")
|
||||||
|
newSong["Sort Name"] = getStr("Sort Name")
|
||||||
|
newSong["Persistent ID"] = getStr("Persistent ID")
|
||||||
|
newSong["Track Type"] = getStr("Track Type")
|
||||||
|
|
||||||
|
out.append(newSong)
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def convert(self, filename, out_path):
|
||||||
|
with open(filename, 'r', encoding='utf-8') as xml_file:
|
||||||
|
data_dict = xmltodict.parse(xml_file.read())
|
||||||
|
|
||||||
|
# Extract the "Tracks" dictionary to be flattened
|
||||||
|
tracks_dict = data_dict['plist']['dict']['dict']
|
||||||
|
|
||||||
|
flat_tracks_dict = self.flatten_dict(tracks_dict["dict"])
|
||||||
|
|
||||||
|
json_data = json.dumps(flat_tracks_dict, indent=2)
|
||||||
|
|
||||||
|
with open(out_path, 'w', encoding='utf-8') as json_file:
|
||||||
|
json_file.write(json_data)
|
||||||
|
|
||||||
|
|
||||||
|
class ParseItunesXml:
|
||||||
|
def __init__(self):
|
||||||
|
self.track_map: dict[str, Track] = {}
|
||||||
|
self.artist_map: dict[str, Artist] = {}
|
||||||
|
self.album_map: dict[str, Album] = {}
|
||||||
|
self.library = Library()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_artists(self, data: list) -> List[Artist]:
|
||||||
|
if not data:
|
||||||
|
raise "No artist found"
|
||||||
|
|
||||||
|
artists = []
|
||||||
|
|
||||||
|
for artist_data in data:
|
||||||
|
name = artist_data
|
||||||
|
artist_id = name
|
||||||
|
|
||||||
|
if artist_id in self.artist_map:
|
||||||
|
artists.append(self.artist_map[artist_id])
|
||||||
|
continue
|
||||||
|
|
||||||
|
artist = Artist()
|
||||||
|
artist.id = artist_id
|
||||||
|
artist.title = name
|
||||||
|
|
||||||
|
self.artist_map[artist_id] = artist
|
||||||
|
self.library.artists.append(artist)
|
||||||
|
|
||||||
|
artists.append(artist)
|
||||||
|
|
||||||
|
return artists
|
||||||
|
|
||||||
|
def parse_album(self, data: str) -> Album:
|
||||||
|
if not data:
|
||||||
|
raise "album is none"
|
||||||
|
|
||||||
|
name = data
|
||||||
|
album_id = name
|
||||||
|
|
||||||
|
if album_id in self.album_map:
|
||||||
|
return self.album_map[album_id]
|
||||||
|
|
||||||
|
album = Album()
|
||||||
|
album.id = album_id
|
||||||
|
album.title = name
|
||||||
|
|
||||||
|
self.album_map[album_id] = album
|
||||||
|
self.library.albums.append(album)
|
||||||
|
|
||||||
|
return album
|
||||||
|
|
||||||
|
|
||||||
|
def parse(self, tracks_file_xml: Path, tracks_file_json: Path) -> Library:
|
||||||
|
XmlToJson().convert(tracks_file_xml, tracks_file_json)
|
||||||
|
|
||||||
|
tracks_content = json.loads(tracks_file_json.read_text())
|
||||||
|
|
||||||
|
for data in tracks_content:
|
||||||
|
track = Track()
|
||||||
|
|
||||||
|
track.id = data.get("Track ID")
|
||||||
|
track.title = data.get("Name")
|
||||||
|
track.artists = self.parse_artists([data.get("Artist")])
|
||||||
|
track.album = self.parse_album(data.get("Album"))
|
||||||
|
|
||||||
|
if not track.album or not len(track.artists) or not track.title:
|
||||||
|
raise "error parsing"
|
||||||
|
|
||||||
|
self.track_map[track.id] = track
|
||||||
|
self.library.tracks.append(track)
|
||||||
|
|
||||||
|
for track_id, track in self.track_map.items():
|
||||||
|
for artist in track.artists:
|
||||||
|
if artist not in track.album.artists:
|
||||||
|
track.album.artists.append(artist)
|
||||||
|
|
||||||
|
self.library.update_cache()
|
||||||
|
|
||||||
|
self.library.name = "Itunes"
|
||||||
|
|
||||||
|
return self.library
|
||||||
|
|
||||||
|
|
||||||
|
def parse_track_item(self, id, track: dict[str, Any]) -> Track:
|
||||||
|
|
||||||
|
return track
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue