feat(fuse,core): thumbnails for videos and for our own uploads

Videos without a server thumbnail fell through to the local fallback,
which downloaded the whole file, failed to decode it as an image and
wrote the video off for good. Grab one frame with ffmpeg instead, a
second in or from the start for shorter clips. Probe for ffmpeg before
downloading, bound every run, and skip videos over 256 MiB.
Migration V32 gives the videos written off so far another try.

Those videos had no server thumbnail because our uploads never sent one.
Every upload path (bulk upload, drained mount writes, conflict copies,
new revisions and folder sync) now attaches a thumbnail rendered from
the local file, kept under the 60 KiB Proton's own clients use. Files
that are not images or videos, or cannot be decoded, upload bare as
before; a .ts file only counts as video with an MPEG-TS sync byte.

ffmpeg is a new optional dependency in the PKGBUILDs.
This commit is contained in:
nvrl committed 2026-09-23 20:29:15 +02:00
1 parent 0b7236bd4a
commit 90e110e893
13 files changed
+573 -65

No files matched your search

+8 -2
View File
@@ -1226,6 +1226,9 @@ impl Core {
// Taken from the descriptor being streamed, so it names the content that
// actually goes up rather than whatever is at the path when we finish.
let streamed = LocalSig::from(&meta);
let thumbnails = self
.upload_thumbnails_async(path.clone(), name.to_string())
.await;
// Count the bytes as they are read, so this upload shows up in
// `GetQueueStatus` next to a manual one. The uid isn't known until the
// draft is sealed, so the transfer registers without one.
@@ -1246,7 +1249,7 @@ impl Core {
crate::media_type_for(name),
reader,
meta.len() as i64,
Vec::new(),
thumbnails,
Some(mtime),
false,
)
@@ -1274,6 +1277,9 @@ impl Core {
let meta = file.metadata().map_err(|e| format!("stat {rel}: {e}"))?;
let mtime = system_mtime(&meta);
let streamed = LocalSig::from(&meta);
let thumbnails = self
.upload_thumbnails_async(path.clone(), base_name(rel).to_string())
.await;
let reader = OwnedCountingReader::new(
file,
self.transfers.begin(
@@ -1284,7 +1290,7 @@ impl Core {
),
);
self.client
.upload_new_revision_from(uid, reader, meta.len() as i64, Vec::new(), Some(mtime))
.upload_new_revision_from(uid, reader, meta.len() as i64, thumbnails, Some(mtime))
.await
.map_err(|e| format!("upload revision {rel}: {e}"))?;
warn_if_torn(rel, &path, streamed);