Skip to content

v0.3.15

Choose a tag to compare

@SimoHypers SimoHypers released this 11 Aug 17:43
· 386 commits to master since this release

There was an issue with 0.3.14, so we are skipping it and landing on 0.3.15

Restricted tracks play again. Anything YouTube only serves as a signed stream (made-for-kids uploads, live, and some other restricted music) was being skipped with "YouTube rejected the stream link", because deciphering had stopped working altogether: the signature and n-transform functions are found by regex, and no such pattern matches the players YouTube now serves. Ours, rustypipe's and yt-dlp's were all checked against three current players, in both variants, and none of them match. Limusic now takes its cipher entries from the same two community registries Metrolist reads, polling and merging both so one going stale is survivable, and every release bundles a snapshot so a first run on a network that blocks raw.githubusercontent.com still has a table to work from.

The iOS stream client is gone. googlevideo only serves its URLs for small bounded byte ranges (measured across 22 videos: a 2 KiB range returns 206, while a plain GET, a HEAD and an open-ended range all return 403), and mpv opens a stream with an open-ended range, so those URLs were dead on arrival every time. Two things kept it hidden: iOS sat last in the chain, and the last client was returned without validation, so the one client whose URL was knowably unplayable was the one never checked. Metrolist's ANDROID_VR build takes the slot, and the last client is now validated like every other one.

A stale session no longer reaches you as "http: HTTP status client error (403 Forbidden) for url (...)". A signed-in 401 or 403 now says the session expired and that signing in again is what fixes it. Separately, requests stopped sending a sync ID without a cookie, which was turning calls that would have worked anonymously into hard 401s.

Launch is faster and lighter. Google's answer says the PoToken session token is good for about 12 hours, but it only ever lived in the process, so every launch stood up a hidden web process and ran BotGuard to re-learn a string that was still valid until the next day. It persists between runs now. A player.js already known to carry no usable functions is no longer re-probed in a webview either, which had been a 2.9 MB script injection at every launch to re-learn nothing. Together that is roughly 2.4 seconds of startup and an entire hidden web process gone from every launch after the first.

Long playlists and queues now cost what their page costs, not what their length costs. Both lists render only the rows near the viewport, with padded boxes standing in for the rest so the scrollbar still measures the whole thing. A 5,000-track playlist used to take about three times the memory of a 100-track one and now takes the same; opening the queue panel on a 3,000-track queue used to mount around 58,000 DOM nodes and now mounts about 1,200. Card covers also ask for the size the screen can actually show instead of always 400px, which cut the decoded bitmap memory of a full library page to roughly a quarter.

Smaller things. The app builds one HTTP client instead of six, so one TLS config and one connection pool rather than six of each. WebKit's media and 3D stacks are no longer booted inside a web process that has no audio, video or WebGL in it, worth about 12 MiB per process. The database opens with WAL journalling, so a volume nudge, a pause or a track change no longer takes a full rollback journal and an fsync. And the stream URL cache prunes expired rows instead of growing forever: a real install was sitting at 1,803 rows with 1,772 of them long expired, and came back with 31.