feat: Add API endpoint for listing oneliners
This commit introduces a new API endpoint `/api/oneliners` that allows authenticated users to retrieve a list of their recent oneliners. The endpoint supports conditional GET requests using the `ETag` header for efficient caching. It also allows specifying a time window for the oneliners via the `hours` query parameter, with sensible defaults and clamping. The implementation includes: - A new route handler `handle_oneliners`. - A new type `OnelinerWatermark` for tracking user-specific timestamps. - Logic for scanning user data directories, filtering by time, and handling deleted cases. - Test cases to cover various scenarios, including caching, time windowing, and edge cases.
This commit is contained in:
@@ -2,12 +2,13 @@ use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use time::OffsetDateTime;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
use super::{ffmpeg, ollama, whisper, TranscribeReceiver};
|
||||
use crate::config::{Config, WhisperUserSettings};
|
||||
use crate::gazetteer::Gazetteer;
|
||||
use crate::{BusyGuard, WorkerBusy};
|
||||
use crate::{BusyGuard, OnelinerWatermark, WorkerBusy};
|
||||
|
||||
const ONELINER_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
|
||||
@@ -19,6 +20,7 @@ pub async fn run(
|
||||
client: reqwest::Client,
|
||||
worker_busy: WorkerBusy,
|
||||
vocab: Arc<Gazetteer>,
|
||||
watermark: OnelinerWatermark,
|
||||
) {
|
||||
info!("Transcription worker started");
|
||||
let timeout = Duration::from_secs(config.whisper_timeout_seconds);
|
||||
@@ -88,7 +90,7 @@ pub async fn run(
|
||||
if let Some(case_dir) = audio_path.parent()
|
||||
&& !has_pending_recordings(case_dir).await
|
||||
{
|
||||
update_oneliner(case_dir, &client, &config, &vocab).await;
|
||||
update_oneliner(case_dir, &job.user_slug, &client, &config, &vocab, &watermark).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,9 +129,11 @@ async fn mark_failed(audio_path: &Path) {
|
||||
/// no-op — next transcript write retries.
|
||||
pub(crate) async fn update_oneliner(
|
||||
case_dir: &Path,
|
||||
user_slug: &str,
|
||||
client: &reqwest::Client,
|
||||
config: &Config,
|
||||
vocab: &Gazetteer,
|
||||
watermark: &OnelinerWatermark,
|
||||
) {
|
||||
let path = case_dir.join("oneliner.txt");
|
||||
|
||||
@@ -154,6 +158,12 @@ pub(crate) async fn update_oneliner(
|
||||
error!(path = %path.display(), error = %e, "writing oneliner failed");
|
||||
} else {
|
||||
info!(path = %path.display(), chars = line.chars().count(), "Oneliner updated");
|
||||
// Bump the per-user watermark so the next `/api/oneliners`
|
||||
// poll sees a fresh ETag and fetches the new content.
|
||||
watermark
|
||||
.write()
|
||||
.await
|
||||
.insert(user_slug.to_string(), OffsetDateTime::now_utc());
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
|
||||
Reference in New Issue
Block a user