fix(plex): a collection created on Plex keeps its editable row when the refresh read fails

The rollback dropped the new row: the next sync recreated it read-only, out of every 'add to' list, and the duplicate-name check let a retry make a second same-named collection on Plex.
This commit is contained in:
2026-10-08 21:18:48 +02:00
parent 0fce732497
commit 48bd038c5f
2 changed files with 21 additions and 1 deletions
+10 -1
View File
@@ -454,16 +454,25 @@ def create_collection(
raise not_found("library") raise not_found("library")
_reject_duplicate_name(db, lib.id, title) _reject_duplicate_name(db, lib.id, title)
wrote = False wrote = False
new_rk = ""
try: try:
with PlexClient(db) as plex: with PlexClient(db) as plex:
# A show collection is made as one (Plex type 2): as a movie one it stayed empty. # 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) new = plex.create_collection(lib.plex_key, title, seed, 2 if seed_show else 1)
wrote = True 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 col.editable = True
db.commit() db.commit()
except (PlexError, PlexNotConfigured) as e: except (PlexError, PlexNotConfigured) as e:
db.rollback() 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 raise _write_or_pending_error(wrote, e) from e
return _collection_card(col) return _collection_card(col)
@@ -15,6 +15,7 @@ from app.db import get_db
from app.main import app from app.main import app
from app.models import PlexCollection, PlexItem, PlexLibrary, PlexShow, User from app.models import PlexCollection, PlexItem, PlexLibrary, PlexShow, User
from app.plex.client import PlexError from app.plex.client import PlexError
from app.plex.lookups import by_rating_key
from app.routes.plex import collections as coll_routes 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.status_code == 422, r.text
assert r.json()["detail"] == {"code": "refused", "reason": "collection_pending_sync"} assert r.json()["detail"] == {"code": "refused", "reason": "collection_pending_sync"}
assert "create_collection" in stub.calls 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): def test_mutation_failure_returns_422_write_failed(db, monkeypatch, override_dep):