Merge pull request #685 from lokal-music/release/v12.5.2
Changelog
Windows builds: free code signing provided by SignPath.io, certificate by SignPath Foundation (code signing policy).
FYI:
Lokal is 100% local; the library, history, anything in database, or any info that is used for API's such as yt-dlp, LRCLIB, is NOT seen by us.
What's Changed
-
fix(desktop): restore saved YouTube Music playlists and session-stable cache (#683)
Details
Root cause
Desktop's
online:libraryPlaylistsandonline:libraryPlaylistTracksIPC handlers derived the account/cache key by hashing every YouTube cookie, then re-read the session after the network request and compared the entire cookie string. Normal cookie refreshes during YouTube API requests were interpreted as a sign-in/account switch, discarding the result and returningYouTube Music account changed.. The sidebar consequently showed an empty YouTube shelf even while signed in.Fix
- Introduce
youtubePlaylistAccountIdas a SHA-256 identifier based on Lokal's saved YouTube sign-in revision + isolated session partition + selected Music account/channel. It deliberately ignores rotating visitor cookies, session timestamps, client versions, and unrelated cookie order. - Apply the stable identifier consistently for the playlist list and individual track cache, pending requests, and post-request session validation. Keep the existing
youtube.accountHeadersauthentication validation; an actual sign-in, sign-out, profile switch, or Music channel switch invalidates prior cached metadata. - For older sessions without sign-in revision, fall back to a durable authentication cookie (not short-lived YouTube tracking cookies).
- Reuse the existing per-account disk cache and optional 24h auto-refresh; the old unstable cache key may cause one initial refresh after upgrade, but subsequent normal cookie rotations should no longer clear lists.
Additional playlist-list fix
The existing
scripts/youtube-library-playlists.test.mjstest surfaced a separate continuation issue: when a playlist ID repeated on another page, the later (potentially incomplete) metadata overwrote the first saved playlist card. The parser now retains the first occurrence, preserving original title, artwork, and order.CI
The Discovery shelf workflow now includes the YouTube saved-playlist continuation test, the new session/cache tests, and a syntax check for the new identity module.
Regression tests
scripts/youtube-playlist-session-stability.test.mjschecks cookie rotation, account revisions, partitions, account/channel changes, legacy cookies, and persisted playlist cache retention/isolation.Scope
Desktop Lokal only; no change to Spotify playlists or mobile YouTube session handling. No actual user credentials are logged or persisted.
Verification
Tests committed, but execution of the full Electron app and live authenticated YouTube Music request was not possible in this session. CI results should be reviewed before merge.
- Introduce
-
Fix desktop NAS watcher reconciliation and avoid redundant artwork I/O (#684)
Details
Audit findings
Desktop uses OS-mounted SMB/WebDAV folders, unlike mobile's protocol-specific clients. The earlier desktop NAS patches already address basic UNC/WebClient mapping, per-folder artwork caching, and bounded metadata worker operations; those are left intact.
Identified failures:
- Unreachable catch-up reconciliation:
libraryWatcher.scan()callsscanner.walkDir()even though that private function is not exported. Every directory catch-up reaches a TypeError after a scan and cannot complete its safe missing-file reconciliation. - Double NAS enumeration: catch-up would have walked a share a second time even if exported, increasing NAS I/O and creating a race between scan and destructive pruning.
- Cross-root watcher disruption: NAS overflow clearing
pendingdrops local/other-share updates; a single watcher error invokesstopWatching()on every root and later rebuilds all watchers. - Redundant cover lookups: unchanged tracks with working app-owned embedded artwork still enumerate each album folder, even though embedded art takes priority. This adds network latency and can stall scan progress.
- Legacy remote artwork I/O: an existing UNC cover pointer can reach synchronous
fs.existsSync()on Electron's main thread during metadata extraction. - Synchronous pruning checks: watcher catch-up invokes
fs.existsSync()on missing UNC/WebDAV paths inside a SQLite pruning transaction; this can stall playback and mistake transient unavailability for deletion.
Changes
- Feed the scanner's existing per-root file inventory and walk-failure list to watcher catch-up; refuse to prune on any incomplete scan or missing NAS access. No secondary directory walk.
- Scope overflow event removal and watcher recovery to the affected root; keep healthy roots' watchers and event queues alive. Preserve the existing bounded recovery check.
- Skip folder-cover probing on unchanged tracks with valid app-owned non-folder artwork. Preserve folder-cover refresh/repair behavior and local full-resolution originals.
- Bound legacy remote-cover pointer checks using asynchronous stat.
- Preverify potentially missing file paths in bounded asynchronous batches; enter the synchronous pruning transaction with an in-memory existence predicate only. Fail closed and keep indexed songs if a NAS check times out or the root becomes unreadable.
- Add an integration watcher test for safe catch-up/pruning and lightweight network regressions for per-root event preservation, independent watcher recovery, and refusal to prune on ambiguous NAS errors.
Scope and safety
- No direct SMB/WebDAV protocol stack or credentials added. No changes to playback selection or audio decoding.
- No destructive prune after an incomplete directory walk or metadata failure.
- No downgrade of local artwork resolution; network folder art still uses cached thumbnails + original companion files.
- There is no claim this reproduces the user's Android crash: that is handled separately in mobile PR #386.
Validation
- Network library CI: syntax checks,
npm run test:network-shares, metadata worker regression, renderer build. - Additional native dependency integration test:
node --test scripts/library-watch.test.mjson a desktop environment withbetter-sqlite3installed. - Real Windows SMB/WebDAV mounts: play local or NAS track during refresh; simulate NAS disconnect, reconnect, rename, missing cover and root watcher error. Check that unrelated local/other shares continue updating and offline NAS entries are retained.
Draft until CI and real mounted-share verification.
- Unreachable catch-up reconciliation:
Contributors: @CHEYCKIT
Full Changelog: v12.5.1...v12.5.2
Setup
See README.md for full setup instructions.
macOS: this build is ad-hoc signed, not notarized (no Developer ID certificate is configured). The first time, right-click Lokal in Applications and choose Open, or run xattr -dr com.apple.quarantine /Applications/Lokal.app.
Binary Checksums (SHA-256)
Lokal-12.5.2-arm64-mac.zip:
[4817439BEBD23683A091A1DAE0A23E3E6BC311CE83CD6784C2B87BCC6CC073F9]
Lokal-12.5.2-arm64-mac.zip.blockmap:[CBDA10FB81309A24D2588D258520E39A64A97BDA1A6DECB397E69F853F1259FC]
Lokal-12.5.2-arm64.AppImage:[818C1EBFFAF8115F0732CF6A359CAAA1A419CA68604159D36D404E6182CE11A2]
Lokal-12.5.2-arm64.dmg:[5140FBCEECF53F49E3FC464460E83A1713A7F44B9E1A79499556140EE60672EF]
Lokal-12.5.2-arm64.dmg.blockmap:[76B205A9B6D32E2F9CBB35DD0A7E7B18DF5FB05315D9C7075C58148B47C8865A]
Lokal-12.5.2-mac.zip:[A43B84C28EDFEF78912A2ED0E94AEB4C2094C7F14BB1A4BC995CA1169203F20B]
Lokal-12.5.2-mac.zip.blockmap:[0F6B9E324815459AF5A6C4DEB0D3606124A9405B1D698BA717A966083179DC6F]
Lokal-12.5.2.AppImage:[C58DA114CCDFA8675FD8A104D23CBFF1369C67FB36F07C1920D9274CB63C8E5D]
Lokal-12.5.2.dmg:[2C1C0ECA1A6D9F1B6DCF8DA7AA0F6D5DDF31EFE4159EA2888DC0CCDFFD628826]
Lokal-12.5.2.dmg.blockmap:[DEC2A35E30FD462A19C6959FA1FA5B58E6D463A981BF7E528764108EF6E5AFD0]
Lokal-12.5.2.exe:[2AB99F2AB9168A686DCB925BB9AEE7B639E0B708AF05DC8CD5AC6DC6B7C29E46]
Lokal-Setup-12.5.2.exe:[F8F144545980549D921B4CEF5C723C9D7AF80E34A6F3DBDDC836F0AC3F2D56A0]
Lokal-Setup-12.5.2.exe.blockmap:[23C064CED00EFCED4DA2628D581BA9A9B5FC825C0F449AC9A9C76EF570EBDCE7]
builder-debug.yml:[832C38DC08A192096B29FAB12EAC5C63E202806DFABF57150D3330182FFFF631]
latest-linux-arm64.yml:[CF0C6EE63AB25F5CE2290B3B5B5E86C0FBFBBAF96352CD1648C0E321AD21D219]
latest-linux.yml:[21D8210E00923CF66A23F554768C058AF3F0C96DBB089C842CDBAA20260031DE]
latest-mac.yml:[CCA0A28006289CDF220890E397158CAA2E716C7B5B6F9B2EEC7F43558B5F824F]
latest.yml:[CD81BB298C306EA8927E001C4DB9EC9638E95FD66EBA1CBA944F0C02C12EAEA0]
lokal-music_12.5.2_amd64.deb:[9BF14C2DB10551F7124B98FBAEB02911F96B107AD3D5ACE97CF8CACACD862D3F]
lokal-music_12.5.2_arm64.deb:[65A572899E363936DD72E22AC546052190F93575BFC68E6C966DDAB20BA9E2E8]
