rettelser

This commit is contained in:
2026-04-19 17:19:59 +02:00
parent c966d38f11
commit fb7622549c
7 changed files with 81 additions and 33 deletions

View File

@@ -252,9 +252,9 @@ def push(
return {
"status": "ok",
"songs_synced": len(song_id_map),
"playlists_synced": len(playlist_id_map),
"song_id_map": song_id_map,
"playlist_id_map": playlist_id_map,
"playlists_synced": len(playlist_id_map)
#"song_id_map": song_id_map,
#"playlist_id_map": playlist_id_map,
}

View File

@@ -304,11 +304,15 @@ MIGRATIONS: dict[int, list[str]] = {
12: [
# Tabel til at huske slettede playlister — til sync med serveren
"""CREATE TABLE IF NOT EXISTS deleted_playlists (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
deleted_at TEXT NOT NULL DEFAULT (datetime('now'))
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
deleted_at TEXT NOT NULL DEFAULT (datetime('now'))
)""",
],
13: [
# Tilføj api_project_id så serveren præcist ved hvilken playlist der skal slettes
"""ALTER TABLE deleted_playlists ADD COLUMN api_project_id TEXT""",
],
}
@@ -546,6 +550,24 @@ def create_playlist(name: str, description: str = "", tags: str = "") -> int:
return cur.lastrowid
def delete_playlist(playlist_id: int):
"""
Slet en playliste lokalt og registrér sletningen til næste sync.
Gemmer api_project_id så serveren præcist ved hvad der skal slettes.
"""
with get_db() as conn:
row = conn.execute(
"SELECT name, api_project_id FROM playlists WHERE id=?",
(playlist_id,)
).fetchone()
if row:
conn.execute(
"INSERT INTO deleted_playlists (name, api_project_id) VALUES (?,?)",
(row["name"], row["api_project_id"] or None)
)
conn.execute("DELETE FROM playlists WHERE id=?", (playlist_id,))
def create_linked_playlist(name: str, api_project_id: str,
permission: str = "view",
description: str = "", tags: str = "") -> int:

View File

@@ -110,19 +110,29 @@ class SyncManager:
try:
# 1. Push lokal data op — inkl. sletninger
payload = self._build_push_payload()
deleted = payload.get("deleted_playlists", [])
logger.info(f"Sync push — {len(payload['songs'])} sange, "
f"{len(payload['playlists'])} playlister, "
f"sletter {len(deleted)}: {deleted}")
push_result = self._post("/sync/push", payload)
self._save_playlist_ids(push_result.get("playlist_id_map", {}))
logger.info(f"Push svar: {push_result}")
# Ryd deleted_playlists nu de er sendt
if payload.get("deleted_playlists"):
# 2. Pull — sletninger er nu gennemført på serveren.
# VIGTIGT: ryd deleted_playlists EFTER pull, så _apply_pull
# stadig kan filtrere de slettede lister fra.
pull_result = self._get("/sync/pull")
pl_names = [p.get("name") for p in pull_result.get("my_playlists", [])]
logger.info(f"Pull modtog {len(pl_names)} playlister: {pl_names}")
self._apply_pull(pull_result)
# Ryd deleted_playlists nu pull er kørt og filtreringen er sket
if deleted:
conn = sqlite3.connect(self._db_path)
conn.execute("DELETE FROM deleted_playlists")
conn.commit()
conn.close()
# 2. Pull — nu er sletninger gennemført på serveren
pull_result = self._get("/sync/pull")
self._apply_pull(pull_result)
logger.info(f"deleted_playlists ryddet efter pull")
pl_count = len(pull_result.get("my_playlists", []))
logger.info(
@@ -240,11 +250,15 @@ class SyncManager:
"songs": pl_songs,
})
# Slettede playlister — skal fjernes fra serveren
# Slettede playlister — skal fjernes fra serveren.
# Serveren forventer en liste af strings (api_project_id).
# Playlister uden api_project_id har aldrig nået serveren — ignorer dem.
deleted = [
row["name"] for row in conn.execute(
"SELECT name FROM deleted_playlists"
row["api_project_id"]
for row in conn.execute(
"SELECT api_project_id FROM deleted_playlists"
).fetchall()
if row["api_project_id"]
]
conn.close()
@@ -282,14 +296,18 @@ class SyncManager:
d.get("stepsheet_url",""), existing["id"]))
# Importer/opdater egne playlister fra server — server er sandhed
# Hent navne på lokalt slettede playlister så vi ikke genskaber dem
# Hent både navne og server-IDs på lokalt slettede playlister
try:
deleted_names = set(
row["name"] for row in conn.execute(
"SELECT name FROM deleted_playlists"
).fetchall()
)
deleted_rows = conn.execute(
"SELECT name, api_project_id FROM deleted_playlists"
).fetchall()
deleted_server_ids = {
row["api_project_id"] for row in deleted_rows
if row["api_project_id"]
}
deleted_names = {row["name"] for row in deleted_rows}
except Exception:
deleted_server_ids = set()
deleted_names = set()
for pl in data.get("my_playlists", []):
@@ -299,7 +317,10 @@ class SyncManager:
continue
# Spring over hvis listen er markeret som slettet lokalt
if name in deleted_names:
# Tjek på server-ID (præcist) og navn (fallback)
if server_id in deleted_server_ids:
continue
if name in deleted_names and not server_id:
continue
existing = conn.execute(

View File

@@ -365,6 +365,7 @@ class MainWindow(QMainWindow):
self._playlist_panel.event_started.connect(self._on_event_started)
self._playlist_panel.next_song_ready.connect(self._on_next_song_ready)
self._playlist_panel.playlist_changed.connect(self._on_playlist_changed)
self._playlist_panel.sync_requested.connect(self._manual_sync)
# Debounce-timer til auto-sync — starter sync 5 sek efter sidst ændring
self._sync_debounce = QTimer(self)

View File

@@ -20,6 +20,7 @@ class PlaylistBrowserDialog(QDialog):
"""Kombineret gem/hent dialog til danselister."""
playlist_selected = pyqtSignal(int, str) # playlist_id, name
sync_requested = pyqtSignal() # bed main_window om at køre sync
def __init__(self, mode: str = "load", current_songs: list = None,
current_name: str = "", parent=None):
@@ -344,10 +345,11 @@ class PlaylistBrowserDialog(QDialog):
)
if reply == QMessageBox.StandardButton.Yes:
try:
from local.local_db import get_db
with get_db() as conn:
conn.execute("DELETE FROM playlists WHERE id=?", (pl["id"],))
from local.local_db import delete_playlist
delete_playlist(pl["id"])
self._load_data()
# Signal til main_window om at køre sync
self.sync_requested.emit()
except Exception as e:
QMessageBox.warning(self, "Fejl", f"Kunne ikke slette: {e}")

View File

@@ -178,13 +178,8 @@ class PlaylistManagerDialog(QDialog):
)
if reply == QMessageBox.StandardButton.Yes:
try:
from local.local_db import get_db
with get_db() as conn:
conn.execute(
"INSERT INTO deleted_playlists (name) "
"SELECT name FROM playlists WHERE id=?", (pl["id"],)
)
conn.execute("DELETE FROM playlists WHERE id=?", (pl["id"],))
from local.local_db import delete_playlist
delete_playlist(pl["id"])
self._load_saved_playlists()
except Exception as e:
self._load_status.setText(f"Fejl: {e}")

View File

@@ -39,6 +39,7 @@ class PlaylistPanel(QWidget):
playlist_changed = pyqtSignal()
event_started = pyqtSignal()
next_song_ready = pyqtSignal(dict) # udsendes når næste sang ændres — main_window indlæser den # udsendes af Start event — main_window indlæser første sang # udsendes ved enhver ændring → trigger autogem
sync_requested = pyqtSignal() # bed main_window om at køre sync (efter sletning)
STATUS_ICON = {"pending": " ", "playing": "", "played": "", "skipped": "", "next": ""}
STATUS_COLOR = {"pending": "#5a6070", "playing": "#e8a020", "played": "#2ecc71", "skipped": "#e74c3c", "next": "#3b8fd4"}
@@ -526,6 +527,7 @@ class PlaylistPanel(QWidget):
self._btn_save_current.setToolTip(f"Gem ændringer til '{name}'")
self._save_named_playlist_id(pl_id)
dialog.playlist_selected.connect(on_saved)
dialog.sync_requested.connect(self._request_sync)
dialog.exec()
def _save_current(self):
@@ -572,10 +574,15 @@ class PlaylistPanel(QWidget):
except Exception as e:
QMessageBox.warning(self, "Fejl", f"Kunne ikke gemme: {e}")
def _request_sync(self):
"""Bobl sync-anmodning op til main_window."""
self.sync_requested.emit()
def _load_dialog(self):
from ui.playlist_browser import PlaylistBrowserDialog
dialog = PlaylistBrowserDialog(mode="load", parent=self.window())
dialog.playlist_selected.connect(self._load_playlist_by_id)
dialog.sync_requested.connect(self._request_sync)
dialog.exec()
def _load_playlist_by_id(self, pl_id: int, pl_name: str):