132 lines
6.8 KiB
Markdown
132 lines
6.8 KiB
Markdown
# Persisted settings across production updates
|
||
|
||
## Result
|
||
|
||
No Phase 1–5 persistence regression was reproduced. No runtime fix or breaking
|
||
commit is justified. The old b77938a shell (verified tag `1175f1a1d2c1`) upgraded
|
||
through its own banner/Refresh UI flow at the same origin to current main
|
||
`cf27d4f15a29747123437862ab4e461fd78178e6`. All 24 existing setting keys and all 14 checked controls were preserved
|
||
on Chromium and WebKit. The original worktree base/live deployment snapshot
|
||
`0b8e398` was also tested separately: both engines preserve the same 24/24 keys,
|
||
controls, profile/import policy; Chromium again saves and plays the complete
|
||
clip before/after. This avoids conflating concurrent main changes.
|
||
|
||
Both servers are actual archived Bun implementations, with isolated DB/media
|
||
directories. A local proxy supplies deliberately older profile copies and blocks
|
||
profile writes; no credentials or real online profiles are used. Settings values
|
||
were set using real controls/change handlers, and EQ using its actual preset
|
||
button. The installed page was reloaded before recording its exact `_ytpdata`
|
||
JSON; old/new raw snapshots are in the results file. Refresh UI and import use
|
||
the existing button handlers via `.click()` on the DOM element: physical
|
||
Playwright hit-testing was obstructed by the bottom nav at large font size.
|
||
This tests persistence/update handlers, not modal pointer geometry.
|
||
|
||
## Every-key comparison
|
||
|
||
Values below are identical on both engines; no key was removed or reset.
|
||
|
||
| Key | Old (after reload) | Upgraded | Status |
|
||
| --- | --- | --- | --- |
|
||
| quality | `"360p"` | `"360p"` | same |
|
||
| volume | `1` | `1` | same |
|
||
| audioOnly | `false` | `false` | same |
|
||
| autoPreload | `true` | `true` | same |
|
||
| p2pShare | `true` | `true` | same |
|
||
| p2pReceive | `true` | `true` | same |
|
||
| saveBeforePlay | `true` | `true` | same |
|
||
| repeatMode | `"all"` | `"all"` | same |
|
||
| loopOne | `true` | `true` | same |
|
||
| shuffle | `false` | `false` | same |
|
||
| theme | `"light"` | `"light"` | same |
|
||
| fontScale | `"large"` | `"large"` | same |
|
||
| levelVolume | `false` | `false` | same |
|
||
| liveAudioIOS | `false` | `false` | same |
|
||
| parallelSaves | `2` | `2` | same |
|
||
| density | `"compact"` | `"compact"` | same |
|
||
| perfMode | `true` | `true` | same |
|
||
| reduceMotion | `true` | `true` | same |
|
||
| layout | `"f7-swipe"` | `"f7-swipe"` | same |
|
||
| autoBackupEnabled | `false` | `false` | same |
|
||
| autoBackupIntervalDays | `7` | `7` | same |
|
||
| serviceVideo | `false` | `false` | same |
|
||
| foldedCards | `{}` | `{}` | same |
|
||
| eq | `{"preset":"bass","g":[5,7,5,3,1,0,0,0,0,0]}` | `{"preset":"bass","g":[5,7,5,3,1,0,0,0,0,0]}` | same |
|
||
|
||
Save before playing and Auto-save playlist videos stayed ON; audio-only OFF.
|
||
Quality stayed 360p, layout f7-swipe, loop/repeat ON, light theme, large font,
|
||
compact density, performance/reduced motion ON and two parallel saves.
|
||
The tested b77938a build already has ten-band EQ, so its Bass boost curve was
|
||
unchanged. The five-band compatibility migration in app.js was not triggered.
|
||
|
||
## Behaviour and alternative overwrite paths
|
||
|
||
Chromium saved the complete 30,290-byte H.264/AAC clip before and after the
|
||
upgrade, set cachedIds, used an OPFS blob source, reached readyState ≥3 and
|
||
advanced playback time. The existing copy was deleted before the second open
|
||
to force a genuinely new post-upgrade download. Linux WebKit reports native
|
||
OPFS unsupported on both builds; preservation/UI checks pass but successful
|
||
native storage/playback there is not claimed or shimmed.
|
||
|
||
Profile tests exercise the launch path with `syncedAt:200`:
|
||
|
||
| Server updatedAt | Server Save before playing | Device result, both engines |
|
||
| ---: | --- | --- |
|
||
| 100 | false | Local true and every local setting retained |
|
||
| 300 | false | Server false adopted; layout classic and defaults/remote values applied |
|
||
|
||
A copy with old *values* but a newer timestamp can therefore disable autosave.
|
||
This is existing last-write-wins behavior, not an update migration regression:
|
||
`pullProfileIfNewer()` and `applyProfileData()` at b77938a already implement the
|
||
same timestamp comparison and whole-settings replacement. Equal timestamps
|
||
also retain local settings (unit test). A genuine older timestamp cannot reset
|
||
local settings in these tests.
|
||
|
||
`API.loadData()` reads localStorage in web mode; boot overlays loaded.settings
|
||
on DEFAULT_SETTINGS. Phase 4 extraction does not create a second data object.
|
||
The Phase 3 head loader only reads `_ytpdata` and sets appearance attributes;
|
||
it never writes settings. `syncOnLaunch()` invokes profile reconciliation
|
||
after the first render. `/api/user/data` was monitored/mocked with false
|
||
autosave settings and received zero requests: fingerprint sync is not a settings
|
||
pull at launch. `saveDataToStorage()` pushes metadata to `/api/user/sync`,
|
||
without a settings field.
|
||
|
||
Importing a backup containing opposite settings through the actual file input
|
||
and Import button preserved the current settings. `importBackup()` merges
|
||
playlists/history/positions but does not apply `imp.settings`. Intentional
|
||
profile adoption/share-link loading uses `applyProfileData()` and *does* replace
|
||
settings; this is already documented on the existing confirmation UI.
|
||
|
||
## Verification
|
||
|
||
```sh
|
||
node perf/settings-persist.mjs --browser all --target main --allow-unsupported-webkit
|
||
node perf/settings-persist.mjs --browser all --target 0b8e398 --allow-unsupported-webkit --out perf/results/settings-persist-live-base.json
|
||
node --test frontend/*.test.js
|
||
(cd server && bun install && bun run test)
|
||
```
|
||
|
||
Without `--allow-unsupported-webkit`, unverified native WebKit storage exits 2.
|
||
Any assertion failure exits 1 even with that allowance. The server-side proxy
|
||
is essential: WebKit worker network requests bypass page-route mocks.
|
||
|
||
The two shared Chromium Settings errors (`null.addEventListener`) are recorded
|
||
in the results; they already occur on the old build. They did not stop the
|
||
playback/autosave handlers from persisting or saving. This is not an assertion
|
||
that every Settings interaction is error-free.
|
||
|
||
Only tests, harness and documentation changed; no runtime implementation or
|
||
save behavior changed. A phase bisect and failing-then-fixed runtime test cannot be supplied
|
||
honestly because no setting loss was reproduced on either target.
|
||
|
||
## Phone checks
|
||
|
||
On the installed PWA, inspect Settings → Playback → Save before playing, ensure
|
||
Audio-only is OFF and no explicit streaming override is selected. Auto-save
|
||
playlist videos is a separate policy. Check the linked profile and whether
|
||
another device/imported profile pushed a newer copy with the toggle OFF.
|
||
Compare settings before/after an online launch. Re-check origin/PWA identity,
|
||
iOS Clear Website Data/storage eviction/private mode, free quota and Downloads
|
||
failure details. Native iPhone OPFS, suspension, quota sharing and storage reset
|
||
remain unverified by Linux WebKit. A phone-side failing sequence/settings export
|
||
is needed before changing the sync policy or save logic.
|