Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promote experimental changes #360

Merged
merged 6 commits into from
Jul 12, 2024
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
128 changes: 70 additions & 58 deletions apps/server/src/routers/api/v1/epub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@ use axum::{
routing::{get, put},
Json, Router,
};
use prisma_client_rust::chrono::Utc;
use serde::Deserialize;
use specta::Type;
use stump_core::{
db::entity::{Bookmark, Epub, ReadProgress, UpdateEpubProgress},
db::entity::{
ActiveReadingSession, Bookmark, Epub, FinishedReadingSession,
ProgressUpdateReturn, UpdateEpubProgress,
},
filesystem::media::EpubProcessor,
prisma::{bookmark, media, media_annotation, read_progress, user},
prisma::{
active_reading_session, bookmark, finished_reading_session, media,
media_annotation, user,
},
};
use tower_sessions::Session;
use utoipa::ToSchema;
Expand Down Expand Up @@ -43,7 +48,7 @@ pub(crate) fn mount(app_state: AppState) -> Router<AppState> {
.layer(from_extractor_with_state::<Auth, AppState>(app_state))
}

/// Get an Epub by ID. The `read_progress` relation is loaded.
/// Get an Epub by ID
async fn get_epub_by_id(
Path(id): Path<String>,
State(ctx): State<AppState>,
Expand All @@ -55,8 +60,11 @@ async fn get_epub_by_id(
.db
.media()
.find_unique(media::id::equals(id.clone()))
.with(media::read_progresses::fetch(vec![
read_progress::user_id::equals(user_id.clone()),
.with(media::active_user_reading_sessions::fetch(vec![
active_reading_session::user_id::equals(user_id.clone()),
]))
.with(media::finished_user_reading_sessions::fetch(vec![
finished_reading_session::user_id::equals(user_id.clone()),
]))
.with(media::annotations::fetch(vec![
media_annotation::user_id::equals(user_id),
Expand All @@ -82,68 +90,72 @@ async fn update_epub_progress(
State(ctx): State<AppState>,
session: Session,
Json(input): Json<UpdateEpubProgress>,
) -> APIResult<Json<ReadProgress>> {
let db = &ctx.db;
) -> APIResult<Json<ProgressUpdateReturn>> {
let client = &ctx.db;
let user_id = get_session_user(&session)?.id;

let input_is_complete = input.is_complete.unwrap_or(input.percentage >= 1.0);
let input_completed_at = input_is_complete.then(|| Utc::now().into());

// NOTE: I am running this in a transaction with 2 queries because I don't want to update the
// is_complete and completed_at unless a book is *newly* completed.
// TODO: I will eventually add a way to set a book as uncompleted
let progress = db
._transaction()
.run(|client| async move {
let existing_progress = client
.read_progress()
.find_unique(read_progress::user_id_media_id(user_id.clone(), id.clone()))
.exec()
.await?;

if let Some(progress) = existing_progress {
let already_completed = progress.is_completed;
let is_completed = already_completed || input_is_complete;
let completed_at = progress.completed_at.or(input_completed_at);
let is_complete = input.is_complete.unwrap_or(input.percentage >= 1.0);

client
.read_progress()
.update(
read_progress::user_id_media_id(user_id.clone(), id.clone()),
vec![
read_progress::epubcfi::set(Some(input.epubcfi)),
read_progress::is_completed::set(is_completed),
read_progress::percentage_completed::set(Some(
input.percentage,
)),
read_progress::completed_at::set(completed_at),
],
)
if is_complete {
let finished_session = client
._transaction()
.run(|tx| async move {
let deleted_session = tx
.active_reading_session()
.delete(active_reading_session::user_id_media_id(
user_id.clone(),
id.clone(),
))
.exec()
.await
} else {
client
.read_progress()
.ok();
tracing::trace!(?deleted_session, "Deleted active reading session");

tx.finished_reading_session()
.create(
-1,
media::id::equals(id),
user::id::equals(user_id),
vec![
read_progress::epubcfi::set(Some(input.epubcfi)),
read_progress::is_completed::set(input_is_complete),
read_progress::percentage_completed::set(Some(
input.percentage,
)),
read_progress::completed_at::set(input_completed_at),
],
deleted_session.map(|s| s.started_at).unwrap_or_default(),
media::id::equals(id.clone()),
user::id::equals(user_id.clone()),
vec![],
)
.exec()
.await
}
})
.await?;
})
.await?;
tracing::trace!(?finished_session, "Created finished reading session");

Ok(Json(ReadProgress::from(progress)))
Ok(Json(ProgressUpdateReturn::Finished(
FinishedReadingSession::from(finished_session),
)))
} else {
let active_session = client
.active_reading_session()
.upsert(
active_reading_session::user_id_media_id(user_id.clone(), id.clone()),
(
media::id::equals(id.clone()),
user::id::equals(user_id.clone()),
vec![
active_reading_session::epubcfi::set(Some(input.epubcfi.clone())),
active_reading_session::percentage_completed::set(Some(
input.percentage,
)),
],
),
vec![
active_reading_session::epubcfi::set(Some(input.epubcfi)),
active_reading_session::percentage_completed::set(Some(
input.percentage,
)),
],
)
.exec()
.await?;

Ok(Json(ProgressUpdateReturn::Active(
ActiveReadingSession::from(active_session),
)))
}
}

async fn get_bookmarks(
Expand Down
17 changes: 8 additions & 9 deletions apps/server/src/routers/api/v1/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use stump_core::{
PrismaCountTrait,
},
filesystem::{
analyze_media_job::{AnalyzeMediaJob, AnalyzeMediaJobVariant},
analyze_media_job::AnalyzeMediaJob,
get_unknown_thumnail,
image::{
self, generate_thumbnail, place_thumbnail, remove_thumbnails,
Expand Down Expand Up @@ -294,6 +294,7 @@ pub struct LibraryStatsParams {
all_users: bool,
}

// TODO(historical-read-session): refactor query
#[utoipa::path(
get,
path = "/api/v1/libraries/stats",
Expand Down Expand Up @@ -1752,14 +1753,12 @@ async fn start_media_analysis(
let _ = enforce_session_permissions(&session, &[UserPermission::ManageLibrary])?;

// Start analysis job
ctx.enqueue_job(AnalyzeMediaJob::new(
AnalyzeMediaJobVariant::AnalyzeLibrary(id),
))
.map_err(|e| {
let err = "Failed to enqueue analyze library media job";
error!(?e, err);
APIError::InternalServerError(err.to_string())
})?;
ctx.enqueue_job(AnalyzeMediaJob::analyze_library(id))
.map_err(|e| {
let err = "Failed to enqueue analyze library media job";
error!(?e, err);
APIError::InternalServerError(err.to_string())
})?;

APIResult::Ok(())
}
Loading
Loading