plex deadlock + version showing stale + "disco" screen after docker update fixes
This commit is contained in:
@@ -40,7 +40,7 @@ class PreferencesService:
|
|||||||
self._settings = settings
|
self._settings = settings
|
||||||
self._config_path = settings.config_file_path
|
self._config_path = settings.config_file_path
|
||||||
self._config_cache: Optional[dict] = None
|
self._config_cache: Optional[dict] = None
|
||||||
self._cache_lock = threading.Lock()
|
self._cache_lock = threading.RLock()
|
||||||
self._migrate_musicbrainz_settings()
|
self._migrate_musicbrainz_settings()
|
||||||
self._ensure_instance_id()
|
self._ensure_instance_id()
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ from fastapi.responses import FileResponse
|
|||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
|
|
||||||
|
_NO_CACHE_HEADERS = {"Cache-Control": "no-cache"}
|
||||||
|
|
||||||
|
|
||||||
def mount_frontend(app: FastAPI):
|
def mount_frontend(app: FastAPI):
|
||||||
backend_static = Path(__file__).parent / "static"
|
backend_static = Path(__file__).parent / "static"
|
||||||
frontend_root = Path(__file__).resolve().parents[1] / "frontend"
|
frontend_root = Path(__file__).resolve().parents[1] / "frontend"
|
||||||
@@ -83,7 +86,7 @@ def mount_frontend(app: FastAPI):
|
|||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def serve_root():
|
async def serve_root():
|
||||||
if index_html.exists():
|
if index_html.exists():
|
||||||
return FileResponse(index_html)
|
return FileResponse(index_html, headers=_NO_CACHE_HEADERS)
|
||||||
raise HTTPException(status_code=404, detail="Frontend not built yet")
|
raise HTTPException(status_code=404, detail="Frontend not built yet")
|
||||||
|
|
||||||
@app.get("/{full_path:path}")
|
@app.get("/{full_path:path}")
|
||||||
@@ -91,5 +94,5 @@ def mount_frontend(app: FastAPI):
|
|||||||
if full_path.startswith("api"):
|
if full_path.startswith("api"):
|
||||||
raise HTTPException(status_code=404, detail="API route not found")
|
raise HTTPException(status_code=404, detail="API route not found")
|
||||||
if index_html.exists():
|
if index_html.exists():
|
||||||
return FileResponse(index_html)
|
return FileResponse(index_html, headers=_NO_CACHE_HEADERS)
|
||||||
raise HTTPException(status_code=404, detail="Frontend not built yet")
|
raise HTTPException(status_code=404, detail="Frontend not built yet")
|
||||||
|
|||||||
@@ -77,6 +77,43 @@ class TestCreatePlexPin:
|
|||||||
assert resp.status_code == 500
|
assert resp.status_code == 500
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetOrCreateSettingNoDeadlock:
|
||||||
|
def test_get_or_create_setting_does_not_deadlock(self, tmp_path):
|
||||||
|
import threading
|
||||||
|
|
||||||
|
from core.config import Settings
|
||||||
|
from services.preferences_service import PreferencesService
|
||||||
|
|
||||||
|
config_path = tmp_path / "config.json"
|
||||||
|
settings = Settings(
|
||||||
|
root_app_dir=tmp_path,
|
||||||
|
config_file_path=config_path,
|
||||||
|
cache_dir=tmp_path / "cache",
|
||||||
|
library_db_path=tmp_path / "cache" / "library.db",
|
||||||
|
queue_db_path=tmp_path / "cache" / "queue.db",
|
||||||
|
)
|
||||||
|
|
||||||
|
result = None
|
||||||
|
exc = None
|
||||||
|
|
||||||
|
def run():
|
||||||
|
nonlocal result, exc
|
||||||
|
try:
|
||||||
|
prefs = PreferencesService(settings)
|
||||||
|
result = prefs.get_or_create_setting("plex_client_id", lambda: "test-client-id")
|
||||||
|
result = (result, prefs.get_or_create_setting("plex_client_id", lambda: "other"))
|
||||||
|
except Exception as e:
|
||||||
|
exc = e
|
||||||
|
|
||||||
|
t = threading.Thread(target=run)
|
||||||
|
t.start()
|
||||||
|
t.join(timeout=5)
|
||||||
|
assert not t.is_alive(), "Deadlock detected: PreferencesService hung for 5s"
|
||||||
|
assert exc is None
|
||||||
|
assert result[0] == "test-client-id"
|
||||||
|
assert result[1] == "test-client-id"
|
||||||
|
|
||||||
|
|
||||||
class TestPollPlexPin:
|
class TestPollPlexPin:
|
||||||
def test_poll_pending(self, auth_client):
|
def test_poll_pending(self, auth_client):
|
||||||
resp = auth_client.get("/plex/auth/poll?pin_id=12345")
|
resp = auth_client.get("/plex/auth/poll?pin_id=12345")
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ export const getVersionQuery = () =>
|
|||||||
staleTime: CACHE_TTL.VERSION_INFO,
|
staleTime: CACHE_TTL.VERSION_INFO,
|
||||||
queryKey: VersionQueryKeyFactory.info(),
|
queryKey: VersionQueryKeyFactory.info(),
|
||||||
queryFn: ({ signal }) => api.global.get<VersionInfo>(API.version.info(), { signal }),
|
queryFn: ({ signal }) => api.global.get<VersionInfo>(API.version.info(), { signal }),
|
||||||
refetchOnWindowFocus: false
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnMount: 'always'
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const getUpdateCheckQuery = () =>
|
export const getUpdateCheckQuery = () =>
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { AlertTriangle, Home, RotateCw } from 'lucide-svelte';
|
||||||
|
import { page } from '$app/state';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Error - MusicSeerr</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="w-full px-2 sm:px-4 lg:px-8 py-4 sm:py-8 max-w-7xl mx-auto">
|
||||||
|
<div class="flex flex-col items-center justify-center py-20 gap-4 text-center">
|
||||||
|
<AlertTriangle class="h-16 w-16 text-error/40" />
|
||||||
|
<h1 class="text-lg font-semibold text-base-content/80">Something went wrong</h1>
|
||||||
|
<p class="text-sm text-base-content/60">
|
||||||
|
{page.error?.message ?? 'An unexpected error occurred'}
|
||||||
|
</p>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<a href="/" class="btn btn-ghost btn-sm">
|
||||||
|
<Home class="h-4 w-4" />
|
||||||
|
Home
|
||||||
|
</a>
|
||||||
|
<button class="btn btn-accent btn-sm" onclick={() => location.reload()}>
|
||||||
|
<RotateCw class="h-4 w-4" />
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user