rettelser
This commit is contained in:
@@ -252,9 +252,9 @@ def push(
|
|||||||
return {
|
return {
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"songs_synced": len(song_id_map),
|
"songs_synced": len(song_id_map),
|
||||||
"playlists_synced": len(playlist_id_map),
|
"playlists_synced": len(playlist_id_map)
|
||||||
"song_id_map": song_id_map,
|
#"song_id_map": song_id_map,
|
||||||
"playlist_id_map": playlist_id_map,
|
#"playlist_id_map": playlist_id_map,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -304,11 +304,15 @@ MIGRATIONS: dict[int, list[str]] = {
|
|||||||
12: [
|
12: [
|
||||||
# Tabel til at huske slettede playlister — til sync med serveren
|
# Tabel til at huske slettede playlister — til sync med serveren
|
||||||
"""CREATE TABLE IF NOT EXISTS deleted_playlists (
|
"""CREATE TABLE IF NOT EXISTS deleted_playlists (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
deleted_at TEXT NOT NULL DEFAULT (datetime('now'))
|
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
|
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,
|
def create_linked_playlist(name: str, api_project_id: str,
|
||||||
permission: str = "view",
|
permission: str = "view",
|
||||||
description: str = "", tags: str = "") -> int:
|
description: str = "", tags: str = "") -> int:
|
||||||
|
|||||||
@@ -110,19 +110,29 @@ class SyncManager:
|
|||||||
try:
|
try:
|
||||||
# 1. Push lokal data op — inkl. sletninger
|
# 1. Push lokal data op — inkl. sletninger
|
||||||
payload = self._build_push_payload()
|
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)
|
push_result = self._post("/sync/push", payload)
|
||||||
self._save_playlist_ids(push_result.get("playlist_id_map", {}))
|
self._save_playlist_ids(push_result.get("playlist_id_map", {}))
|
||||||
|
logger.info(f"Push svar: {push_result}")
|
||||||
|
|
||||||
# Ryd deleted_playlists nu de er sendt
|
# 2. Pull — sletninger er nu gennemført på serveren.
|
||||||
if payload.get("deleted_playlists"):
|
# 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 = sqlite3.connect(self._db_path)
|
||||||
conn.execute("DELETE FROM deleted_playlists")
|
conn.execute("DELETE FROM deleted_playlists")
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
logger.info(f"deleted_playlists ryddet efter pull")
|
||||||
# 2. Pull — nu er sletninger gennemført på serveren
|
|
||||||
pull_result = self._get("/sync/pull")
|
|
||||||
self._apply_pull(pull_result)
|
|
||||||
|
|
||||||
pl_count = len(pull_result.get("my_playlists", []))
|
pl_count = len(pull_result.get("my_playlists", []))
|
||||||
logger.info(
|
logger.info(
|
||||||
@@ -240,11 +250,15 @@ class SyncManager:
|
|||||||
"songs": pl_songs,
|
"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 = [
|
deleted = [
|
||||||
row["name"] for row in conn.execute(
|
row["api_project_id"]
|
||||||
"SELECT name FROM deleted_playlists"
|
for row in conn.execute(
|
||||||
|
"SELECT api_project_id FROM deleted_playlists"
|
||||||
).fetchall()
|
).fetchall()
|
||||||
|
if row["api_project_id"]
|
||||||
]
|
]
|
||||||
|
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -282,14 +296,18 @@ class SyncManager:
|
|||||||
d.get("stepsheet_url",""), existing["id"]))
|
d.get("stepsheet_url",""), existing["id"]))
|
||||||
|
|
||||||
# Importer/opdater egne playlister fra server — server er sandhed
|
# 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:
|
try:
|
||||||
deleted_names = set(
|
deleted_rows = conn.execute(
|
||||||
row["name"] for row in conn.execute(
|
"SELECT name, api_project_id FROM deleted_playlists"
|
||||||
"SELECT name FROM deleted_playlists"
|
).fetchall()
|
||||||
).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:
|
except Exception:
|
||||||
|
deleted_server_ids = set()
|
||||||
deleted_names = set()
|
deleted_names = set()
|
||||||
|
|
||||||
for pl in data.get("my_playlists", []):
|
for pl in data.get("my_playlists", []):
|
||||||
@@ -299,7 +317,10 @@ class SyncManager:
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# Spring over hvis listen er markeret som slettet lokalt
|
# 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
|
continue
|
||||||
|
|
||||||
existing = conn.execute(
|
existing = conn.execute(
|
||||||
@@ -408,4 +429,4 @@ class SyncManager:
|
|||||||
position += 1
|
position += 1
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -365,6 +365,7 @@ class MainWindow(QMainWindow):
|
|||||||
self._playlist_panel.event_started.connect(self._on_event_started)
|
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.next_song_ready.connect(self._on_next_song_ready)
|
||||||
self._playlist_panel.playlist_changed.connect(self._on_playlist_changed)
|
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
|
# Debounce-timer til auto-sync — starter sync 5 sek efter sidst ændring
|
||||||
self._sync_debounce = QTimer(self)
|
self._sync_debounce = QTimer(self)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class PlaylistBrowserDialog(QDialog):
|
|||||||
"""Kombineret gem/hent dialog til danselister."""
|
"""Kombineret gem/hent dialog til danselister."""
|
||||||
|
|
||||||
playlist_selected = pyqtSignal(int, str) # playlist_id, name
|
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,
|
def __init__(self, mode: str = "load", current_songs: list = None,
|
||||||
current_name: str = "", parent=None):
|
current_name: str = "", parent=None):
|
||||||
@@ -344,10 +345,11 @@ class PlaylistBrowserDialog(QDialog):
|
|||||||
)
|
)
|
||||||
if reply == QMessageBox.StandardButton.Yes:
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
try:
|
try:
|
||||||
from local.local_db import get_db
|
from local.local_db import delete_playlist
|
||||||
with get_db() as conn:
|
delete_playlist(pl["id"])
|
||||||
conn.execute("DELETE FROM playlists WHERE id=?", (pl["id"],))
|
|
||||||
self._load_data()
|
self._load_data()
|
||||||
|
# Signal til main_window om at køre sync
|
||||||
|
self.sync_requested.emit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
QMessageBox.warning(self, "Fejl", f"Kunne ikke slette: {e}")
|
QMessageBox.warning(self, "Fejl", f"Kunne ikke slette: {e}")
|
||||||
|
|
||||||
|
|||||||
@@ -178,13 +178,8 @@ class PlaylistManagerDialog(QDialog):
|
|||||||
)
|
)
|
||||||
if reply == QMessageBox.StandardButton.Yes:
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
try:
|
try:
|
||||||
from local.local_db import get_db
|
from local.local_db import delete_playlist
|
||||||
with get_db() as conn:
|
delete_playlist(pl["id"])
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO deleted_playlists (name) "
|
|
||||||
"SELECT name FROM playlists WHERE id=?", (pl["id"],)
|
|
||||||
)
|
|
||||||
conn.execute("DELETE FROM playlists WHERE id=?", (pl["id"],))
|
|
||||||
self._load_saved_playlists()
|
self._load_saved_playlists()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._load_status.setText(f"Fejl: {e}")
|
self._load_status.setText(f"Fejl: {e}")
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class PlaylistPanel(QWidget):
|
|||||||
playlist_changed = pyqtSignal()
|
playlist_changed = pyqtSignal()
|
||||||
event_started = 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
|
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_ICON = {"pending": " ", "playing": " ▶ ", "played": " ✓ ", "skipped": " — ", "next": " ▷ "}
|
||||||
STATUS_COLOR = {"pending": "#5a6070", "playing": "#e8a020", "played": "#2ecc71", "skipped": "#e74c3c", "next": "#3b8fd4"}
|
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._btn_save_current.setToolTip(f"Gem ændringer til '{name}'")
|
||||||
self._save_named_playlist_id(pl_id)
|
self._save_named_playlist_id(pl_id)
|
||||||
dialog.playlist_selected.connect(on_saved)
|
dialog.playlist_selected.connect(on_saved)
|
||||||
|
dialog.sync_requested.connect(self._request_sync)
|
||||||
dialog.exec()
|
dialog.exec()
|
||||||
|
|
||||||
def _save_current(self):
|
def _save_current(self):
|
||||||
@@ -572,10 +574,15 @@ class PlaylistPanel(QWidget):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
QMessageBox.warning(self, "Fejl", f"Kunne ikke gemme: {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):
|
def _load_dialog(self):
|
||||||
from ui.playlist_browser import PlaylistBrowserDialog
|
from ui.playlist_browser import PlaylistBrowserDialog
|
||||||
dialog = PlaylistBrowserDialog(mode="load", parent=self.window())
|
dialog = PlaylistBrowserDialog(mode="load", parent=self.window())
|
||||||
dialog.playlist_selected.connect(self._load_playlist_by_id)
|
dialog.playlist_selected.connect(self._load_playlist_by_id)
|
||||||
|
dialog.sync_requested.connect(self._request_sync)
|
||||||
dialog.exec()
|
dialog.exec()
|
||||||
|
|
||||||
def _load_playlist_by_id(self, pl_id: int, pl_name: str):
|
def _load_playlist_by_id(self, pl_id: int, pl_name: str):
|
||||||
|
|||||||
Reference in New Issue
Block a user