[feature] Use gifv type for short soundless mp4 videos (#3182)

This commit is contained in:
tobi 2024-08-08 10:12:16 +02:00 committed by GitHub
parent 3045782b49
commit b19cfee7ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 49 additions and 8 deletions

View file

@ -91,6 +91,7 @@ type Thumbnail struct {
FileTypeImage FileType = 1 // FileTypeImage is for jpegs, pngs, and standard gifs FileTypeImage FileType = 1 // FileTypeImage is for jpegs, pngs, and standard gifs
FileTypeAudio FileType = 2 // FileTypeAudio is for audio-only files (no video) FileTypeAudio FileType = 2 // FileTypeAudio is for audio-only files (no video)
FileTypeVideo FileType = 3 // FileTypeVideo is for files with audio + visual FileTypeVideo FileType = 3 // FileTypeVideo is for files with audio + visual
FileTypeGifv FileType = 4 // FileTypeGifv is for short video-only files (20s or less, mp4, no audio).
) )
// String returns a stringified, frontend API compatible form of FileType. // String returns a stringified, frontend API compatible form of FileType.
@ -104,6 +105,8 @@ func (t FileType) String() string {
return "audio" return "audio"
case FileTypeVideo: case FileTypeVideo:
return "video" return "video"
case FileTypeGifv:
return "gifv"
default: default:
panic("invalid filetype") panic("invalid filetype")
} }

View file

@ -305,7 +305,15 @@ func (res *result) GetFileType() (gtsmodel.FileType, string) {
case "mov,mp4,m4a,3gp,3g2,mj2": case "mov,mp4,m4a,3gp,3g2,mj2":
switch { switch {
case len(res.video) > 0: case len(res.video) > 0:
return gtsmodel.FileTypeVideo, "mp4" if len(res.audio) == 0 &&
res.duration <= 30 {
// Short, soundless
// video file aka gifv.
return gtsmodel.FileTypeGifv, "mp4"
} else {
// Video file (with or without audio).
return gtsmodel.FileTypeVideo, "mp4"
}
case len(res.audio) > 0 && case len(res.audio) > 0 &&
res.audio[0].codec == "aac": res.audio[0].codec == "aac":
// m4a only supports [aac] audio. // m4a only supports [aac] audio.

View file

@ -202,7 +202,8 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
switch p.media.Type { switch p.media.Type {
case gtsmodel.FileTypeImage, case gtsmodel.FileTypeImage,
gtsmodel.FileTypeVideo: gtsmodel.FileTypeVideo,
gtsmodel.FileTypeGifv:
// Attempt to clean as metadata from file as possible. // Attempt to clean as metadata from file as possible.
if err := clearMetadata(ctx, temppath); err != nil { if err := clearMetadata(ctx, temppath); err != nil {
return gtserror.Newf("error cleaning metadata: %w", err) return gtserror.Newf("error cleaning metadata: %w", err)

View file

@ -26,6 +26,8 @@ const Prism = require("./prism.js");
Prism.manual = true; Prism.manual = true;
Prism.highlightAll(); Prism.highlightAll();
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
let [_, _user, type, id] = window.location.pathname.split("/"); let [_, _user, type, id] = window.location.pathname.split("/");
if (type == "statuses") { if (type == "statuses") {
let firstStatus = document.getElementsByClassName("thread")[0].children[0]; let firstStatus = document.getElementsByClassName("thread")[0].children[0];
@ -49,9 +51,12 @@ new PhotoswipeCaptionPlugin(lightbox, {
lightbox.addFilter('itemData', (item) => { lightbox.addFilter('itemData', (item) => {
const el = item.element; const el = item.element;
if (el && el.classList.contains("plyr-video")) { if (
el &&
el.classList.contains("plyr-video") &&
el._plyrContainer !== undefined
) {
const parentNode = el._plyrContainer.parentNode; const parentNode = el._plyrContainer.parentNode;
return { return {
alt: el.getAttribute("alt"), alt: el.getAttribute("alt"),
_video: { _video: {
@ -118,13 +123,14 @@ dynamicSpoiler("text-spoiler", (spoiler) => {
dynamicSpoiler("media-spoiler", (spoiler) => { dynamicSpoiler("media-spoiler", (spoiler) => {
const eye = spoiler.querySelector(".eye.button"); const eye = spoiler.querySelector(".eye.button");
const video = spoiler.querySelector(".plyr-video"); const video = spoiler.querySelector(".plyr-video");
const loopingAuto = !reduceMotion.matches && video != null && video.classList.contains("gifv");
return () => { return () => {
if (spoiler.open) { if (spoiler.open) {
eye.setAttribute("aria-label", "Hide media"); eye.setAttribute("aria-label", "Hide media");
} else { } else {
eye.setAttribute("aria-label", "Show media"); eye.setAttribute("aria-label", "Show media");
if (video) { if (video && !loopingAuto) {
video.pause(); video.pause();
} }
} }
@ -132,6 +138,22 @@ dynamicSpoiler("media-spoiler", (spoiler) => {
}); });
Array.from(document.getElementsByClassName("plyr-video")).forEach((video) => { Array.from(document.getElementsByClassName("plyr-video")).forEach((video) => {
const loopingAuto = !reduceMotion.matches && video.classList.contains("gifv");
if (loopingAuto) {
// If we're able to play this as a
// looping gifv, then do so, else fall
// back to user-controllable video player.
video.draggable = false;
video.autoplay = true;
video.loop = true;
video.classList.remove("photoswipe-slide");
video.classList.remove("plry-video");
video.load();
video.play();
return;
}
let player = new Plyr(video, { let player = new Plyr(video, {
title: video.title, title: video.title,
settings: ["loop"], settings: ["loop"],

View file

@ -99,7 +99,7 @@ media photoswipe-gallery {{ (len .) | oddOrEven }} {{ if eq (len .) 1 }}single{{
<i class="hide fa fa-fw fa-eye-slash" aria-hidden="true"></i> <i class="hide fa fa-fw fa-eye-slash" aria-hidden="true"></i>
<i class="show fa fa-fw fa-eye" aria-hidden="true"></i> <i class="show fa fa-fw fa-eye" aria-hidden="true"></i>
</span> </span>
{{- if eq .Type "video" }} {{- if or (eq .Type "video") (eq .Type "gifv") }}
{{- include "videoPreview" $media | indent 4 }} {{- include "videoPreview" $media | indent 4 }}
{{- else if eq .Type "image" }} {{- else if eq .Type "image" }}
{{- include "imagePreview" $media | indent 4 }} {{- include "imagePreview" $media | indent 4 }}
@ -107,11 +107,17 @@ media photoswipe-gallery {{ (len .) | oddOrEven }} {{ if eq (len .) 1 }}single{{
{{- include "audioPreview" $media | indent 4 }} {{- include "audioPreview" $media | indent 4 }}
{{- end }} {{- end }}
</summary> </summary>
{{- if eq .Type "video" }} {{- if or (eq .Type "video") (eq .Type "gifv") }}
<video <video
{{- if eq .Type "video" }}
preload="none" preload="none"
class="plyr-video photoswipe-slide" {{- else }}
preload="auto"
muted
{{- end }}
class="plyr-video photoswipe-slide{{- if eq .Type "gifv" }} gifv{{ end }}"
controls controls
playsinline
data-pswp-index="{{- $index -}}" data-pswp-index="{{- $index -}}"
poster="{{- .PreviewURL -}}" poster="{{- .PreviewURL -}}"
data-pswp-width="{{- $media.Meta.Small.Width -}}px" data-pswp-width="{{- $media.Meta.Small.Width -}}px"
@ -128,6 +134,7 @@ media photoswipe-gallery {{ (len .) | oddOrEven }} {{ if eq (len .) 1 }}single{{
preload="none" preload="none"
class="plyr-video photoswipe-slide" class="plyr-video photoswipe-slide"
controls controls
playsinline
data-pswp-index="{{- $index -}}" data-pswp-index="{{- $index -}}"
{{- if and $media.PreviewURL $media.Meta.Small.Width }} {{- if and $media.PreviewURL $media.Meta.Small.Width }}
poster="{{- .PreviewURL -}}" poster="{{- .PreviewURL -}}"