MCPcopy Index your code
hub / github.com/J-M-PUNK/tideway

github.com/J-M-PUNK/tideway @v1.18.7

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.18.7 ↗ · + Follow
3,979 symbols 13,072 edges 336 files 1,499 documented · 38%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Tideway

The native desktop client for Tidal. Bit-perfect gapless playback, hi-res downloads, a real parametric EQ, and your listening history from Last.fm and Spotify, in one app with no web bloat. It packages a FastAPI backend, a React and Tailwind frontend, and a PyAV and sounddevice audio engine behind a single pywebview window.

About your Tidal account

This app is not made by Tidal. It talks to the same private endpoints the official Tidal apps use, but with traffic patterns that can look unusual to Tidal's anti-abuse system. Heavy use, especially mass downloads or rapid browsing, has triggered both soft rate limits and longer "abuse detected" cooldowns.

A soft rate limit pauses playback and search for about a minute. The abuse-detected variant pauses everything for thirty minutes and counts as a strike. Repeated strikes can escalate to account suspension or a permanent ban.

The app throttles itself in normal use and surfaces a banner the moment a backoff engages, but you are using your own account at your own risk. If you cannot accept any chance of a Tidal action against your account, do not use this app.

Tideway

Download for macOS, Windows, or Linux »

Contents

What's inside

Playback. Audio plays through a pipeline built on PyAV for decode and sounddevice for output. Tracks hit the OS audio API at their native sample rate and bit depth. There is no resampling and no hidden software mixer. Transitions between tracks at the same sample rate splice at the PCM sample boundary, so they are truly gapless. Transitions that change sample rate reopen the output stream and bridge across roughly fifty milliseconds, which is the same limitation Tidal desktop and foobar2000 have.

A parametric equalizer runs in three modes. Off bypasses the EQ stage entirely, leaving the audio bit-perfect. Manual is a full parametric EQ: every band carries its own filter type (peaking, low shelf, or high shelf), frequency, gain, and Q, and you add or remove bands freely. You shape them on a live frequency-response graph — drag a node to set frequency and gain, scroll over it to tighten or widen its Q, double-click empty space to drop a new band or a node to delete it — or type exact values in the per-band rows. The curve redraws in real time as you drag, computed with the same RBJ biquad math the audio engine runs. A fresh install seeds six flat bands so there's something to grab, and flat bands compile to nothing, so the audio stays bit-perfect until you actually shape one. Presets render as miniature response curves, a master preamp keeps headroom for boosts, and an A/B bypass flips the whole EQ off and on without losing your bands. Profile loads a per-headphone AutoEQ correction file so your headphones target neutral, Harman, or B&K. Profiles are user-imported (ParametricEQ.txt from AutoEQ or .txt from Equalizer APO), with optional bass, treble, and preamp tilt sliders stacked on top, an A/B bypass to compare on the fly, a frequency-response graph that visualises the cumulative shape, and per-device profile mapping so different headphones plugged into different DACs each apply their own correction automatically. See docs/eq-and-autoeq.md for the full guide.

Three more optional stages sit in Settings under Playback, all off by default so the path stays bit-perfect until you reach for them. Crossfeed bleeds a little of each channel into the opposite ear through a Bauer-style 700 Hz split, which pulls hard-panned mixes out of the middle of your skull and closer to a speaker soundstage. ReplayGain loudness leveling smooths the volume jumps between tracks using the EBU R128 numbers Tidal already ships and that downloaded files carry in their tags, with per-track and per-album modes. A Signal Path readout shows exactly what is happening to the audio right now, from source bit depth and sample rate through each active stage to the output device, so you can confirm a stream really is bit-perfect.

Settings, Playback tab

Parametric EQ editor

AutoEQ profile tone graph

The output device picker lists every USB DAC, Bluetooth sink, and builtin option the OS exposes, plus Chromecast targets, Tidal Connect renderers, and UPnP/DLNA devices discovered on the LAN. You can switch between any of them mid playback. The DLNA path covers WiiM, newer Bluesound, Cambridge, NAD, most network AVRs, and a long tail of cheaper Hi-Fi network bridges. See docs/dlna-renderer.md for the DLNA-side details. Global media keys for play, pause, next, and previous work even when the window is minimized. There is an opt-in desktop notification on every track change.

Browsing and library. Search, a unified Charts page (Popular, Top, Rising, and New Releases as tabs), and dedicated album, artist, playlist, and mix pages are all there. The Home page also surfaces two AlbumOfTheYear-backed discovery rows, a top-of-year highlight reel and a new-releases row, so you can find music that isn't already in heavy rotation. Both open into full pages where you can filter by genre, so pulling up the year's best ambient or this week's new metal is one dropdown away, with the data credited back to AlbumOfTheYear. Album, artist, playlist, and mix pages have Play, Shuffle, and a More menu. Artist pages keep compilations in their own section instead of mixing them into the studio albums, and lead with a Latest releases row. Albums also show a quality badge that reflects the best version Tidal has in its catalog. That includes Max, Lossless, Dolby Atmos, and 360 Reality Audio, though see the limits section below for what the app can actually stream. Playlists can be created, edited, reordered, and extended. Any track in the app has an "Add to playlist" action attached to it. There are dedicated radio pages for artists and tracks. The Stats page is backed by Last.fm and surfaces your top tracks, artists, and albums, along with a listening activity chart that responds to period filters.

Artist page

Data enrichment. Last.fm powers scrobbling and per-user or global playcounts on every track, album, and artist page. Spotify contributes a separate layer of public data through its anonymous GraphQL endpoint. This includes track playcounts, artist monthly listeners, and top listening cities. The two services complement each other. Last.fm gives you your own history, and Spotify fills in the global popularity signal.

Stats page

Downloads. The file format follows the quality tier. High and Max are lossless and download as FLAC. Tidal delivers that FLAC inside an MP4, so Tideway remuxes it into a native .flac file. Low and Medium are lossy AAC and download as .m4a, which is the standard AAC container, not a deprecated format. Music videos download as well. Everything runs through a concurrent queue that you can tune. Metadata and artwork are embedded with mutagen. Any track you have downloaded plays straight from disk without touching the Tidal streaming path. A filename template, toggles for per-album and per-playlist folders, and a skip-existing option cover the common download preferences. A downloaded playlist lands in a folder named after the playlist and is numbered in playlist order rather than by album track number, and the template understands {playlist_num} and {playlist} alongside the usual title, track, album, and artist tokens.

Download quality picker

Downloads queue

On this device. Everything you've downloaded is browsable from a dedicated page that reads tags directly off disk. Music groups by album (or by artist, or sorted by date added), and videos sit on their own tab. Tracks play locally without round-tripping through Tidal's streaming endpoints, so they keep working offline and don't count toward any per-day stream cap.

On this device, music

On this device, videos

Import. You can transfer playlists from Spotify using PKCE OAuth, which requires a Spotify Developer client id that you supply yourself. There is also support for Deezer and for plain M3U or text lists. Liked songs, saved albums, and followed artists all mirror across.

Video. Music videos play through native HLS, powered by hls.js with a CORS proxy that keeps things working inside the Chromium based WebView the packaged app uses. Picture in picture, fullscreen, a quality picker, and subtitle toggles are all supported.

Tidal attribution. Every track you play fires a playback session event at Tidal's event producer bus, in the exact wire format the official tidal-sdk-web uses. That means plays credit the artist and surface in your Tidal Recently Played. There is a diagnostic panel under Settings that shows the outgoing events and the status Tidal returned for each one.

Updates. On launch the app checks GitHub Releases. When a newer version is available a banner surfaces across the top of the UI, and clicking Install downloads the right asset for your OS and runs it.

Support

If Tideway is useful to you and you'd like to support its development, you can buy me a coffee on Ko-fi. Tideway is free and will stay that way; donations are appreciated but never expected.

Support on Ko-fi

Install a released build

Grab the latest from the Releases page for whichever fork of this repo you are installing from.

On macOS, download Tideway-<version>.dmg, double-click it, and drag the Tideway app into /Applications. Apple Silicon only: the binary is arm64-native and won't launch on Intel Macs. This has been the case since the earliest signed release; the README just hadn't called it out before.

On Windows, download Tideway-setup-<version>.exe and run it. The installer drops the app under your user profile, registers a Start Menu entry, and offers an optional desktop shortcut.

On Linux, the recommended install is the Flatpak. It bundles GTK and WebKit2GTK from the GNOME 49 runtime so the app gets a real native window without any host-package fiddling, and flatpak update keeps it current.

# Subscribe to the auto-updating repo (one-time)
flatpak remote-add --user --if-not-exists tideway \
  https://j-m-punk.github.io/tideway/tideway.flatpakrepo

# Install
flatpak install --user tideway com.tidaldownloader.Tideway

# Run
flatpak run com.tidaldownloader.Tideway

# Update later
flatpak update --user com.tidaldownloader.Tideway

If you'd rather not subscribe to a remote, download Tideway-<version>.flatpak from the latest release and install the single-file bundle:

flatpak install --user --bundle Tideway-<version>.flatpak

The bundle won't auto-update — grab a newer one from the release page when you want to upgrade.

Global media keys require an X11 session (Wayland will degrade — the player still works, the global hotkeys do not). ARM Linux (Raspberry Pi etc.) is not built today.

If you were running the v1.10.x or earlier AppImage, install the Flatpak with the commands above to keep getting updates. The AppImage build was retired in v1.11.0 because it had no native window and fell back to opening the app in the system browser.

Why the OS warns you on first launch

The builds are not code signed. Signing costs 99 dollars a year from Apple and upwards of 200 dollars a year from Microsoft, which is not a cost worth paying for an open source hobby project. Both operating systems show one scary looking warning the first time you open an unsigned app. After that they remember the choice and the warning never comes back.

On macOS, right click (or Control click) the .app, pick Open, and confirm Open in the dialog that appears.

On Windows, click More info in the SmartScreen dialog, then Run anyway.

The Linux Flatpak doesn't carry a Gatekeeper-style first-launch warning; the sandbox is the trust boundary.

If you would rather verify the build yourself, clone the repo and follow Run it from source below. PyInstaller produces the same macOS / Windows bundle you download from Releases; the Linux Flatpak is built with flatpak-builder against the manifest at flatpak/com.tidaldownloader.Tideway.yaml.

Stack

The backend runs on FastAPI with tidalapi and mutagen. All mutable state, which includes your settings, your Tidal session, the download queue, and the Spotify and Last.fm caches, lives in a per-user app data directory.

The audio engine is PyAV for demuxing and dec

Extension points exported contracts — how you extend this code

Props (Interface)
* Three-dots overflow menu for a detail page (mix / album / playlist). * Keeps the core actions (Add to queue, Add to p
web/src/components/CollectionOverflowMenu.tsx
AutoEqDeviceRow (Interface)
* Per-device AutoEQ profile mapping — Phase 3 of the scope doc. * * For each output device the user has used, lets the
web/src/pages/SettingsPage.tsx
Biquad (Interface)
Normalized biquad coefficients with a0 folded to 1.
web/src/lib/eqCurve.ts
ReqOptions (Interface)
Optional per-call overrides for `req()`.
web/src/api/client.ts
PlayerMeta (Interface)
* Splits the unified `Player` value into three contexts so consumers only * re-render when the slice they care about ch
web/src/hooks/PlayerContext.tsx

Core symbols most depended-on inside this repo

get
called by 454
app/local_index.py
get
called by 423
server.py
req
called by 196
web/src/api/client.ts
cn
called by 160
web/src/lib/utils.ts
_require_auth
called by 110
server.py
_require_local_access
called by 83
server.py
close
called by 78
app/audio/decoder.py
get
called by 77
app/audio/autoeq/index.py

Shape

Function 2,414
Method 937
Class 295
Route 220
Interface 113

Languages

Python77%
TypeScript23%

Modules by API surface

server.py663 symbols
app/audio/player.py91 symbols
web/src/pages/SettingsPage.tsx81 symbols
tests/test_tidal_connect.py76 symbols
app/downloader.py76 symbols
tests/test_upnp_session.py57 symbols
app/audio/openhome.py55 symbols
app/tidal_client.py54 symbols
app/audio/tidal_connect_real.py52 symbols
web/src/api/types.ts49 symbols
tests/test_http_stream.py49 symbols
tests/test_openhome_soap.py43 symbols

For agents

$ claude mcp add tideway \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page