004 Multi-library Support #54
@@ -74,8 +74,11 @@ impl AppState {
|
||||
let video_playlist_manager =
|
||||
VideoPlaylistManager::new(video_path.clone(), playlist_generator.start());
|
||||
|
||||
let preview_clip_generator =
|
||||
PreviewClipGenerator::new(preview_clips_path.clone(), base_path.clone(), preview_dao);
|
||||
let preview_clip_generator = PreviewClipGenerator::new(
|
||||
preview_clips_path.clone(),
|
||||
libraries_vec.clone(),
|
||||
preview_dao,
|
||||
);
|
||||
|
||||
Self {
|
||||
stream_manager,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::database::PreviewDao;
|
||||
use crate::is_video;
|
||||
use crate::libraries::Library;
|
||||
use crate::otel::global_tracer;
|
||||
use crate::video::ffmpeg::generate_preview_clip;
|
||||
use actix::prelude::*;
|
||||
@@ -500,23 +501,40 @@ pub struct GeneratePreviewClipMessage {
|
||||
pub struct PreviewClipGenerator {
|
||||
semaphore: Arc<Semaphore>,
|
||||
preview_clips_dir: String,
|
||||
base_path: String,
|
||||
libraries: Vec<Library>,
|
||||
preview_dao: Arc<Mutex<Box<dyn PreviewDao>>>,
|
||||
}
|
||||
|
||||
impl PreviewClipGenerator {
|
||||
pub fn new(
|
||||
preview_clips_dir: String,
|
||||
base_path: String,
|
||||
libraries: Vec<Library>,
|
||||
preview_dao: Arc<Mutex<Box<dyn PreviewDao>>>,
|
||||
) -> Self {
|
||||
PreviewClipGenerator {
|
||||
semaphore: Arc::new(Semaphore::new(2)),
|
||||
preview_clips_dir,
|
||||
base_path,
|
||||
libraries,
|
||||
preview_dao,
|
||||
}
|
||||
}
|
||||
|
||||
/// Strip whichever library root actually contains `video_path`.
|
||||
/// Falls back to the first library if none match, so we never
|
||||
/// accidentally emit the absolute input path as the output path
|
||||
/// (which ffmpeg rejects as "cannot edit existing files in place").
|
||||
fn relativize(&self, video_path: &str) -> String {
|
||||
for lib in &self.libraries {
|
||||
if let Some(stripped) = video_path.strip_prefix(&lib.root_path) {
|
||||
return stripped
|
||||
.trim_start_matches(['/', '\\'])
|
||||
.replace('\\', "/");
|
||||
}
|
||||
}
|
||||
video_path
|
||||
.trim_start_matches(['/', '\\'])
|
||||
.replace('\\', "/")
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for PreviewClipGenerator {
|
||||
@@ -533,9 +551,10 @@ impl Handler<GeneratePreviewClipMessage> for PreviewClipGenerator {
|
||||
) -> Self::Result {
|
||||
let semaphore = self.semaphore.clone();
|
||||
let preview_clips_dir = self.preview_clips_dir.clone();
|
||||
let base_path = self.base_path.clone();
|
||||
let preview_dao = self.preview_dao.clone();
|
||||
let video_path = msg.video_path;
|
||||
// Resolve against whichever library actually owns this video.
|
||||
let relative_path = self.relativize(&video_path);
|
||||
|
||||
Box::pin(async move {
|
||||
let permit = semaphore
|
||||
@@ -543,13 +562,6 @@ impl Handler<GeneratePreviewClipMessage> for PreviewClipGenerator {
|
||||
.await
|
||||
.expect("Unable to acquire preview semaphore");
|
||||
|
||||
// Compute relative path (from BASE_PATH) for DB operations, consistent with EXIF convention
|
||||
let relative_path = video_path
|
||||
.strip_prefix(&base_path)
|
||||
.unwrap_or(&video_path)
|
||||
.trim_start_matches(['/', '\\'])
|
||||
.to_string();
|
||||
|
||||
// Update status to processing
|
||||
{
|
||||
let otel_ctx = opentelemetry::Context::current();
|
||||
|
||||
Reference in New Issue
Block a user