Repository navigation
Dispatcharr Phase 1: native API source loading #201
Copy link
Copy link
Closed
Labels
enhancementNew feature or requestNew feature or request
Description
Part of #200. Fully shippable on its own (load + scan Dispatcharr source streams, read-only).
New src-tauri/src/engine/dispatcharr.rs (register in engine/mod.rs)
DispatcharrClient— normalize base URL likenormalize_xtream_server(playlist.rs:775); reqwest client with the same knobs as the playlist downloader.ensure_token(): API-key header if set, else JWT login → cache access token (expiry now+25 min) → refresh on expiry → full re-login on 401;request()retries once on 401.- Serde-tolerant DTOs:
DispatcharrChannel { id, uuid, channel_number, name, channel_group, tvg_id, logo, streams },DispatcharrStream { id, name, url, m3u_account, is_custom, tvg_id, is_stale, stream_stats, ... },DispatcharrGroup. fetch_all<T>()handling both bare arrays and DRF{count, next, results}pagination (untagged enum), rewritingnexthost onto the base URL (reverse-proxy gotcha), hard page ceiling (500). Fetchers: channels, per-channel streams, groups, m3u accounts (accounts tolerate failure).build_m3u(...): one#EXTINFper stream; name = channel name, or"{name} [{order+1}/{count}] {account-or-stream-name}"for multi-stream channels; attrstvg-id,tvg-chno,tvg-logo({base}/api/channels/logos/{id}/cache/),group-title, plus the fivex-dispatcharr-*attrs (escape quotes). URL line = providerstream.url.dispatcharr_ids_from_extinf(&str) -> Option<DispatcharrStreamRef>built onparse_extinf_attributes.- Session registry:
register_session(identity, Arc<DispatcharrClient>)/get_session(identity)(same pattern asSERVER_LOCATION_CACHE). - Write-op signatures used later by Dispatcharr Phase 2: scan source streams #202:
patch_stream_stats,set_channel_streams,bulk_delete_streams.
Modified src-tauri/src/commands/playlist.rs
DispatcharrOpenRequest { server, username, password, api_key }(next toXtreamOpenRequest).open_playlist_dispatcharr+_innermirroringopen_playlist_xtream_inner: validate (api_key OR user+pass) → source keydispatcharr:{host}|{user}(mirrorbuild_xtream_source_key) → emitplaylist://load-progressstages → fetch channels/groups/accounts viatokio::join!, per-channel streams withbuffer_unordered(8)→build_m3u→write_bytes_to_cache→parse_playlist_with_progress→ setpreview.file_name = "{host} (Dispatcharr)"andpreview.source_identity→register_session.
Saved/recent/wiring
SavedPlaylistSource::Dispatcharr { server, username?, password?, api_key? }variant +SavedPlaylistKindarm (models/saved_playlist.rs); arms incommands/saved.rs(default_display_name,sanitize_saved_playlist_entry,open_saved_playlistwith identity overridesaved:{id});RecentPlaylistKind::Dispatcharrincommands/recent.rs(secrets only if "remember" checked); register the command inlib.rsinvoke_handler.
Frontend
src/lib/types.ts+src/lib/tauri.ts: request type +openPlaylistDispatcharrwrapper.src/components/OpenSourceDialog.tsx: new "Dispatcharr" tab (server URL, API-key vs username/password toggle, remember-credentials); autofill server from pasted/proxy/ts/stream/or/output/m3uURLs (mirrorparseXtreamM3ULink).src/App.tsx:openPlaylistDispatcharrValuecallback modeled onopenPlaylistXtreamValue.SavedPlaylistEditorDialog.tsx+src/lib/savedPlaylists.ts: dispatcharr form branch.
Tests
engine/dispatcharr.rsunit tests using thespawn_http_serverpattern from checker.rs, extended to capture method+body: JWT login/refresh/401-retry, API-key header, pagination (paginated + bare array + host rewrite).- Pure tests:
build_m3u→parser::parse_m3u→dispatcharr_ids_from_extinfround-trip (the load-bearing invariant); attr escaping. SavedPlaylistSource::Dispatcharrserde round-trip; legacy JSON still parses.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
kristofferR commentedon Sep 30, 2026
Implemented in #248.
feat(dispatcharr): load Dispatcharr sources via its API with write-ba…