Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions crates/apotheke/src/repo/audiobook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,15 @@ pub async fn list_audiobooks(
})
}

pub async fn count_audiobooks(pool: &SqlitePool) -> Result<i64, DbError> {
sqlx::query_scalar("SELECT COUNT(*) FROM audiobooks")
.fetch_one(pool)
.await
.context(QuerySnafu {
table: "audiobooks",
})
}

pub async fn update_audiobook(
pool: &SqlitePool,
id: &[u8],
Expand Down
39 changes: 39 additions & 0 deletions crates/apotheke/src/repo/book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ pub async fn list_books(pool: &SqlitePool, limit: i64, offset: i64) -> Result<Ve
.context(QuerySnafu { table: "books" })
}

pub async fn count_books(pool: &SqlitePool) -> Result<i64, DbError> {
sqlx::query_scalar("SELECT COUNT(*) FROM books")
.fetch_one(pool)
.await
.context(QuerySnafu { table: "books" })
}

/// Distinct book author names, sorted, paginated.
///
/// WHY: authors live in `media_registry` (entity_type = 'person') joined
Expand Down Expand Up @@ -243,4 +250,36 @@ mod tests {
let err = delete_book(&pool, &make_id()).await.unwrap_err();
assert!(matches!(err, DbError::NotFound { .. }));
}

#[tokio::test]
async fn count_books_returns_total_row_count() {
let pool = setup().await;
assert_eq!(count_books(&pool).await.unwrap(), 0);
for i in 0..5 {
let book = Book {
id: make_id(),
registry_id: None,
title: format!("Book {i}"),
subtitle: None,
isbn: None,
isbn13: None,
openlibrary_id: None,
goodreads_id: None,
publisher: None,
publish_date: None,
language: None,
page_count: None,
description: None,
file_path: None,
file_format: None,
file_size_bytes: None,
quality_score: None,
quality_profile_id: None,
source_type: "local".to_string(),
added_at: "2026-01-01T00:00:00Z".to_string(),
};
insert_book(&pool, &book).await.unwrap();
}
assert_eq!(count_books(&pool).await.unwrap(), 5);
}
}
7 changes: 7 additions & 0 deletions crates/apotheke/src/repo/comic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ pub async fn list_comics(
.context(QuerySnafu { table: "comics" })
}

pub async fn count_comics(pool: &SqlitePool) -> Result<i64, DbError> {
sqlx::query_scalar("SELECT COUNT(*) FROM comics")
.fetch_one(pool)
.await
.context(QuerySnafu { table: "comics" })
}

pub async fn update_comic(
pool: &SqlitePool,
id: &[u8],
Expand Down
39 changes: 39 additions & 0 deletions crates/apotheke/src/repo/movie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ pub async fn list_movies(
.context(QuerySnafu { table: "movies" })
}

pub async fn count_movies(pool: &SqlitePool) -> Result<i64, DbError> {
sqlx::query_scalar("SELECT COUNT(*) FROM movies")
.fetch_one(pool)
.await
.context(QuerySnafu { table: "movies" })
}

pub async fn update_movie(
pool: &SqlitePool,
id: &[u8],
Expand Down Expand Up @@ -194,4 +201,36 @@ mod tests {
let err = delete_movie(&pool, &make_id()).await.unwrap_err();
assert!(matches!(err, DbError::NotFound { .. }));
}

#[tokio::test]
async fn count_movies_returns_total_row_count() {
let pool = setup().await;
assert_eq!(count_movies(&pool).await.unwrap(), 0);
for i in 0..5 {
let movie = Movie {
id: make_id(),
registry_id: None,
title: format!("Movie {i}"),
original_title: None,
year: Some(2020),
tmdb_id: None,
imdb_id: None,
runtime_min: None,
overview: None,
certification: None,
file_path: None,
file_format: None,
file_size_bytes: None,
resolution: None,
codec: None,
hdr_type: None,
quality_score: None,
quality_profile_id: None,
source_type: "local".to_string(),
added_at: "2026-01-01T00:00:00Z".to_string(),
};
insert_movie(&pool, &movie).await.unwrap();
}
assert_eq!(count_movies(&pool).await.unwrap(), 5);
}
}
29 changes: 29 additions & 0 deletions crates/apotheke/src/repo/music.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ pub async fn list_release_groups(
})
}

pub async fn count_release_groups(pool: &SqlitePool) -> Result<i64, DbError> {
sqlx::query_scalar("SELECT COUNT(*) FROM music_release_groups")
.fetch_one(pool)
.await
.context(QuerySnafu {
table: "music_release_groups",
})
}

pub async fn update_release_group(
pool: &SqlitePool,
id: &[u8],
Expand Down Expand Up @@ -1051,4 +1060,24 @@ mod tests {
assert_eq!(count_tracks(&pool, "Track 03").await.unwrap(), 1);
assert_eq!(count_tracks(&pool, "No Such Title").await.unwrap(), 0);
}

#[tokio::test]
async fn count_release_groups_returns_total_row_count() {
let pool = setup().await;
assert_eq!(count_release_groups(&pool).await.unwrap(), 0);
for i in 0..4 {
let group = MusicReleaseGroup {
id: make_id(),
registry_id: None,
title: format!("Album {i}"),
rg_type: "album".to_string(),
mb_release_group_id: None,
year: Some(1971),
quality_profile_id: None,
added_at: now(),
};
insert_release_group(&pool, &group).await.unwrap();
}
assert_eq!(count_release_groups(&pool).await.unwrap(), 4);
}
}
9 changes: 9 additions & 0 deletions crates/apotheke/src/repo/news.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ pub async fn list_feeds(
})
}

pub async fn count_feeds(pool: &SqlitePool) -> Result<i64, DbError> {
sqlx::query_scalar("SELECT COUNT(*) FROM news_feeds")
.fetch_one(pool)
.await
.context(QuerySnafu {
table: "news_feeds",
})
}

pub async fn update_feed(
pool: &SqlitePool,
id: &[u8],
Expand Down
9 changes: 9 additions & 0 deletions crates/apotheke/src/repo/podcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ pub async fn list_subscriptions(
})
}

pub async fn count_subscriptions(pool: &SqlitePool) -> Result<i64, DbError> {
sqlx::query_scalar("SELECT COUNT(*) FROM podcast_subscriptions")
.fetch_one(pool)
.await
.context(QuerySnafu {
table: "podcast_subscriptions",
})
}

pub async fn update_subscription(
pool: &SqlitePool,
id: &[u8],
Expand Down
7 changes: 7 additions & 0 deletions crates/apotheke/src/repo/tv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ pub async fn list_series(
.context(QuerySnafu { table: "tv_series" })
}

pub async fn count_series(pool: &SqlitePool) -> Result<i64, DbError> {
sqlx::query_scalar("SELECT COUNT(*) FROM tv_series")
.fetch_one(pool)
.await
.context(QuerySnafu { table: "tv_series" })
}

pub async fn update_series(
pool: &SqlitePool,
id: &[u8],
Expand Down
7 changes: 7 additions & 0 deletions crates/apotheke/src/repo/want.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ pub async fn list_wants(pool: &SqlitePool, limit: i64, offset: i64) -> Result<Ve
.context(QuerySnafu { table: "wants" })
}

pub async fn count_wants(pool: &SqlitePool) -> Result<i64, DbError> {
sqlx::query_scalar("SELECT COUNT(*) FROM wants")
.fetch_one(pool)
.await
.context(QuerySnafu { table: "wants" })
}

pub async fn list_wants_by_type_and_status(
pool: &SqlitePool,
media_type: &str,
Expand Down
2 changes: 1 addition & 1 deletion crates/paroche/src/routes/audiobook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub async fn list_audiobooks(
apotheke::repo::audiobook::list_audiobooks(&state.db.read, per_page as i64, offset as i64)
.await?;

let total = books.len() as u64;
let total = apotheke::repo::audiobook::count_audiobooks(&state.db.read).await? as u64;
let data: Vec<AudiobookResponse> = books.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/paroche/src/routes/book.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub async fn list_books(
)
.await?;

let total = books.len() as u64;
let total = apotheke::repo::book::count_books(&state.db.read).await? as u64;
let data: Vec<BookResponse> = books.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/paroche/src/routes/comic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub async fn list_comics(
)
.await?;

let total = comics.len() as u64;
let total = apotheke::repo::comic::count_comics(&state.db.read).await? as u64;
let data: Vec<ComicResponse> = comics.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
}
Expand Down
8 changes: 6 additions & 2 deletions crates/paroche/src/routes/indexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,13 @@ pub async fn list_indexers(
.await
.map_err(|_| ParocheError::Internal)?;

let total = rows.len() as u64;
let total: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM indexers")
.fetch_one(&state.db.read)
.await
.map_err(|_| ParocheError::Internal)?;

let data: Vec<IndexerResponse> = rows.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
Ok(ApiResponse::paginated(data, page, per_page, total as u64))
}

pub async fn get_indexer(
Expand Down
2 changes: 1 addition & 1 deletion crates/paroche/src/routes/movie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub async fn list_movies(
let movies =
apotheke::repo::movie::list_movies(&state.db.read, per_page as i64, offset as i64).await?;

let total = movies.len() as u64;
let total = apotheke::repo::movie::count_movies(&state.db.read).await? as u64;
let data: Vec<MovieResponse> = movies.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/paroche/src/routes/music.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub async fn list_release_groups(
apotheke::repo::music::list_release_groups(&state.db.read, per_page as i64, offset as i64)
.await?;

let total = groups.len() as u64;
let total = apotheke::repo::music::count_release_groups(&state.db.read).await? as u64;
let data: Vec<ReleaseGroupResponse> = groups.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/paroche/src/routes/news.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub async fn list_feeds(
)
.await?;

let total = feeds.len() as u64;
let total = apotheke::repo::news::count_feeds(&state.db.read).await? as u64;
let data: Vec<FeedResponse> = feeds.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/paroche/src/routes/podcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub async fn list_subscriptions(
apotheke::repo::podcast::list_subscriptions(&state.db.read, per_page as i64, offset as i64)
.await?;

let total = subs.len() as u64;
let total = apotheke::repo::podcast::count_subscriptions(&state.db.read).await? as u64;
let data: Vec<SubscriptionResponse> = subs.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/paroche/src/routes/tv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ pub async fn list_series(
let series =
apotheke::repo::tv::list_series(&state.db.read, per_page as i64, offset as i64).await?;

let total = series.len() as u64;
let total = apotheke::repo::tv::count_series(&state.db.read).await? as u64;
let data: Vec<TvSeriesResponse> = series.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/paroche/src/routes/wanted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ pub async fn list_wanted(
)
.await?;

let total = wants.len() as u64;
let total = apotheke::repo::want::count_wants(&state.db.read).await? as u64;
let data: Vec<WantedResponse> = wants.into_iter().map(Into::into).collect();
Ok(ApiResponse::paginated(data, page, per_page, total))
}
Expand Down