feat: video duration / bitrate
This commit is contained in:
parent
5fbe40faae
commit
3ba5e7bc4c
4
migrations/20250127210244_video_metadata.sql
Normal file
4
migrations/20250127210244_video_metadata.sql
Normal file
@ -0,0 +1,4 @@
|
||||
-- Add migration script here
|
||||
alter table uploads
|
||||
add column duration float,
|
||||
add column bitrate integer unsigned;
|
@ -139,6 +139,8 @@ async fn migrate_file(
|
||||
},
|
||||
blur_hash: None,
|
||||
alt: f.description.clone(),
|
||||
duration: None,
|
||||
bitrate: None,
|
||||
};
|
||||
db.add_file(&fu, uid).await?;
|
||||
Ok(())
|
||||
|
12
src/db.rs
12
src/db.rs
@ -25,6 +25,10 @@ pub struct FileUpload {
|
||||
pub blur_hash: Option<String>,
|
||||
/// Alt text of the media
|
||||
pub alt: Option<String>,
|
||||
/// Duration of media in seconds
|
||||
pub duration: Option<f32>,
|
||||
/// Average bitrate in bits/s
|
||||
pub bitrate: Option<u32>,
|
||||
|
||||
#[sqlx(skip)]
|
||||
#[cfg(feature = "labels")]
|
||||
@ -43,6 +47,8 @@ impl From<&NewFileResult> for FileUpload {
|
||||
height: value.height,
|
||||
blur_hash: value.blur_hash.clone(),
|
||||
alt: None,
|
||||
duration: value.duration,
|
||||
bitrate: value.bitrate,
|
||||
#[cfg(feature = "labels")]
|
||||
labels: value.labels.clone(),
|
||||
}
|
||||
@ -145,7 +151,7 @@ impl Database {
|
||||
pub async fn add_file(&self, file: &FileUpload, user_id: u64) -> Result<(), Error> {
|
||||
let mut tx = self.pool.begin().await?;
|
||||
let q = sqlx::query("insert ignore into \
|
||||
uploads(id,name,size,mime_type,blur_hash,width,height,alt,created) values(?,?,?,?,?,?,?,?,?)")
|
||||
uploads(id,name,size,mime_type,blur_hash,width,height,alt,created,duration,bitrate) values(?,?,?,?,?,?,?,?,?,?,?)")
|
||||
.bind(&file.id)
|
||||
.bind(&file.name)
|
||||
.bind(file.size)
|
||||
@ -154,7 +160,9 @@ impl Database {
|
||||
.bind(file.width)
|
||||
.bind(file.height)
|
||||
.bind(&file.alt)
|
||||
.bind(file.created);
|
||||
.bind(file.created)
|
||||
.bind(file.duration)
|
||||
.bind(file.bitrate);
|
||||
tx.execute(q).await?;
|
||||
|
||||
let q2 = sqlx::query("insert ignore into user_uploads(file,user_id) values(?,?)")
|
||||
|
@ -1,5 +1,6 @@
|
||||
#[cfg(feature = "labels")]
|
||||
use crate::db::FileLabel;
|
||||
use crate::processing::can_compress;
|
||||
#[cfg(feature = "labels")]
|
||||
use crate::processing::labeling::label_frame;
|
||||
#[cfg(feature = "media-compression")]
|
||||
@ -36,6 +37,8 @@ pub struct NewFileResult {
|
||||
pub width: Option<u32>,
|
||||
pub height: Option<u32>,
|
||||
pub blur_hash: Option<String>,
|
||||
pub duration: Option<f32>,
|
||||
pub bitrate: Option<u32>,
|
||||
#[cfg(feature = "labels")]
|
||||
pub labels: Vec<FileLabel>,
|
||||
}
|
||||
@ -74,7 +77,7 @@ impl FileStore {
|
||||
return Ok(FileSystemResult::AlreadyExists(hash));
|
||||
}
|
||||
|
||||
let mut res = if compress {
|
||||
let mut res = if compress && can_compress(mime_type) {
|
||||
#[cfg(feature = "media-compression")]
|
||||
{
|
||||
let res = match self.compress_file(&temp_file, mime_type).await {
|
||||
@ -92,7 +95,7 @@ impl FileStore {
|
||||
anyhow::bail!("Compression not supported!");
|
||||
}
|
||||
} else {
|
||||
let (width, height, mime_type) = {
|
||||
let (width, height, mime_type, duration, bitrate) = {
|
||||
#[cfg(feature = "media-compression")]
|
||||
{
|
||||
let probe = probe_file(&temp_file).ok();
|
||||
@ -102,10 +105,18 @@ impl FileStore {
|
||||
v_stream.map(|v| v.width as u32),
|
||||
v_stream.map(|v| v.height as u32),
|
||||
mime,
|
||||
probe.as_ref().map(|p| p.duration),
|
||||
probe.as_ref().map(|p| p.bitrate as u32),
|
||||
)
|
||||
}
|
||||
#[cfg(not(feature = "media-compression"))]
|
||||
(None, None, Self::infer_mime_type(mime_type, &temp_file))
|
||||
(
|
||||
None,
|
||||
None,
|
||||
Self::infer_mime_type(mime_type, &temp_file),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
};
|
||||
NewFileResult {
|
||||
path: temp_file,
|
||||
@ -115,6 +126,8 @@ impl FileStore {
|
||||
width,
|
||||
height,
|
||||
blur_hash: None,
|
||||
duration,
|
||||
bitrate,
|
||||
}
|
||||
};
|
||||
|
||||
@ -194,6 +207,8 @@ impl FileStore {
|
||||
height: Some(compressed_result.height as u32),
|
||||
blur_hash: None,
|
||||
mime_type: compressed_result.mime_type,
|
||||
duration: Some(compressed_result.duration),
|
||||
bitrate: Some(compressed_result.bitrate),
|
||||
#[cfg(feature = "labels")]
|
||||
labels,
|
||||
})
|
||||
|
@ -66,6 +66,8 @@ impl WebpProcessor {
|
||||
mime_type: "image/webp".to_string(),
|
||||
width: image_stream.width,
|
||||
height: image_stream.height,
|
||||
duration: probe.duration,
|
||||
bitrate: probe.bitrate as u32,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -126,6 +128,8 @@ pub struct NewFileProcessorResult {
|
||||
pub mime_type: String,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub duration: f32,
|
||||
pub bitrate: u32,
|
||||
}
|
||||
|
||||
pub fn can_compress(mime_type: &str) -> bool {
|
||||
|
@ -81,6 +81,13 @@ impl Nip94Event {
|
||||
if let (Some(w), Some(h)) = (upload.width, upload.height) {
|
||||
tags.push(vec!["dim".to_string(), format!("{}x{}", w, h)])
|
||||
}
|
||||
if let Some(d) = &upload.duration {
|
||||
tags.push(vec!["duration".to_string(), d.to_string()]);
|
||||
}
|
||||
if let Some(b) = &upload.bitrate {
|
||||
tags.push(vec!["bitrate".to_string(), b.to_string()]);
|
||||
}
|
||||
|
||||
#[cfg(feature = "labels")]
|
||||
for l in &upload.labels {
|
||||
let val = if l.label.contains(',') {
|
||||
|
Loading…
x
Reference in New Issue
Block a user