pict-rs/src/upload_manager.rs

421 lines
13 KiB
Rust
Raw Normal View History

2020-07-11 20:40:40 +00:00
use crate::{
2022-03-28 04:27:07 +00:00
config::ImageFormat,
2022-03-24 22:09:15 +00:00
details::Details,
2021-09-14 01:22:42 +00:00
error::{Error, UploadError},
2021-10-23 04:48:56 +00:00
ffmpeg::{InputFormat, ThumbnailFormat},
2022-03-24 22:09:15 +00:00
magick::details_hint,
2022-03-26 21:49:23 +00:00
repo::{
sled::SledRepo, Alias, AliasRepo, DeleteToken, HashRepo, IdentifierRepo, Repo, SettingsRepo,
},
2021-10-23 04:48:56 +00:00
store::{Identifier, Store},
2020-07-11 20:40:40 +00:00
};
2022-03-26 21:49:23 +00:00
use futures_util::StreamExt;
2020-06-06 21:41:17 +00:00
use sha2::Digest;
2022-03-26 21:49:23 +00:00
use std::sync::Arc;
use tracing::{debug, error, instrument, Span};
2021-09-16 22:51:20 +00:00
use tracing_futures::Instrument;
2020-06-06 21:41:17 +00:00
2021-10-18 23:44:05 +00:00
mod hasher;
mod session;
pub(super) use session::UploadManagerSession;
2021-11-01 02:11:35 +00:00
const STORE_MIGRATION_PROGRESS: &[u8] = b"store-migration-progress";
2021-10-19 01:29:06 +00:00
2020-06-06 21:41:17 +00:00
#[derive(Clone)]
2021-11-01 02:11:35 +00:00
pub(crate) struct UploadManager {
2020-06-06 21:41:17 +00:00
inner: Arc<UploadManagerInner>,
}
2021-10-23 04:48:56 +00:00
pub(crate) struct UploadManagerInner {
2022-03-28 04:27:07 +00:00
format: Option<ImageFormat>,
2020-06-06 21:41:17 +00:00
hasher: sha2::Sha256,
2022-03-26 21:49:23 +00:00
repo: Repo,
}
2021-11-01 02:11:35 +00:00
impl UploadManager {
2020-06-06 21:41:17 +00:00
/// Create a new UploadManager
2022-03-28 04:27:07 +00:00
pub(crate) async fn new(repo: Repo, format: Option<ImageFormat>) -> Result<Self, Error> {
2021-10-18 23:44:05 +00:00
let manager = UploadManager {
2020-06-06 21:41:17 +00:00
inner: Arc::new(UploadManagerInner {
2020-06-07 01:44:26 +00:00
format,
2020-06-06 21:41:17 +00:00
hasher: sha2::Sha256::new(),
2022-03-26 21:49:23 +00:00
repo,
2020-06-06 21:41:17 +00:00
}),
2021-10-18 23:44:05 +00:00
};
Ok(manager)
2020-06-06 21:41:17 +00:00
}
2021-11-01 02:11:35 +00:00
pub(crate) async fn migrate_store<S1, S2>(&self, from: S1, to: S2) -> Result<(), Error>
where
S1: Store,
S2: Store,
{
2022-03-26 21:49:23 +00:00
match self.inner.repo {
Repo::Sled(ref sled_repo) => do_migrate_store(sled_repo, from, to).await,
2021-11-01 02:11:35 +00:00
}
2021-10-23 04:48:56 +00:00
}
2022-03-26 21:49:23 +00:00
pub(crate) async fn still_identifier_from_alias<S: Store + Clone>(
2021-10-21 02:36:18 +00:00
&self,
2021-11-01 02:11:35 +00:00
store: S,
2022-03-26 21:49:23 +00:00
alias: &Alias,
) -> Result<S::Identifier, Error> {
2022-03-26 21:49:23 +00:00
let identifier = self.identifier_from_alias::<S>(alias).await?;
let details = if let Some(details) = self.details(&identifier).await? {
details
} else {
let hint = details_hint(alias);
Details::from_store(store.clone(), identifier.clone(), hint).await?
};
2021-10-21 02:36:18 +00:00
if !details.is_motion() {
2021-10-23 04:48:56 +00:00
return Ok(identifier);
2021-10-21 02:36:18 +00:00
}
2022-03-26 21:49:23 +00:00
if let Some(motion_identifier) = self.motion_identifier::<S>(alias).await? {
2021-10-23 04:48:56 +00:00
return Ok(motion_identifier);
2021-10-21 02:36:18 +00:00
}
let permit = crate::PROCESS_SEMAPHORE.acquire().await;
2021-10-23 04:48:56 +00:00
let mut reader = crate::ffmpeg::thumbnail(
2021-11-01 02:11:35 +00:00
store.clone(),
2021-10-23 04:48:56 +00:00
identifier,
InputFormat::Mp4,
ThumbnailFormat::Jpeg,
)
.await?;
2022-03-26 21:49:23 +00:00
let motion_identifier = store.save_async_read(&mut reader).await?;
2021-10-21 02:36:18 +00:00
drop(permit);
2022-03-26 21:49:23 +00:00
self.store_motion_identifier(alias, &motion_identifier)
2021-10-23 04:48:56 +00:00
.await?;
Ok(motion_identifier)
2021-10-21 02:36:18 +00:00
}
2021-11-01 02:11:35 +00:00
async fn motion_identifier<S: Store>(
&self,
2022-03-26 21:49:23 +00:00
alias: &Alias,
) -> Result<Option<S::Identifier>, Error> {
match self.inner.repo {
Repo::Sled(ref sled_repo) => {
let hash = sled_repo.hash(alias).await?;
Ok(sled_repo.motion_identifier(hash).await?)
}
2021-10-21 02:36:18 +00:00
}
}
2022-03-26 21:49:23 +00:00
async fn store_motion_identifier<I: Identifier + 'static>(
2021-10-21 02:36:18 +00:00
&self,
2022-03-26 21:49:23 +00:00
alias: &Alias,
2021-11-01 02:11:35 +00:00
identifier: &I,
2022-03-26 21:49:23 +00:00
) -> Result<(), Error> {
match self.inner.repo {
Repo::Sled(ref sled_repo) => {
let hash = sled_repo.hash(alias).await?;
Ok(sled_repo.relate_motion_identifier(hash, identifier).await?)
}
}
2021-10-21 02:36:18 +00:00
}
2021-10-19 04:37:11 +00:00
#[instrument(skip(self))]
2022-03-26 21:49:23 +00:00
pub(crate) async fn identifier_from_alias<S: Store>(
2021-10-23 04:48:56 +00:00
&self,
2022-03-26 21:49:23 +00:00
alias: &Alias,
) -> Result<S::Identifier, Error> {
match self.inner.repo {
Repo::Sled(ref sled_repo) => {
let hash = sled_repo.hash(alias).await?;
Ok(sled_repo.identifier(hash).await?)
}
}
2021-10-19 04:37:11 +00:00
}
#[instrument(skip(self))]
2021-11-01 02:11:35 +00:00
async fn store_identifier<I: Identifier>(
2021-10-23 04:48:56 +00:00
&self,
2022-03-26 21:49:23 +00:00
hash: Vec<u8>,
2021-11-01 02:11:35 +00:00
identifier: &I,
2022-03-26 21:49:23 +00:00
) -> Result<(), Error> {
match self.inner.repo {
Repo::Sled(ref sled_repo) => {
Ok(sled_repo.relate_identifier(hash.into(), identifier).await?)
}
}
2021-10-19 04:37:11 +00:00
}
#[instrument(skip(self))]
2021-11-01 02:11:35 +00:00
pub(crate) async fn variant_identifier<S: Store>(
2021-10-19 04:37:11 +00:00
&self,
2022-03-26 21:49:23 +00:00
alias: &Alias,
2021-10-19 04:37:11 +00:00
process_path: &std::path::Path,
2022-03-26 21:49:23 +00:00
) -> Result<Option<S::Identifier>, Error> {
let variant = process_path.to_string_lossy().to_string();
2021-10-19 04:37:11 +00:00
2022-03-26 21:49:23 +00:00
match self.inner.repo {
Repo::Sled(ref sled_repo) => {
let hash = sled_repo.hash(alias).await?;
Ok(sled_repo.variant_identifier(hash, variant).await?)
}
2021-10-19 04:37:11 +00:00
}
}
/// Store the path to a generated image variant so we can easily clean it up later
2020-06-14 15:07:31 +00:00
#[instrument(skip(self))]
2022-03-26 21:49:23 +00:00
pub(crate) async fn store_full_res<I: Identifier>(
2021-10-19 04:37:11 +00:00
&self,
2022-03-26 21:49:23 +00:00
alias: &Alias,
2021-11-01 02:11:35 +00:00
identifier: &I,
2022-03-26 21:49:23 +00:00
) -> Result<(), Error> {
match self.inner.repo {
Repo::Sled(ref sled_repo) => {
let hash = sled_repo.hash(alias).await?;
Ok(sled_repo.relate_identifier(hash, identifier).await?)
}
}
}
2022-03-26 21:49:23 +00:00
/// Store the path to a generated image variant so we can easily clean it up later
#[instrument(skip(self))]
pub(crate) async fn store_variant<I: Identifier>(
&self,
alias: &Alias,
variant_process_path: &std::path::Path,
identifier: &I,
) -> Result<(), Error> {
let variant = variant_process_path.to_string_lossy().to_string();
match self.inner.repo {
Repo::Sled(ref sled_repo) => {
let hash = sled_repo.hash(alias).await?;
Ok(sled_repo
.relate_variant_identifier(hash, variant, identifier)
.await?)
}
}
}
/// Get the image details for a given variant
2021-09-25 22:09:55 +00:00
#[instrument(skip(self))]
2022-03-26 21:49:23 +00:00
pub(crate) async fn details<I: Identifier>(
&self,
2021-11-01 02:11:35 +00:00
identifier: &I,
) -> Result<Option<Details>, Error> {
2022-03-26 21:49:23 +00:00
match self.inner.repo {
Repo::Sled(ref sled_repo) => Ok(sled_repo.details(identifier).await?),
}
}
2021-09-25 22:09:55 +00:00
#[instrument(skip(self))]
2022-03-26 21:49:23 +00:00
pub(crate) async fn store_details<I: Identifier>(
&self,
2021-11-01 02:11:35 +00:00
identifier: &I,
details: &Details,
2022-03-26 21:49:23 +00:00
) -> Result<(), Error> {
match self.inner.repo {
Repo::Sled(ref sled_repo) => Ok(sled_repo.relate_details(identifier, details).await?),
}
}
/// Get a list of aliases for a given alias
2022-03-26 21:49:23 +00:00
pub(crate) async fn aliases_by_alias(&self, alias: &Alias) -> Result<Vec<Alias>, Error> {
match self.inner.repo {
Repo::Sled(ref sled_repo) => {
let hash = sled_repo.hash(alias).await?;
Ok(sled_repo.aliases(hash).await?)
}
}
}
/// Delete an alias without a delete token
2021-11-01 02:11:35 +00:00
pub(crate) async fn delete_without_token<S: Store + 'static>(
&self,
store: S,
2022-03-26 21:49:23 +00:00
alias: Alias,
) -> Result<(), Error> {
2022-03-26 21:49:23 +00:00
let token = match self.inner.repo {
Repo::Sled(ref sled_repo) => sled_repo.delete_token(&alias).await?,
};
self.delete(store, alias, token).await
}
/// Delete the alias, and the file & variants if no more aliases exist
2020-06-14 18:56:42 +00:00
#[instrument(skip(self, alias, token))]
2021-11-01 02:11:35 +00:00
pub(crate) async fn delete<S: Store + 'static>(
&self,
store: S,
2022-03-26 21:49:23 +00:00
alias: Alias,
token: DeleteToken,
) -> Result<(), Error> {
2022-03-26 21:49:23 +00:00
let hash = match self.inner.repo {
Repo::Sled(ref sled_repo) => {
let saved_delete_token = sled_repo.delete_token(&alias).await?;
if saved_delete_token != token {
return Err(UploadError::InvalidToken.into());
2020-06-07 00:29:15 +00:00
}
2022-03-26 21:49:23 +00:00
let hash = sled_repo.hash(&alias).await?;
AliasRepo::cleanup(sled_repo, &alias).await?;
sled_repo.remove_alias(hash.clone(), &alias).await?;
hash.to_vec()
}
};
2020-06-07 00:29:15 +00:00
2021-11-01 02:11:35 +00:00
self.check_delete_files(store, hash).await
2021-09-12 00:53:26 +00:00
}
2021-11-01 02:11:35 +00:00
async fn check_delete_files<S: Store + 'static>(
&self,
store: S,
2022-03-26 21:49:23 +00:00
hash: Vec<u8>,
) -> Result<(), Error> {
2022-03-26 21:49:23 +00:00
match self.inner.repo {
Repo::Sled(ref sled_repo) => {
let hash: <SledRepo as HashRepo>::Bytes = hash.into();
2020-06-07 00:29:15 +00:00
2022-03-26 21:49:23 +00:00
let aliases = sled_repo.aliases(hash.clone()).await?;
if !aliases.is_empty() {
return Ok(());
2021-09-16 22:51:20 +00:00
}
2022-03-26 21:49:23 +00:00
let variant_idents = sled_repo
.variants::<S::Identifier>(hash.clone())
.await?
.into_iter()
.map(|(_, v)| v)
.collect::<Vec<_>>();
let main_ident = sled_repo.identifier(hash.clone()).await?;
let motion_ident = sled_repo.motion_identifier(hash.clone()).await?;
let repo = sled_repo.clone();
HashRepo::cleanup(sled_repo, hash).await?;
let cleanup_span = tracing::info_span!(parent: None, "Cleaning files");
2022-03-26 21:49:23 +00:00
cleanup_span.follows_from(Span::current());
actix_rt::spawn(
async move {
let mut errors = Vec::new();
for identifier in variant_idents
.iter()
.chain(&[main_ident])
.chain(motion_ident.iter())
{
debug!("Deleting {:?}", identifier);
if let Err(e) = store.remove(identifier).await {
errors.push(e);
}
if let Err(e) = IdentifierRepo::cleanup(&repo, identifier).await {
errors.push(e);
}
}
if !errors.is_empty() {
let span = tracing::error_span!("Error deleting files");
span.in_scope(|| {
for error in errors {
error!("{}", error);
}
});
}
}
.instrument(cleanup_span),
2021-09-16 22:51:20 +00:00
);
2020-06-07 00:33:29 +00:00
}
2022-03-26 21:49:23 +00:00
}
2020-06-07 00:29:15 +00:00
Ok(())
}
pub(crate) fn session<S: Store + Clone + 'static>(&self, store: S) -> UploadManagerSession<S> {
2021-11-01 02:11:35 +00:00
UploadManagerSession::new(self.clone(), store)
2021-09-12 00:53:26 +00:00
}
2022-03-26 21:49:23 +00:00
}
2021-09-12 00:53:26 +00:00
2022-03-26 21:49:23 +00:00
async fn migrate_file<S1, S2>(
from: &S1,
to: &S2,
identifier: &S1::Identifier,
) -> Result<S2::Identifier, Error>
where
S1: Store,
S2: Store,
{
let stream = from.to_stream(identifier, None, None).await?;
futures_util::pin_mut!(stream);
let mut reader = tokio_util::io::StreamReader::new(stream);
2021-10-19 04:37:11 +00:00
2022-03-26 21:49:23 +00:00
let new_identifier = to.save_async_read(&mut reader).await?;
2021-10-19 04:37:11 +00:00
2022-03-26 21:49:23 +00:00
Ok(new_identifier)
2021-09-12 00:53:26 +00:00
}
2022-03-26 21:49:23 +00:00
async fn migrate_details<R, I1, I2>(repo: &R, from: I1, to: &I2) -> Result<(), Error>
where
R: IdentifierRepo,
I1: Identifier,
I2: Identifier,
{
if let Some(details) = repo.details(&from).await? {
repo.relate_details(to, &details).await?;
repo.cleanup(&from).await?;
}
2020-06-06 22:43:33 +00:00
2022-03-26 21:49:23 +00:00
Ok(())
2021-10-23 04:48:56 +00:00
}
2022-03-26 21:49:23 +00:00
async fn do_migrate_store<R, S1, S2>(repo: &R, from: S1, to: S2) -> Result<(), Error>
2021-09-14 01:22:42 +00:00
where
2022-03-26 21:49:23 +00:00
S1: Store,
S2: Store,
R: IdentifierRepo + HashRepo + SettingsRepo,
2021-09-14 01:22:42 +00:00
{
2022-03-26 21:49:23 +00:00
let stream = repo.hashes().await;
let mut stream = Box::pin(stream);
while let Some(hash) = stream.next().await {
let hash = hash?;
if let Some(identifier) = repo
.motion_identifier(hash.as_ref().to_vec().into())
.await?
{
let new_identifier = migrate_file(&from, &to, &identifier).await?;
migrate_details(repo, identifier, &new_identifier).await?;
repo.relate_motion_identifier(hash.as_ref().to_vec().into(), &new_identifier)
.await?;
}
2020-06-07 00:29:15 +00:00
2022-03-26 21:49:23 +00:00
for (variant, identifier) in repo.variants(hash.as_ref().to_vec().into()).await? {
let new_identifier = migrate_file(&from, &to, &identifier).await?;
migrate_details(repo, identifier, &new_identifier).await?;
repo.relate_variant_identifier(hash.as_ref().to_vec().into(), variant, &new_identifier)
.await?;
}
2022-03-26 21:49:23 +00:00
let identifier = repo.identifier(hash.as_ref().to_vec().into()).await?;
let new_identifier = migrate_file(&from, &to, &identifier).await?;
migrate_details(repo, identifier, &new_identifier).await?;
repo.relate_identifier(hash.as_ref().to_vec().into(), &new_identifier)
.await?;
repo.set(STORE_MIGRATION_PROGRESS, hash.as_ref().to_vec().into())
.await?;
2021-10-13 04:16:31 +00:00
}
2022-03-26 21:49:23 +00:00
// clean up the migration key to avoid interfering with future migrations
repo.remove(STORE_MIGRATION_PROGRESS).await?;
Ok(())
2021-10-18 23:44:05 +00:00
}
2021-10-13 04:16:31 +00:00
2022-03-26 21:49:23 +00:00
impl std::fmt::Debug for UploadManager {
2021-10-18 23:44:05 +00:00
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
2022-03-26 21:49:23 +00:00
f.debug_struct("UploadManager").finish()
2021-09-04 17:42:40 +00:00
}
}