diff --git a/backend/app/routes/plex/collections.py b/backend/app/routes/plex/collections.py index 3e791adb..abd83d79 100644 --- a/backend/app/routes/plex/collections.py +++ b/backend/app/routes/plex/collections.py @@ -454,16 +454,25 @@ def create_collection( raise not_found("library") _reject_duplicate_name(db, lib.id, title) wrote = False + new_rk = "" try: with PlexClient(db) as plex: # A show collection is made as one (Plex type 2): as a movie one it stayed empty. new = plex.create_collection(lib.plex_key, title, seed, 2 if seed_show else 1) wrote = True - col = plex_sync.resync_collection(db, plex, lib, str(new.get("ratingKey"))) + new_rk = str(new.get("ratingKey") or "") + col = plex_sync.resync_collection(db, plex, lib, new_rk) col.editable = True db.commit() except (PlexError, PlexNotConfigured) as e: db.rollback() + if wrote and new_rk: + # It is on Plex now, and only the follow-up read failed. Its row is kept, editable: the + # full sync preserves `editable` but creates a missing row read-only, so the collection + # would drop out of every "add to" list; and the duplicate-name check reads these rows, + # so a retry of the same name would make a second one on Plex. + db.add(PlexCollection(rating_key=new_rk, library_id=lib.id, title=title, editable=True)) + db.commit() raise _write_or_pending_error(wrote, e) from e return _collection_card(col) diff --git a/backend/tests/test_plex_collections_filter.py b/backend/tests/test_plex_collections_filter.py index 60048cbb..dfa8b012 100644 --- a/backend/tests/test_plex_collections_filter.py +++ b/backend/tests/test_plex_collections_filter.py @@ -15,6 +15,7 @@ from app.db import get_db from app.main import app from app.models import PlexCollection, PlexItem, PlexLibrary, PlexShow, User from app.plex.client import PlexError +from app.plex.lookups import by_rating_key from app.routes.plex import collections as coll_routes @@ -355,6 +356,16 @@ def test_create_resync_failure_is_422(db, monkeypatch, override_dep): assert r.status_code == 422, r.text assert r.json()["detail"] == {"code": "refused", "reason": "collection_pending_sync"} assert "create_collection" in stub.calls + # It exists on Plex: its row stays, editable, until the next sync fills it in — so it is offered + # where collections are added to, and the same name is not made twice. + row = by_rating_key(db, PlexCollection, "NEW") + assert row is not None and row.editable and row.title == "Fresh Coll" + again = client.post( + "/api/plex/collections", + json={"library": "1", "title": "Fresh Coll", "item_rating_key": "m1"}, + ) + assert again.status_code == 409, again.text + assert stub.calls.count("create_collection") == 1 def test_mutation_failure_returns_422_write_failed(db, monkeypatch, override_dep):