diff --git a/src/config.rs b/src/config.rs
index 2c4279e..506575e 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -12,10 +12,9 @@ use defaults::Defaults;
pub(crate) use commandline::Operation;
pub(crate) use file::{
- ConfigFile as Configuration, Media as MediaConfiguration, ObjectStorage, OpenTelemetry, Repo,
- Sled, Store, Tracing,
+ ConfigFile as Configuration, ObjectStorage, OpenTelemetry, Repo, Sled, Store, Tracing,
};
-pub(crate) use primitives::{AudioCodec, Filesystem, ImageFormat, LogFormat, VideoCodec};
+pub(crate) use primitives::{Filesystem, LogFormat};
/// Source for pict-rs configuration when embedding as a library
pub enum ConfigSource
{
diff --git a/src/config/commandline.rs b/src/config/commandline.rs
index 7d50ec0..a16537d 100644
--- a/src/config/commandline.rs
+++ b/src/config/commandline.rs
@@ -1,5 +1,6 @@
use crate::{
- config::primitives::{AudioCodec, ImageFormat, LogFormat, Targets, VideoCodec},
+ config::primitives::{AudioCodec, LogFormat, Targets, VideoCodec},
+ formats::ImageFormat,
serde_str::Serde,
};
use clap::{Parser, Subcommand};
diff --git a/src/config/file.rs b/src/config/file.rs
index 390d3ca..a93f72d 100644
--- a/src/config/file.rs
+++ b/src/config/file.rs
@@ -1,5 +1,6 @@
use crate::{
- config::primitives::{AudioCodec, Filesystem, ImageFormat, LogFormat, Targets, VideoCodec},
+ config::primitives::{AudioCodec, Filesystem, LogFormat, Targets, VideoCodec},
+ formats::ImageFormat,
serde_str::Serde,
};
use once_cell::sync::OnceCell;
diff --git a/src/config/primitives.rs b/src/config/primitives.rs
index 0bb3e4c..172c6d3 100644
--- a/src/config/primitives.rs
+++ b/src/config/primitives.rs
@@ -1,4 +1,3 @@
-use crate::magick::ValidInputType;
use clap::ValueEnum;
use std::{fmt::Display, path::PathBuf, str::FromStr};
use tracing::Level;
@@ -25,28 +24,6 @@ pub(crate) enum LogFormat {
Pretty,
}
-#[derive(
- Clone,
- Copy,
- Debug,
- PartialEq,
- Eq,
- PartialOrd,
- Ord,
- Hash,
- serde::Deserialize,
- serde::Serialize,
- ValueEnum,
-)]
-#[serde(rename_all = "snake_case")]
-pub(crate) enum ImageFormat {
- Avif,
- Jpeg,
- Jxl,
- Png,
- Webp,
-}
-
#[derive(
Clone,
Copy,
@@ -169,32 +146,6 @@ pub(crate) enum Store {
ObjectStorage(ObjectStorage),
}
-impl ImageFormat {
- pub(crate) fn as_hint(self) -> ValidInputType {
- ValidInputType::from_format(self)
- }
-
- pub(crate) fn as_magick_format(self) -> &'static str {
- match self {
- Self::Avif => "AVIF",
- Self::Jpeg => "JPEG",
- Self::Jxl => "JXL",
- Self::Png => "PNG",
- Self::Webp => "WEBP",
- }
- }
-
- pub(crate) fn as_ext(self) -> &'static str {
- match self {
- Self::Avif => ".avif",
- Self::Jpeg => ".jpeg",
- Self::Jxl => ".jxl",
- Self::Png => ".png",
- Self::Webp => ".webp",
- }
- }
-}
-
impl From for Store {
fn from(f: Filesystem) -> Self {
Self::Filesystem(f)
@@ -260,21 +211,6 @@ impl Display for Targets {
}
}
-impl FromStr for ImageFormat {
- type Err = String;
-
- fn from_str(s: &str) -> Result {
- match s.to_lowercase().as_str() {
- "avif" => Ok(Self::Avif),
- "jpeg" | "jpg" => Ok(Self::Jpeg),
- "jxl" => Ok(Self::Jxl),
- "png" => Ok(Self::Png),
- "webp" => Ok(Self::Webp),
- other => Err(format!("Invalid variant: {other}")),
- }
- }
-}
-
impl FromStr for LogFormat {
type Err = String;
@@ -288,15 +224,6 @@ impl FromStr for LogFormat {
}
}
-impl Display for ImageFormat {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- self.to_possible_value()
- .expect("no values are skipped")
- .get_name()
- .fmt(f)
- }
-}
-
impl Display for LogFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.to_possible_value()
diff --git a/src/details.rs b/src/details.rs
index 1e760d6..e40c0ae 100644
--- a/src/details.rs
+++ b/src/details.rs
@@ -1,7 +1,7 @@
use crate::{
+ discover::DiscoveryLite,
error::Error,
formats::{InternalFormat, InternalVideoFormat},
- magick::{video_mp4, video_webm, ValidInputType},
serde_str::Serde,
store::Store,
};
@@ -17,9 +17,9 @@ pub(crate) enum MaybeHumanDate {
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub(crate) struct Details {
- width: usize,
- height: usize,
- frames: Option,
+ width: u16,
+ height: u16,
+ frames: Option,
content_type: Serde,
created_at: MaybeHumanDate,
#[serde(skip_serializing_if = "Option::is_none")]
@@ -27,71 +27,41 @@ pub(crate) struct Details {
}
impl Details {
- pub(crate) fn is_motion(&self) -> bool {
+ pub(crate) fn is_video(&self) -> bool {
self.content_type.type_() == "video"
- || self.content_type.type_() == "image" && self.content_type.subtype() == "gif"
}
- pub(crate) async fn from_bytes(input: web::Bytes, hint: ValidInputType) -> Result {
- let details = if hint.is_video() {
- crate::ffmpeg::details_bytes(input.clone()).await?
- } else {
- None
- };
-
- let details = if let Some(details) = details {
- details
- } else {
- crate::magick::details_bytes(input, Some(hint)).await?
- };
-
- Ok(Details::now(
- details.width,
- details.height,
- details.mime_type,
- details.frames,
- ))
- }
-
- pub(crate) async fn from_store(
- store: S,
- identifier: S::Identifier,
- expected_format: Option,
- ) -> Result {
- let details = if expected_format.map(|t| t.is_video()).unwrap_or(true) {
- crate::ffmpeg::details_store(&store, &identifier).await?
- } else {
- None
- };
-
- let details = if let Some(details) = details {
- details
- } else {
- crate::magick::details_store(store, identifier, expected_format).await?
- };
-
- Ok(Details::now(
- details.width,
- details.height,
- details.mime_type,
- details.frames,
- ))
- }
-
- pub(crate) fn now(
- width: usize,
- height: usize,
- content_type: mime::Mime,
- frames: Option,
- ) -> Self {
- Details {
+ pub(crate) async fn from_bytes(input: web::Bytes) -> Result {
+ let DiscoveryLite {
+ format,
width,
height,
frames,
- content_type: Serde::new(content_type),
- created_at: MaybeHumanDate::HumanDate(time::OffsetDateTime::now_utc()),
- format: None,
+ } = crate::discover::discover_bytes_lite(input).await?;
+
+ Ok(Details::from_parts(format, width, height, frames))
+ }
+
+ pub(crate) async fn from_store(
+ store: &S,
+ identifier: &S::Identifier,
+ ) -> Result {
+ let DiscoveryLite {
+ format,
+ width,
+ height,
+ frames,
+ } = crate::discover::discover_store_lite(store, identifier).await?;
+
+ Ok(Details::from_parts(format, width, height, frames))
+ }
+
+ pub(crate) fn internal_format(&self) -> Option {
+ if let Some(format) = self.format {
+ return Some(format);
}
+
+ InternalFormat::maybe_from_media_type(&self.content_type, self.frames.is_some())
}
pub(crate) fn content_type(&self) -> mime::Mime {
@@ -102,12 +72,12 @@ impl Details {
self.created_at.into()
}
- pub(crate) fn input_format(&self) -> Option {
- if *self.content_type == video_mp4() {
+ pub(crate) fn video_format(&self) -> Option {
+ if *self.content_type == crate::formats::mimes::video_mp4() {
return Some(InternalVideoFormat::Mp4);
}
- if *self.content_type == video_webm() {
+ if *self.content_type == crate::formats::mimes::video_webm() {
return Some(InternalVideoFormat::Webm);
}
@@ -121,9 +91,9 @@ impl Details {
frames: Option,
) -> Self {
Self {
- width: width.into(),
- height: height.into(),
- frames: frames.map(|f| f.try_into().expect("Reasonable size")),
+ width,
+ height,
+ frames,
content_type: Serde::new(format.media_type()),
created_at: MaybeHumanDate::HumanDate(OffsetDateTime::now_utc()),
format: Some(format),
diff --git a/src/discover.rs b/src/discover.rs
index aac21f1..840d264 100644
--- a/src/discover.rs
+++ b/src/discover.rs
@@ -4,7 +4,10 @@ mod magick;
use actix_web::web::Bytes;
-use crate::formats::{AnimationInput, ImageInput, InputFile, InternalFormat, InternalVideoFormat};
+use crate::{
+ formats::{InputFile, InternalFormat},
+ store::Store,
+};
pub(crate) struct Discovery {
pub(crate) input: InputFile,
@@ -13,20 +16,42 @@ pub(crate) struct Discovery {
pub(crate) frames: Option,
}
-impl Discovery {
- pub(crate) fn internal_format(&self) -> InternalFormat {
- match self.input {
- InputFile::Image(ImageInput { format, .. }) => InternalFormat::Image(format),
- InputFile::Animation(AnimationInput { format }) => InternalFormat::Animation(format),
- // we're making up bs now lol
- InputFile::Video(crate::formats::VideoFormat::Mp4) => {
- InternalFormat::Video(InternalVideoFormat::Mp4)
- }
- InputFile::Video(crate::formats::VideoFormat::Webm { .. }) => {
- InternalFormat::Video(InternalVideoFormat::Webm)
- }
- }
+pub(crate) struct DiscoveryLite {
+ pub(crate) format: InternalFormat,
+ pub(crate) width: u16,
+ pub(crate) height: u16,
+ pub(crate) frames: Option,
+}
+
+pub(crate) async fn discover_bytes_lite(
+ bytes: Bytes,
+) -> Result {
+ if let Some(discovery) = ffmpeg::discover_bytes_lite(bytes.clone()).await? {
+ return Ok(discovery);
}
+
+ let discovery = magick::discover_bytes_lite(bytes).await?;
+
+ Ok(discovery)
+}
+
+pub(crate) async fn discover_store_lite(
+ store: &S,
+ identifier: &S::Identifier,
+) -> Result
+where
+ S: Store + 'static,
+{
+ if let Some(discovery) =
+ ffmpeg::discover_stream_lite(store.to_stream(identifier, None, None).await?).await?
+ {
+ return Ok(discovery);
+ }
+
+ let discovery =
+ magick::discover_stream_lite(store.to_stream(identifier, None, None).await?).await?;
+
+ Ok(discovery)
}
pub(crate) async fn discover_bytes(bytes: Bytes) -> Result {
diff --git a/src/discover/ffmpeg.rs b/src/discover/ffmpeg.rs
index cb1799e..3ec4246 100644
--- a/src/discover/ffmpeg.rs
+++ b/src/discover/ffmpeg.rs
@@ -2,43 +2,25 @@ use std::{collections::HashSet, sync::OnceLock};
use crate::{
ffmpeg::FfMpegError,
- formats::{AnimationFormat, AnimationInput, ImageFormat, ImageInput, InputFile, VideoFormat},
+ formats::{
+ AnimationFormat, AnimationInput, ImageFormat, ImageInput, InputFile, InternalFormat,
+ InternalVideoFormat, VideoFormat,
+ },
process::Process,
};
use actix_web::web::Bytes;
+use futures_util::Stream;
use tokio::io::AsyncReadExt;
-use super::Discovery;
+use super::{Discovery, DiscoveryLite};
-const FFMPEG_FORMAT_MAPPINGS: &[(&str, InputFile)] = &[
- (
- "apng",
- InputFile::Animation(AnimationInput {
- format: AnimationFormat::Apng,
- }),
- ),
- (
- "gif",
- InputFile::Animation(AnimationInput {
- format: AnimationFormat::Gif,
- }),
- ),
- ("mp4", InputFile::Video(VideoFormat::Mp4)),
- (
- "png_pipe",
- InputFile::Image(ImageInput {
- format: ImageFormat::Png,
- needs_reorient: false,
- }),
- ),
- ("webm", InputFile::Video(VideoFormat::Webm { alpha: false })),
- (
- "webp_pipe",
- InputFile::Image(ImageInput {
- format: ImageFormat::Webp,
- needs_reorient: false,
- }),
- ),
+const FFMPEG_FORMAT_MAPPINGS: &[(&str, InternalFormat)] = &[
+ ("apng", InternalFormat::Animation(AnimationFormat::Apng)),
+ ("gif", InternalFormat::Animation(AnimationFormat::Gif)),
+ ("mp4", InternalFormat::Video(InternalVideoFormat::Mp4)),
+ ("png_pipe", InternalFormat::Image(ImageFormat::Png)),
+ ("webm", InternalFormat::Video(InternalVideoFormat::Webm)),
+ ("webp_pipe", InternalFormat::Image(ImageFormat::Webp)),
];
#[derive(Debug, serde::Deserialize)]
@@ -76,7 +58,23 @@ struct Flags {
}
pub(super) async fn discover_bytes(bytes: Bytes) -> Result