Repository navigation
Conversation
streamrip sets WindowsSelectorEventLoopPolicy on Windows, and that loop does not implement asyncio subprocesses: every create_subprocess_exec call raises NotImplementedError, so --codec / [conversion] and segment concatenation always failed there. Run ffmpeg through subprocess.run in a worker thread instead, with stdin closed so it never reads from the TTY.
Tidal serves Max quality as an MPEG-DASH manifest (application/dash+xml) instead of the JSON manifest used by the other tiers. get_downloadable() tried json.loads() on it, hit JSONDecodeError and silently retried at a lower quality, so Max always ended up as AAC. Parse the MPD (single Representation, $Number$ SegmentTemplate), fetch the init segment and media segments concurrently, join them in order and remux to FLAC with ffmpeg without re-encoding. Unknown manifest layouts raise NonStreamableError instead of downloading the wrong thing. Fixes nathom#981
Tracks that Tidal only delivers as AAC (.m4a) can now be converted to 16-bit FLAC with ffmpeg, via [tidal] convert_aac_to_flac = true or the --aac-to-flac flag. It runs before tagging so metadata and cover art are written natively to the FLAC file, and it is skipped when the general [conversion] setting is enabled. The option defaults to false, so existing config files without the key keep loading.
This was referenced Oct 25, 2026
|
Hi, I've instelled your fork with rip --no-db url https://tidal.com/album/544118021 ## got FLAC
rip --no-db url https://tidal.com/album/558242992 ## got M4ATested with q3. When trying q2, both return m4a. No way to download So it seems to work for some. I'm new to Tidal: Does it mean they do not have everything lossless? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #981. Builds on the idea in #998, but works on Windows without changing the event loop policy and downloads segments concurrently.
Problem
HI_RES_LOSSLESSas an MPEG-DASH manifest (application/dash+xml).get_downloadable()runsjson.loads()on it, getsJSONDecodeError, logs "Retrying with lower quality" and ends up with AAC 320.rip/main.pysetsWindowsSelectorEventLoopPolicy, and that loop does not implement asyncio subprocesses, so everyasyncio.create_subprocess_execraisesNotImplementedError. That breaks--codec/[conversion]on Windows today, and it is also why the ffmpeg remux in (fixes #981) HI_RES_LOSSLESS downloads via MPEG-DASH manifest support #998 fails for Windows users in that thread.Changes
converter.run_ffmpeg(): runs ffmpeg withsubprocess.runin a worker thread (asyncio.to_thread) with stdin closed. Used byConverter.convert()andconcat_audio_files(). Works on every platform and with any event loop.TidalDashDownloadable:get_downloadable()checksmanifestMimeTypefirst. For DASH,_parse_mpd()reads the singleRepresentationand the$Number$SegmentTemplate; segments are fetched concurrently (up to 16 at a time, results kept in order), joined byte for byte after the init segment and remuxed to FLAC with-c:a copy(no re-encoding). Progress is reported per segment. A manifest layout we don't understand raisesNonStreamableErrorinstead of downloading the wrong thing.[tidal] convert_aac_to_flac = false, or--aac-to-flac): tracks that Tidal only delivers as AAC are converted to 16-bit FLAC before tagging, so tags and cover art are written natively. Skipped when[conversion]is enabled. The dataclass field has a default, so existing configs without the key keep loading and no config version bump is needed. It does not recover quality lost to AAC; it is for people who want a uniform FLAC library.No change to
CLIENT_IDis needed: the current one already returnsHI_RES_LOSSLESSDASH manifests.Testing
Windows 11, Python 3.11, ffmpeg 8.1, Tidal Max subscription, running
ripfrom this branch:rip -q 3 url .../track/77646170(Max available)rip -q 3 --aac-to-flac url .../track/103805726(only AAC available)rip -q 3 url .../track/103805726(same, no flag).m4a, unchanged behaviourtests/test_tidal_dash.py(MPD parsing, segment URLs, unsupported layouts).test_meta.py::test_album_metadata_qobuzfails ondevwithout these changes too.Notes
LOSSLESSfor 16-bit tracks returns AACHIGH(also reported in (fixes #981) HI_RES_LOSSLESS downloads via MPEG-DASH manifest support #998). That is Tidal behaviour and not addressed here.