This commit is contained in:
2026-04-14 15:59:02 +02:00
parent d4356e7337
commit 287477753e
4 changed files with 85 additions and 21 deletions

View File

@@ -30,6 +30,7 @@ class SongData(BaseModel):
bpm: int = 0
duration_sec: int = 0
file_format: str = ""
mbid: str = ""
class DanceData(BaseModel):
name: str
@@ -97,22 +98,30 @@ def push(
for s in payload.songs:
if not s.title:
continue
# Match globalt på titel+artist — samme sang deles på tværs af brugere
existing = db.query(Song).filter(
Song.title == s.title,
Song.artist == s.artist,
).first()
# Match 1: MBID — sikrest
existing = None
if s.mbid:
existing = db.query(Song).filter_by(mbid=s.mbid).first()
# Match 2: titel+artist globalt
if not existing:
existing = db.query(Song).filter(
Song.title == s.title,
Song.artist == s.artist,
).first()
if existing:
song_id_map[s.local_id] = existing.id
# Opdater BPM hvis det mangler
# Opdater BPM og MBID hvis de mangler
if s.bpm and not existing.bpm:
existing.bpm = s.bpm
if s.mbid and not existing.mbid:
existing.mbid = s.mbid
else:
song = Song(
owner_id=me.id,
title=s.title, artist=s.artist, album=s.album,
bpm=s.bpm, duration_sec=s.duration_sec,
file_format=s.file_format,
mbid=s.mbid or None,
)
db.add(song)
db.flush()

View File

@@ -89,10 +89,25 @@ def scan_library(library_id: int, library_path: str, db_path: str,
tags = read_tags(fp)
extra = json.dumps(tags.get("extra_tags", {}), ensure_ascii=False)
# Match 0: MBID-match — sikrest mulige match
existing = None
mbid_from_file = tags.get("mbid", "")
if mbid_from_file:
existing = conn.execute(
"SELECT id, bpm FROM songs WHERE mbid=? LIMIT 1",
(mbid_from_file,)
).fetchone()
if existing:
conn.execute(
"UPDATE songs SET local_path=? WHERE id=?",
(path_str, existing["id"])
)
# Match 1: præcis sti-match
existing = conn.execute(
"SELECT id, bpm FROM songs WHERE local_path=?", (path_str,)
).fetchone()
if not existing:
existing = conn.execute(
"SELECT id, bpm FROM songs WHERE local_path=?", (path_str,)
).fetchone()
# Match 2: titel+artist match — fil er flyttet eller var missing
if not existing:
@@ -131,28 +146,31 @@ def scan_library(library_id: int, library_path: str, db_path: str,
bpm = tags.get("bpm", 0)
if not overwrite_bpm and existing["bpm"] and existing["bpm"] > 0:
bpm = existing["bpm"] # behold eksisterende BPM
mbid = tags.get("mbid", "")
conn.execute("""
UPDATE songs SET
library_id=?, title=?, artist=?, album=?,
bpm=?, duration_sec=?, file_format=?,
file_modified_at=?, file_missing=0, extra_tags=?
file_modified_at=?, file_missing=0, extra_tags=?,
mbid=CASE WHEN ? != '' THEN ? ELSE mbid END
WHERE id=?
""", (library_id, tags.get("title",""), tags.get("artist",""),
tags.get("album",""), bpm, tags.get("duration_sec",0),
tags.get("file_format",""), mtime, extra, existing["id"]))
tags.get("file_format",""), mtime, extra,
mbid, mbid, existing["id"]))
song_id = existing["id"]
else:
song_id = str(uuid.uuid4())
conn.execute("""
INSERT OR IGNORE INTO songs
(id, library_id, local_path, title, artist, album,
bpm, duration_sec, file_format, file_modified_at, extra_tags)
VALUES (?,?,?,?,?,?,?,?,?,?,?)
bpm, duration_sec, file_format, file_modified_at, extra_tags, mbid)
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)
""", (song_id, library_id, path_str,
tags.get("title",""), tags.get("artist",""),
tags.get("album",""), tags.get("bpm",0),
tags.get("duration_sec",0), tags.get("file_format",""),
mtime, extra))
mtime, extra, tags.get("mbid","")))
# Importer dans-tags fra filen hvis de ikke allerede er i DB
file_dances = tags.get("dances", [])

View File

@@ -132,7 +132,7 @@ class SyncManager:
# Sange
songs = []
for row in conn.execute(
"SELECT id, title, artist, album, bpm, duration_sec, file_format "
"SELECT id, title, artist, album, bpm, duration_sec, file_format, mbid "
"FROM songs WHERE file_missing=0"
).fetchall():
songs.append({
@@ -143,6 +143,7 @@ class SyncManager:
"bpm": row["bpm"] or 0,
"duration_sec": row["duration_sec"] or 0,
"file_format": row["file_format"] or "",
"mbid": row["mbid"] or "",
})
# Danse

View File

@@ -175,6 +175,29 @@ def _read_mp3(audio, result: dict):
result["dances"] = [dances[k] for k in sorted(dances.keys())]
result["extra_tags"] = extra
# MusicBrainz Recording ID (MBID)
mbid = None
# Metode 1: UFID frame
for key, frame in tags.items():
if key.startswith("UFID") and "musicbrainz.org" in key.lower():
try:
mbid = frame.data.decode("utf-8", errors="replace").strip()
except Exception:
pass
break
# Metode 2: TXXX:MusicBrainz Recording Id
if not mbid:
for key, frame in tags.items():
if key.lower() in ("txxx:musicbrainz recording id",
"txxx:musicbrainz_trackid"):
try:
mbid = str(frame.text[0]).strip()
except Exception:
pass
break
if mbid:
result["mbid"] = mbid
def _read_vorbis(audio, result: dict):
"""FLAC og OGG/Opus bruger begge Vorbis Comments."""
@@ -212,9 +235,14 @@ def _read_vorbis(audio, result: dict):
except Exception:
pass
result["extra_tags"] = extra
def _read_m4a(audio, result: dict):
# MBID
for key in ("musicbrainz_trackid", "musicbrainz recording id"):
if key in tags:
try:
result["mbid"] = str(tags[key][0]).strip()
except Exception:
pass
break
tags = audio.tags
if not tags:
return
@@ -256,9 +284,17 @@ def _read_m4a(audio, result: dict):
except Exception:
pass
result["extra_tags"] = extra
def _read_generic(audio, result: dict):
# MBID — gemmes som freeform atom ----:com.apple.iTunes:MusicBrainz Recording Id
for key in tags:
if "musicbrainz" in key.lower() and "recording" in key.lower():
try:
val = tags[key][0]
if isinstance(val, (bytes, MP4FreeForm)):
val = val.decode("utf-8", errors="replace")
result["mbid"] = str(val).strip()
except Exception:
pass
break
try:
easy = MutagenFile(result["local_path"], easy=True)
if easy and easy.tags: