Skip to content

Commit 987f1ae

Browse files
cenodudePazzie van den Berg
andauthored
Change: honor provider chunk settings and expose missing sync tuning controls (#196)
* Change: adjust default batch sizes to intended values * Change: pass SIMKL chunk settings into adapter config * Change: pass Trakt chunk and paging settings into adapter config * Change: add missing provider chunk controls to pair config modal * Fix: run upgrade migration at the correct point in auth flow --------- Co-authored-by: Pazzie van den Berg <cenodude@outlook.com>
1 parent 6c06132 commit 987f1ae

5 files changed

Lines changed: 204 additions & 5 deletions

File tree

assets/js/modals/pair-config/index.js

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,6 +1042,38 @@ function renderFeaturePanel(state){
10421042
`);
10431043
}
10441044

1045+
if (hasSimkl(state)) {
1046+
const sm = (state.cfgRaw?.simkl) || {};
1047+
parts.push(`
1048+
<div class="panel-title small" style="margin-top:6px">SIMKL</div>
1049+
<details id="cx-sm-wl">
1050+
<summary class="muted" style="margin-bottom:10px;">SIMKL watchlist controls</summary>
1051+
<div class="grid2 compact">
1052+
<div class="opt-row" style="grid-column:1/-1">
1053+
<label for="sm-wl-batch">Batch size</label>
1054+
<input id="sm-wl-batch" class="input small" type="number" min="1" max="1000" value="${sm.watchlist_batch_size ?? 500}">
1055+
</div>
1056+
</div>
1057+
</details>
1058+
`);
1059+
}
1060+
1061+
if (hasMDBList(state)) {
1062+
const md = (state.cfgRaw?.mdblist) || {};
1063+
parts.push(`
1064+
<div class="panel-title small" style="margin-top:6px">MDBList</div>
1065+
<details id="cx-md-wl">
1066+
<summary class="muted" style="margin-bottom:10px;">MDBList watchlist controls</summary>
1067+
<div class="grid2 compact">
1068+
<div class="opt-row" style="grid-column:1/-1">
1069+
<label for="md-wl-batch">Batch size</label>
1070+
<input id="md-wl-batch" class="input small" type="number" min="1" max="1000" value="${md.watchlist_batch_size ?? 500}">
1071+
</div>
1072+
</div>
1073+
</details>
1074+
`);
1075+
}
1076+
10451077
right.innerHTML = parts.join("");
10461078
applySubDisable("watchlist");
10471079
return;
@@ -1102,6 +1134,46 @@ function renderFeaturePanel(state){
11021134
`);
11031135
}
11041136

1137+
if (hasSimkl(state)) {
1138+
const sm = (state.cfgRaw?.simkl) || {};
1139+
parts.push(`
1140+
<div class="panel-title small" style="margin-top:6px">SIMKL</div>
1141+
<details id="cx-sm-rt">
1142+
<summary class="muted" style="margin-bottom:10px;">SIMKL ratings controls</summary>
1143+
<div class="grid2 compact">
1144+
<div class="opt-row" style="grid-column:1/-1">
1145+
<label for="sm-rt-chunk">Write chunk size</label>
1146+
<input id="sm-rt-chunk" class="input small" type="number" min="1" max="1000" value="${sm.ratings_chunk_size ?? 500}">
1147+
</div>
1148+
</div>
1149+
</details>
1150+
`);
1151+
}
1152+
1153+
if (hasMDBList(state)) {
1154+
const md = (state.cfgRaw?.mdblist) || {};
1155+
parts.push(`
1156+
<div class="panel-title small" style="margin-top:6px">MDBList</div>
1157+
<details id="cx-md-rt">
1158+
<summary class="muted" style="margin-bottom:10px;">MDBList ratings controls</summary>
1159+
<div class="grid2 compact">
1160+
<div class="opt-row">
1161+
<label for="md-rt-perpage">Items per page</label>
1162+
<input id="md-rt-perpage" class="input small" type="number" min="1" max="5000" value="${md.ratings_per_page ?? 200}">
1163+
</div>
1164+
<div class="opt-row">
1165+
<label for="md-rt-maxpages">Max pages</label>
1166+
<input id="md-rt-maxpages" class="input small" type="number" min="1" max="2000" value="${md.ratings_max_pages ?? 50}">
1167+
</div>
1168+
<div class="opt-row" style="grid-column:1/-1">
1169+
<label for="md-rt-chunk">Write chunk size</label>
1170+
<input id="md-rt-chunk" class="input small" type="number" min="1" max="1000" value="${md.ratings_chunk_size ?? 500}">
1171+
</div>
1172+
</div>
1173+
</details>
1174+
`);
1175+
}
1176+
11051177
if (hasSimkl(state)) {
11061178
parts.push(`<div class="simkl-alert" role="note" aria-live="polite"><div class="title"><span class="ic">⚠</span> Simkl heads-up for Ratings</div><div class="body"><ul class="bul"><li><b>Movies:</b> Rating auto-marks as <i>Completed</i> on Simkl.</li><li>Can appear under <i>Recently watched</i> and <i>My List</i>.</li></ul><div class="mini">Tip: Prefer small windows when backfilling.</div></div></div>`);
11071179
}
@@ -1231,6 +1303,58 @@ left.innerHTML = `
12311303
<label for="cx-tr-hs-unres">Unresolved Freeze</label>
12321304
<label class="switch"><input id="cx-tr-hs-unres" type="checkbox" ${trCfg.history_unresolved ? "checked" : ""}><span class="slider"></span></label>
12331305
</div>
1306+
<div class="opt-row">
1307+
<label for="tr-hs-perpage">Items per page</label>
1308+
<input id="tr-hs-perpage" class="input small" type="number" min="1" max="500" value="${trCfg.history_per_page ?? 100}">
1309+
</div>
1310+
<div class="opt-row">
1311+
<label for="tr-hs-maxpages">Max pages</label>
1312+
<input id="tr-hs-maxpages" class="input small" type="number" min="1" max="100000" value="${trCfg.history_max_pages ?? 10000}">
1313+
</div>
1314+
<div class="opt-row" style="grid-column:1/-1">
1315+
<label for="tr-hs-chunk">Write chunk size</label>
1316+
<input id="tr-hs-chunk" class="input small" type="number" min="1" max="1000" value="${trCfg.history_chunk_size ?? 100}">
1317+
</div>
1318+
</div>
1319+
</details>
1320+
`);
1321+
}
1322+
1323+
if (hasSimkl(state)) {
1324+
const sm = (state.cfgRaw?.simkl) || {};
1325+
parts.push(`
1326+
<div class="panel-title small" style="margin-top:6px">SIMKL</div>
1327+
<details id="cx-sm-hs">
1328+
<summary class="muted" style="margin-bottom:10px;">SIMKL history controls</summary>
1329+
<div class="grid2 compact">
1330+
<div class="opt-row" style="grid-column:1/-1">
1331+
<label for="sm-hs-chunk">Write chunk size</label>
1332+
<input id="sm-hs-chunk" class="input small" type="number" min="1" max="1000" value="${sm.history_chunk_size ?? 500}">
1333+
</div>
1334+
</div>
1335+
</details>
1336+
`);
1337+
}
1338+
1339+
if (hasMDBList(state)) {
1340+
const md = (state.cfgRaw?.mdblist) || {};
1341+
parts.push(`
1342+
<div class="panel-title small" style="margin-top:6px">MDBList</div>
1343+
<details id="cx-md-hs">
1344+
<summary class="muted" style="margin-bottom:10px;">MDBList history controls</summary>
1345+
<div class="grid2 compact">
1346+
<div class="opt-row">
1347+
<label for="md-hs-perpage">Items per page</label>
1348+
<input id="md-hs-perpage" class="input small" type="number" min="1" max="5000" value="${md.history_per_page ?? 1000}">
1349+
</div>
1350+
<div class="opt-row">
1351+
<label for="md-hs-maxpages">Max pages</label>
1352+
<input id="md-hs-maxpages" class="input small" type="number" min="1" max="2000" value="${md.history_max_pages ?? 250}">
1353+
</div>
1354+
<div class="opt-row" style="grid-column:1/-1">
1355+
<label for="md-hs-chunk">Write chunk size</label>
1356+
<input id="md-hs-chunk" class="input small" type="number" min="1" max="1000" value="${md.history_chunk_size ?? 500}">
1357+
</div>
12341358
</div>
12351359
</details>
12361360
`);
@@ -1753,6 +1877,18 @@ async function saveConfigBits(state){
17531877
});
17541878
}
17551879

1880+
if(ID("sm-wl-batch")){
1881+
cfg.simkl = Object.assign({}, cfg.simkl||{}, {
1882+
watchlist_batch_size: Math.max(1, parseInt(ID("sm-wl-batch")?.value||"500",10)||500)
1883+
});
1884+
}
1885+
1886+
if(ID("md-wl-batch")){
1887+
cfg.mdblist = Object.assign({}, cfg.mdblist||{}, {
1888+
watchlist_batch_size: Math.max(1, parseInt(ID("md-wl-batch")?.value||"500",10)||500)
1889+
});
1890+
}
1891+
17561892
const hasJF=String(state.src||"").toUpperCase()==="JELLYFIN"||String(state.dst||"").toUpperCase()==="JELLYFIN";
17571893
if(hasJF){
17581894
const jf=Object.assign({},cfg.jellyfin||{});
@@ -1806,14 +1942,54 @@ async function saveConfigBits(state){
18061942
const perEl = ID("tr-rt-perpage") || ID("tr-rt-page");
18071943
const pagesEl = ID("tr-rt-maxpages")|| ID("tr-rt-pages");
18081944
const chunkEl = ID("tr-rt-chunk");
1945+
const hsPerEl = ID("tr-hs-perpage");
1946+
const hsPagesEl = ID("tr-hs-maxpages");
1947+
const hsChunkEl = ID("tr-hs-chunk");
18091948

18101949
if (perEl) tr.ratings_per_page = clamp(n(perEl.id), 10, 500);
18111950
if (pagesEl) tr.ratings_max_pages = clamp(n(pagesEl.id), 1, 1000);
18121951
if (chunkEl) tr.ratings_chunk_size = clamp(n(chunkEl.id), 10, 1000);
1952+
if (hsPerEl) tr.history_per_page = clamp(n(hsPerEl.id), 1, 500);
1953+
if (hsPagesEl) tr.history_max_pages = clamp(n(hsPagesEl.id), 1, 100000);
1954+
if (hsChunkEl) tr.history_chunk_size = clamp(n(hsChunkEl.id), 1, 1000);
18131955

18141956
cfg.trakt=tr;
18151957
}
18161958

1959+
const hasSM = String(state.src||"").toUpperCase()==="SIMKL" || String(state.dst||"").toUpperCase()==="SIMKL";
1960+
if(hasSM){
1961+
const sm = Object.assign({}, cfg.simkl||{});
1962+
const wlBatchEl = ID("sm-wl-batch");
1963+
const rtChunkEl = ID("sm-rt-chunk");
1964+
const hsChunkEl = ID("sm-hs-chunk");
1965+
if (wlBatchEl) sm.watchlist_batch_size = Math.max(1, parseInt(wlBatchEl.value||"500", 10) || 500);
1966+
if (rtChunkEl) sm.ratings_chunk_size = Math.max(1, parseInt(rtChunkEl.value||"500", 10) || 500);
1967+
if (hsChunkEl) sm.history_chunk_size = Math.max(1, parseInt(hsChunkEl.value||"500", 10) || 500);
1968+
cfg.simkl = sm;
1969+
}
1970+
1971+
const hasMD = String(state.src||"").toUpperCase()==="MDBLIST" || String(state.dst||"").toUpperCase()==="MDBLIST";
1972+
if(hasMD){
1973+
const md = Object.assign({}, cfg.mdblist||{});
1974+
const n = x => parseInt((ID(x)?.value||"").trim(), 10);
1975+
const clamp = (v,min,max)=>Math.min(max,Math.max(min,Number.isFinite(v)?v:min));
1976+
const wlBatchEl = ID("md-wl-batch");
1977+
const rtPerEl = ID("md-rt-perpage");
1978+
const rtPagesEl = ID("md-rt-maxpages");
1979+
const rtChunkEl = ID("md-rt-chunk");
1980+
const hsPerEl = ID("md-hs-perpage");
1981+
const hsPagesEl = ID("md-hs-maxpages");
1982+
const hsChunkEl = ID("md-hs-chunk");
1983+
if (wlBatchEl) md.watchlist_batch_size = clamp(n(wlBatchEl.id), 1, 1000);
1984+
if (rtPerEl) md.ratings_per_page = clamp(n(rtPerEl.id), 1, 5000);
1985+
if (rtPagesEl) md.ratings_max_pages = clamp(n(rtPagesEl.id), 1, 2000);
1986+
if (rtChunkEl) md.ratings_chunk_size = clamp(n(rtChunkEl.id), 1, 1000);
1987+
if (hsPerEl) md.history_per_page = clamp(n(hsPerEl.id), 1, 5000);
1988+
if (hsPagesEl) md.history_max_pages = clamp(n(hsPagesEl.id), 1, 2000);
1989+
if (hsChunkEl) md.history_chunk_size = clamp(n(hsChunkEl.id), 1, 1000);
1990+
cfg.mdblist = md;
1991+
}
1992+
18171993
const res=await fetch("/api/config",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(cfg)});
18181994
if(!res.ok)throw new Error("POST /api/config "+res.status);
18191995
}catch(e){console.warn("[cx] saving config bits failed",e)}

assets/js/modals/upgrade-warning/index.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export default {
163163
}
164164

165165
async function ensureAutoSaved() {
166-
if (requiresCleanReset || state.step !== "migrate" || state.autoSaveStarted) return;
166+
if (requiresCleanReset || state.autoSaveStarted) return;
167167
state.autoSaveStarted = true;
168168
state.autoSaveFailed = false;
169169
state.autoSaveMessage = "Saving the updated config format in the background...";
@@ -213,6 +213,7 @@ export default {
213213
state.step = "migrate";
214214
notify("Sign-in saved. CrossWatch is updating the config in the background.");
215215
render();
216+
ensureAutoSaved();
216217
return;
217218
} catch (err) {
218219
state.saving = false;
@@ -412,7 +413,6 @@ export default {
412413
try { window.cxCloseModal?.(); } catch {}
413414
});
414415
ensureNotesLoaded();
415-
ensureAutoSaved();
416416
}
417417

418418
function render() {
@@ -423,6 +423,7 @@ export default {
423423
}
424424

425425
render();
426+
if (!requiresCleanReset && state.authReady) ensureAutoSaved();
426427
},
427428

428429
unmount() {

cw_platform/config_base.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,19 +321,19 @@ def _transform_secret_tree(obj: Any, *, decrypt: bool, path: tuple[str, ...] = (
321321
# Watchlist
322322
"watchlist_use_etag": True, # Use ETag + local shadow to skip unchanged lists
323323
"watchlist_shadow_ttl_hours": 168, # Refresh ETag baseline weekly even if 304s keep coming
324-
"watchlist_batch_size": 500, # Chunk size for add/remove to avoid 429/rate spikes
324+
"watchlist_batch_size": 100, # Chunk size for add/remove to avoid 429/rate spikes
325325
"watchlist_log_rate_limits": True, # Log X-RateLimit-* and Retry-After when present
326326
"watchlist_freeze_details": True, # Persist last status & ids in freeze store for debugging
327327

328328
# Ratings
329329
"ratings_per_page": 100, # Items per page when indexing (10–100; clamped to 100)
330330
"ratings_max_pages": 50, # Max pages per type; raise if you have >2k ratings/type
331-
"ratings_chunk_size": 500, # Batch size for POST/REMOVE
331+
"ratings_chunk_size": 100, # Batch size for POST/REMOVE
332332

333333
# History
334334
"history_per_page": 100, # Max allowed by Trakt; fastest without spamming
335335
"history_max_pages": 10000, # Safety cap for huge libraries; lower to bound runtime
336-
"history_chunk_size": 500, # Batch size for history add/remove writes
336+
"history_chunk_size": 100, # Batch size for history add/remove writes
337337
"history_unresolved": False, # bool, default false (enable the freeze file)
338338
"history_number_fallback": False, # episode number fallback (no S/E-based resolution when episode IDs are missing)
339339

providers/sync/_mod_SIMKL.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ class SIMKLConfig:
222222
max_retries: int = 3
223223
rate_get_per_sec: float = 10.0
224224
rate_post_per_sec: float = 1.0
225+
watchlist_batch_size: int = 100
226+
ratings_chunk_size: int = 100
227+
history_chunk_size: int = 100
225228

226229

227230
class SIMKLClient:
@@ -312,6 +315,9 @@ def _rate(key: str, default: float) -> float:
312315
max_retries=int(simkl_cfg.get("max_retries", cfg.get("max_retries", 3))),
313316
rate_get_per_sec=rate_get,
314317
rate_post_per_sec=rate_post,
318+
watchlist_batch_size=int(simkl_cfg.get("watchlist_batch_size", 100) or 100),
319+
ratings_chunk_size=int(simkl_cfg.get("ratings_chunk_size", 100) or 100),
320+
history_chunk_size=int(simkl_cfg.get("history_chunk_size", 100) or 100),
315321
)
316322
if not self.cfg.api_key or not self.cfg.access_token:
317323
raise SIMKLError("SIMKL requires both api_key (or client_id) and access_token")
@@ -321,6 +327,7 @@ def _rate(key: str, default: float) -> float:
321327

322328
self.client = SIMKLClient(self.cfg, simkl_cfg).connect()
323329
self.raw_cfg = cfg
330+
self.config = cfg
324331
self.progress_factory = (
325332
lambda feature, total=None, throttle_ms=300: make_snapshot_progress(
326333
ctx,

providers/sync/_mod_TRAKT.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,13 @@ class TRAKTConfig:
171171
max_retries: int = 3
172172
rate_get_per_sec: float = 3.33
173173
rate_post_per_sec: float = 1.0
174+
watchlist_batch_size: int = 100
175+
ratings_per_page: int = 100
176+
ratings_max_pages: int = 50
177+
ratings_chunk_size: int = 100
178+
history_per_page: int = 100
179+
history_max_pages: int = 10000
180+
history_chunk_size: int = 100
174181
history_number_fallback: bool = False
175182
history_collection: bool = False
176183
history_collection_types: list[str] | None = None
@@ -350,6 +357,13 @@ def _rate(key: str, default: float) -> float:
350357
max_retries=int(t.get("max_retries", cfg.get("max_retries", 3))),
351358
rate_get_per_sec=rate_get,
352359
rate_post_per_sec=rate_post,
360+
watchlist_batch_size=int(t.get("watchlist_batch_size", 100) or 100),
361+
ratings_per_page=int(t.get("ratings_per_page", 100) or 100),
362+
ratings_max_pages=int(t.get("ratings_max_pages", 50) or 50),
363+
ratings_chunk_size=int(t.get("ratings_chunk_size", 100) or 100),
364+
history_per_page=int(t.get("history_per_page", 100) or 100),
365+
history_max_pages=int(t.get("history_max_pages", 10000) or 10000),
366+
history_chunk_size=int(t.get("history_chunk_size", 100) or 100),
353367
history_number_fallback=bool(t.get("history_number_fallback")),
354368
history_collection=bool(t.get("history_collection")),
355369
history_collection_types=types or None,
@@ -362,6 +376,7 @@ def _rate(key: str, default: float) -> float:
362376

363377
self.client = TRAKTClient(self.cfg, cfg).connect()
364378
self.raw_cfg = cfg
379+
self.config = cfg
365380
self.progress_factory = (
366381
lambda feature, total=None, throttle_ms=300: make_snapshot_progress(
367382
ctx,

0 commit comments

Comments
 (0)