Merge branch 'master' into feature/video-gifs
This commit is contained in:
89
src/files.rs
89
src/files.rs
@@ -9,18 +9,20 @@ use ::anyhow;
|
||||
use actix::{Handler, Message};
|
||||
use anyhow::{anyhow, Context};
|
||||
|
||||
use crate::data::{Claims, FilesRequest, FilterMode, PhotosResponse, SortType};
|
||||
use crate::{create_thumbnails, AppState};
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{
|
||||
web::{self, Query},
|
||||
HttpResponse,
|
||||
HttpRequest, HttpResponse,
|
||||
};
|
||||
use log::{debug, error, info, trace};
|
||||
|
||||
use crate::data::{Claims, FilesRequest, FilterMode, PhotosResponse, SortType};
|
||||
use crate::{create_thumbnails, AppState};
|
||||
use opentelemetry::trace::{Span, Status, TraceContextExt, Tracer};
|
||||
use opentelemetry::KeyValue;
|
||||
|
||||
use crate::data::SortType::NameAsc;
|
||||
use crate::error::IntoHttpError;
|
||||
use crate::otel::{extract_context_from_request, global_tracer};
|
||||
use crate::tags::{FileWithTagCount, TagDao};
|
||||
use crate::video::actors::StreamActor;
|
||||
use path_absolutize::*;
|
||||
@@ -30,6 +32,7 @@ use serde::Deserialize;
|
||||
|
||||
pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
|
||||
_: Claims,
|
||||
request: HttpRequest,
|
||||
req: Query<FilesRequest>,
|
||||
app_state: web::Data<AppState>,
|
||||
file_system: web::Data<FS>,
|
||||
@@ -37,11 +40,34 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
|
||||
) -> HttpResponse {
|
||||
let search_path = &req.path;
|
||||
|
||||
let tracer = global_tracer();
|
||||
let context = extract_context_from_request(&request);
|
||||
let mut span = tracer.start_with_context("list_photos", &context);
|
||||
span.set_attributes(vec![
|
||||
KeyValue::new("path", search_path.to_string()),
|
||||
KeyValue::new("recursive", req.recursive.unwrap_or(false).to_string()),
|
||||
KeyValue::new(
|
||||
"tag_ids",
|
||||
req.tag_ids.clone().unwrap_or_default().to_string(),
|
||||
),
|
||||
KeyValue::new(
|
||||
"tag_filter_mode",
|
||||
format!("{:?}", req.tag_filter_mode.unwrap_or(FilterMode::Any)),
|
||||
),
|
||||
KeyValue::new(
|
||||
"exclude_tag_ids",
|
||||
req.exclude_tag_ids.clone().unwrap_or_default().to_string(),
|
||||
),
|
||||
KeyValue::new("sort", format!("{:?}", &req.sort.unwrap_or(NameAsc))),
|
||||
]);
|
||||
|
||||
let span_context = opentelemetry::Context::current_with_span(span);
|
||||
|
||||
let search_recursively = req.recursive.unwrap_or(false);
|
||||
if let Some(tag_ids) = &req.tag_ids {
|
||||
if search_recursively {
|
||||
let filter_mode = &req.tag_filter_mode.unwrap_or(FilterMode::Any);
|
||||
debug!(
|
||||
info!(
|
||||
"Searching for tags: {}. With path: '{}' and filter mode: {:?}",
|
||||
tag_ids, search_path, filter_mode
|
||||
);
|
||||
@@ -61,15 +87,19 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
|
||||
.collect::<Vec<i32>>();
|
||||
|
||||
return match filter_mode {
|
||||
FilterMode::Any => dao.get_files_with_any_tag_ids(tag_ids.clone(), exclude_tag_ids),
|
||||
FilterMode::All => dao.get_files_with_all_tag_ids(tag_ids.clone(), exclude_tag_ids),
|
||||
FilterMode::Any => {
|
||||
dao.get_files_with_any_tag_ids(tag_ids.clone(), exclude_tag_ids, &span_context)
|
||||
}
|
||||
FilterMode::All => {
|
||||
dao.get_files_with_all_tag_ids(tag_ids.clone(), exclude_tag_ids, &span_context)
|
||||
}
|
||||
}
|
||||
.context(format!(
|
||||
"Failed to get files with tag_ids: {:?} with filter_mode: {:?}",
|
||||
tag_ids, filter_mode
|
||||
))
|
||||
.inspect(|files| {
|
||||
debug!(
|
||||
info!(
|
||||
"Found {:?} tagged files, filtering down by search path {:?}",
|
||||
files.len(),
|
||||
search_path
|
||||
@@ -94,11 +124,15 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
|
||||
.map(|files| sort(files, req.sort.unwrap_or(NameAsc)))
|
||||
.inspect(|files| debug!("Found {:?} files", files.len()))
|
||||
.map(|tagged_files: Vec<String>| {
|
||||
trace!(
|
||||
info!(
|
||||
"Found {:?} tagged files: {:?}",
|
||||
tagged_files.len(),
|
||||
tagged_files
|
||||
);
|
||||
span_context
|
||||
.span()
|
||||
.set_attribute(KeyValue::new("file_count", tagged_files.len().to_string()));
|
||||
span_context.span().set_status(Status::Ok);
|
||||
|
||||
HttpResponse::Ok().json(PhotosResponse {
|
||||
photos: tagged_files,
|
||||
@@ -111,7 +145,7 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
|
||||
}
|
||||
|
||||
if let Ok(files) = file_system.get_files_for_path(search_path) {
|
||||
debug!("Valid search path: {:?}", search_path);
|
||||
info!("Found {:?} files in path: {:?}", files.len(), search_path);
|
||||
|
||||
let photos = files
|
||||
.iter()
|
||||
@@ -131,7 +165,9 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
|
||||
.map(|f| f.to_str().unwrap().to_string())
|
||||
.map(|file_name| {
|
||||
let mut tag_dao = tag_dao.lock().expect("Unable to get TagDao");
|
||||
let file_tags = tag_dao.get_tags_for_path(&file_name).unwrap_or_default();
|
||||
let file_tags = tag_dao
|
||||
.get_tags_for_path(&span_context, &file_name)
|
||||
.unwrap_or_default();
|
||||
|
||||
(file_name, file_tags)
|
||||
})
|
||||
@@ -190,12 +226,20 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
|
||||
.map(|f| f.to_str().unwrap().to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
span_context
|
||||
.span()
|
||||
.set_attribute(KeyValue::new("file_count", files.len().to_string()));
|
||||
span_context.span().set_status(Status::Ok);
|
||||
|
||||
HttpResponse::Ok().json(PhotosResponse {
|
||||
photos: response_files,
|
||||
dirs,
|
||||
})
|
||||
} else {
|
||||
error!("Bad photos request: {}", req.path);
|
||||
span_context
|
||||
.span()
|
||||
.set_status(Status::error("Invalid path"));
|
||||
HttpResponse::BadRequest().finish()
|
||||
}
|
||||
}
|
||||
@@ -224,12 +268,21 @@ fn sort(mut files: Vec<FileWithTagCount>, sort_type: SortType) -> Vec<String> {
|
||||
}
|
||||
|
||||
pub fn list_files(dir: &Path) -> io::Result<Vec<PathBuf>> {
|
||||
let tracer = global_tracer();
|
||||
let mut span = tracer.start("list_files");
|
||||
let dir_name_string = dir.to_str().unwrap_or_default().to_string();
|
||||
span.set_attribute(KeyValue::new("dir", dir_name_string));
|
||||
info!("Listing files in: {:?}", dir);
|
||||
|
||||
let files = read_dir(dir)?
|
||||
.filter_map(|res| res.ok())
|
||||
.filter(|entry| is_image_or_video(&entry.path()) || entry.file_type().unwrap().is_dir())
|
||||
.map(|entry| entry.path())
|
||||
.collect::<Vec<PathBuf>>();
|
||||
|
||||
span.set_attribute(KeyValue::new("file_count", files.len().to_string()));
|
||||
span.set_status(Status::Ok);
|
||||
info!("Found {:?} files in directory: {:?}", files.len(), dir);
|
||||
Ok(files)
|
||||
}
|
||||
|
||||
@@ -245,6 +298,7 @@ pub fn is_image_or_video(path: &Path) -> bool {
|
||||
|| extension == "mp4"
|
||||
|| extension == "mov"
|
||||
|| extension == "nef"
|
||||
|| extension == "webp"
|
||||
}
|
||||
|
||||
pub fn is_valid_full_path<P: AsRef<Path> + Debug + AsRef<std::ffi::OsStr>>(
|
||||
@@ -301,6 +355,8 @@ pub async fn move_file<FS: FileSystemAccess>(
|
||||
app_state: Data<AppState>,
|
||||
request: web::Json<MoveFileRequest>,
|
||||
) -> HttpResponse {
|
||||
info!("Moving file: {:?}", request);
|
||||
|
||||
match is_valid_full_path(&app_state.base_path, &request.source, false)
|
||||
.ok_or(ErrorKind::InvalidData)
|
||||
.and_then(|source| {
|
||||
@@ -340,7 +396,7 @@ pub async fn move_file<FS: FileSystemAccess>(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct MoveFileRequest {
|
||||
source: String,
|
||||
destination: String,
|
||||
@@ -372,6 +428,11 @@ impl FileSystemAccess for RealFileSystem {
|
||||
}
|
||||
|
||||
fn move_file<P: AsRef<Path>>(&self, from: P, destination: P) -> anyhow::Result<()> {
|
||||
info!(
|
||||
"Moving file: '{:?}' -> '{:?}'",
|
||||
from.as_ref(),
|
||||
destination.as_ref()
|
||||
);
|
||||
let name = from
|
||||
.as_ref()
|
||||
.file_name()
|
||||
@@ -393,6 +454,8 @@ impl Handler<RefreshThumbnailsMessage> for StreamActor {
|
||||
type Result = ();
|
||||
|
||||
fn handle(&mut self, _msg: RefreshThumbnailsMessage, _ctx: &mut Self::Context) -> Self::Result {
|
||||
let tracer = global_tracer();
|
||||
let _ = tracer.start("RefreshThumbnailsMessage");
|
||||
info!("Refreshing thumbnails after upload");
|
||||
create_thumbnails()
|
||||
}
|
||||
@@ -525,6 +588,7 @@ mod tests {
|
||||
|
||||
let response = list_photos(
|
||||
claims,
|
||||
HttpRequest::default(),
|
||||
request,
|
||||
Data::new(AppState::new(
|
||||
Arc::new(StreamActor {}.start()),
|
||||
@@ -572,6 +636,7 @@ mod tests {
|
||||
|
||||
let response: HttpResponse = list_photos(
|
||||
claims,
|
||||
HttpRequest::default(),
|
||||
request,
|
||||
Data::new(AppState::new(
|
||||
Arc::new(StreamActor {}.start()),
|
||||
|
||||
Reference in New Issue
Block a user