kill LibraryItem, unify to MediaItem; decouple schedule engine from providers
ADR-0001: MediaItem absorbs LibraryItem fields (provider_id, external_id, collection_name, collection_type, synced_at, role/MediaRole). LibraryItem + LibraryItemRow deleted. All ports/adapters/tests updated. ADR-0002: schedule engine takes LibraryQuery instead of IProviderRegistry. Algorithmic blocks query library via search(), manual blocks via get_by_id(). get_stream_url removed from engine; provider_registry moved to ScheduleDeps for playback-time stream URL resolution in application layer. BlockContent provider_id field removed (meaningless when querying library).
This commit is contained in:
@@ -4,9 +4,10 @@ use sqlx::SqlitePool;
|
||||
use adapter_common::{content_type_str, parse_content_type, parse_genres_blob};
|
||||
use domain::{
|
||||
ports::library::{LibraryCommand, LibraryQuery},
|
||||
ContentType, DomainError, DomainResult, LibraryCollection, LibraryItem,
|
||||
LibraryItemRow as DomainLibraryItemRow, LibrarySearchFilter, LibrarySyncLogEntry,
|
||||
LibrarySyncResult, SeasonSummary, ShowSummary,
|
||||
ContentType, DomainError, DomainResult, LibraryCollection,
|
||||
LibrarySearchFilter, LibrarySyncLogEntry,
|
||||
LibrarySyncResult, MediaItem, MediaItemRow as DomainMediaItemRow,
|
||||
MediaRole, SeasonSummary, ShowSummary,
|
||||
};
|
||||
|
||||
pub struct SqliteLibraryRepository {
|
||||
@@ -41,14 +42,15 @@ struct LibraryItemRow {
|
||||
}
|
||||
|
||||
impl LibraryItemRow {
|
||||
fn into_library_item(self) -> LibraryItem {
|
||||
LibraryItem::from_persistence(DomainLibraryItemRow {
|
||||
id: self.id,
|
||||
fn into_media_item(self) -> MediaItem {
|
||||
MediaItem::from_persistence(DomainMediaItemRow {
|
||||
id: domain::MediaItemId::new(&self.id),
|
||||
provider_id: self.provider_id,
|
||||
external_id: self.external_id,
|
||||
title: self.title,
|
||||
content_type: parse_content_type(&self.content_type),
|
||||
duration_secs: self.duration_secs as u32,
|
||||
description: None,
|
||||
series_name: self.series_name,
|
||||
season_number: self.season_number.map(|n| n as u32),
|
||||
episode_number: self.episode_number.map(|n| n as u32),
|
||||
@@ -59,7 +61,8 @@ impl LibraryItemRow {
|
||||
collection_name: self.collection_name,
|
||||
collection_type: self.collection_type,
|
||||
thumbnail_url: self.thumbnail_url,
|
||||
synced_at: self.synced_at,
|
||||
synced_at: Some(self.synced_at),
|
||||
role: MediaRole::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -93,7 +96,7 @@ struct SeasonSummaryRow {
|
||||
|
||||
#[async_trait]
|
||||
impl LibraryCommand for SqliteLibraryRepository {
|
||||
async fn upsert_items(&self, _provider_id: &str, items: Vec<LibraryItem>) -> DomainResult<()> {
|
||||
async fn upsert_items(&self, _provider_id: &str, items: Vec<MediaItem>) -> DomainResult<()> {
|
||||
let mut tx = self
|
||||
.pool
|
||||
.begin()
|
||||
@@ -108,7 +111,7 @@ impl LibraryCommand for SqliteLibraryRepository {
|
||||
collection_id, collection_name, collection_type, thumbnail_url, synced_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
|
||||
)
|
||||
.bind(item.id())
|
||||
.bind(item.id().value())
|
||||
.bind(item.provider_id())
|
||||
.bind(item.external_id())
|
||||
.bind(item.title())
|
||||
@@ -124,7 +127,7 @@ impl LibraryCommand for SqliteLibraryRepository {
|
||||
.bind(item.collection_name())
|
||||
.bind(item.collection_type())
|
||||
.bind(item.thumbnail_url())
|
||||
.bind(item.synced_at())
|
||||
.bind(item.synced_at().unwrap_or(""))
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?;
|
||||
@@ -187,7 +190,7 @@ impl LibraryQuery for SqliteLibraryRepository {
|
||||
async fn search(
|
||||
&self,
|
||||
filter: &LibrarySearchFilter,
|
||||
) -> DomainResult<(Vec<LibraryItem>, u32)> {
|
||||
) -> DomainResult<(Vec<MediaItem>, u32)> {
|
||||
let mut conditions: Vec<String> = vec![];
|
||||
|
||||
if let Some(p) = filter.provider_id() {
|
||||
@@ -263,19 +266,19 @@ impl LibraryQuery for SqliteLibraryRepository {
|
||||
|
||||
Ok((
|
||||
rows.into_iter()
|
||||
.map(LibraryItemRow::into_library_item)
|
||||
.map(LibraryItemRow::into_media_item)
|
||||
.collect(),
|
||||
total as u32,
|
||||
))
|
||||
}
|
||||
|
||||
async fn get_by_id(&self, id: &str) -> DomainResult<Option<LibraryItem>> {
|
||||
async fn get_by_id(&self, id: &str) -> DomainResult<Option<MediaItem>> {
|
||||
let row = sqlx::query_as::<_, LibraryItemRow>("SELECT * FROM library_items WHERE id = ?")
|
||||
.bind(id)
|
||||
.fetch_optional(&self.pool)
|
||||
.await
|
||||
.map_err(|e| DomainError::InfrastructureError(e.to_string()))?;
|
||||
Ok(row.map(LibraryItemRow::into_library_item))
|
||||
Ok(row.map(LibraryItemRow::into_media_item))
|
||||
}
|
||||
|
||||
async fn list_collections(
|
||||
|
||||
Reference in New Issue
Block a user