refactor: replace get_stream_url with get_source_uri per ADR-0002

Providers now expose a source URI (for FFmpeg) instead of a
viewer-facing stream URL. Playout Service will own transcoding.

- Add SourceUri value object (NetworkUrl | FilePath)
- Remove StreamQuality, StreamingProtocol from domain
- Simplify ProviderCapabilities (drop streaming_protocol, transcode)
- Jellyfin: return static direct stream URL
- Local files: return absolute file path
- Rename use case get_stream_url -> get_source
- Stream endpoint returns JSON SourceUri instead of 307 redirect
This commit is contained in:
2026-07-12 07:34:13 +02:00
parent abcf69ce7e
commit 39f5f99bfd
21 changed files with 82 additions and 256 deletions

View File

@@ -41,9 +41,7 @@ pub fn execute(deps: &ConfigDeps, _query: GetConfigQuery) -> SystemConfig {
tags: false,
decade: false,
search: false,
streaming_protocol: domain::ports::StreamingProtocol::DirectFile,
rescan: false,
transcode: false,
});
SystemConfig {

View File

@@ -4,10 +4,8 @@ use async_trait::async_trait;
use domain::errors::DomainResult;
use domain::models::MediaItem;
use domain::ports::{
Collection, IProviderRegistry, ProviderCapabilities, SeriesSummary, StreamQuality,
StreamingProtocol,
};
use domain::ports::{Collection, IProviderRegistry, ProviderCapabilities, SeriesSummary};
use domain::SourceUri;
use domain::testing::{InMemoryLibraryRepository, NoopEventPublisher, NoopLibrarySync};
use domain::value_objects::{ContentType, MediaFilter, MediaItemId};
@@ -29,11 +27,7 @@ impl IProviderRegistry for TestProviderRegistry {
Ok(None)
}
async fn get_stream_url(
&self,
_item_id: &MediaItemId,
_quality: &StreamQuality,
) -> DomainResult<String> {
async fn get_source_uri(&self, _item_id: &MediaItemId) -> DomainResult<SourceUri> {
Err(domain::DomainError::InfrastructureError(
"TestProviderRegistry does not support streaming".into(),
))
@@ -55,9 +49,7 @@ impl IProviderRegistry for TestProviderRegistry {
tags: false,
decade: false,
search: true,
streaming_protocol: StreamingProtocol::Hls,
rescan: false,
transcode: false,
})
}

View File

@@ -1,13 +1,12 @@
use chrono::Utc;
use domain::ports::StreamQuality;
use domain::value_objects::ChannelId;
use domain::value_objects::{ChannelId, SourceUri};
use domain::{DomainResult, ScheduleEngineService};
use super::deps::ScheduleDeps;
use super::queries::GetStreamUrlQuery;
use super::queries::GetSourceQuery;
pub async fn execute(deps: &ScheduleDeps, query: GetStreamUrlQuery) -> DomainResult<Option<String>> {
pub async fn execute(deps: &ScheduleDeps, query: GetSourceQuery) -> DomainResult<Option<SourceUri>> {
let channel_id = ChannelId::from(query.channel_id);
let now = Utc::now();
@@ -22,9 +21,9 @@ pub async fn execute(deps: &ScheduleDeps, query: GetStreamUrlQuery) -> DomainRes
};
let item_id = broadcast.slot().item().id().clone();
let url = deps
let uri = deps
.provider_registry
.get_stream_url(&item_id, &StreamQuality::Direct)
.get_source_uri(&item_id)
.await?;
Ok(Some(url))
Ok(Some(uri))
}

View File

@@ -3,9 +3,9 @@ pub mod deps;
pub mod generate;
pub mod get_current_broadcast;
pub mod get_epg;
pub mod get_stream_url;
pub mod get_source;
pub mod queries;
pub use commands::GenerateScheduleCommand;
pub use deps::ScheduleDeps;
pub use queries::{GetCurrentBroadcastQuery, GetEpgQuery, GetStreamUrlQuery};
pub use queries::{GetCurrentBroadcastQuery, GetEpgQuery, GetSourceQuery};

View File

@@ -8,6 +8,6 @@ pub struct GetEpgQuery {
pub channel_id: Uuid,
}
pub struct GetStreamUrlQuery {
pub struct GetSourceQuery {
pub channel_id: Uuid,
}

View File

@@ -4,10 +4,8 @@ use async_trait::async_trait;
use domain::errors::DomainResult;
use domain::models::MediaItem;
use domain::ports::{
Collection, IProviderRegistry, ProviderCapabilities,
SeriesSummary, StreamQuality, StreamingProtocol,
};
use domain::ports::{Collection, IProviderRegistry, ProviderCapabilities, SeriesSummary};
use domain::SourceUri;
use domain::testing::{InMemoryChannelRepository, InMemoryLibraryRepository, InMemoryScheduleRepository, NoopEventPublisher};
use domain::value_objects::{ContentType, MediaFilter, MediaItemId};
use domain::ScheduleEngineService;
@@ -30,11 +28,7 @@ impl IProviderRegistry for TestProviderRegistry {
Ok(None)
}
async fn get_stream_url(
&self,
_item_id: &MediaItemId,
_quality: &StreamQuality,
) -> DomainResult<String> {
async fn get_source_uri(&self, _item_id: &MediaItemId) -> DomainResult<SourceUri> {
Err(domain::DomainError::InfrastructureError(
"TestProviderRegistry does not support streaming".into(),
))
@@ -56,9 +50,7 @@ impl IProviderRegistry for TestProviderRegistry {
tags: false,
decade: false,
search: false,
streaming_protocol: StreamingProtocol::Hls,
rescan: false,
transcode: false,
})
}