Skip to content

Commit

Permalink
Video files can be trimmed and used as sample+poster
Browse files Browse the repository at this point in the history
  • Loading branch information
jrief committed Sep 4, 2024
1 parent d7a7705 commit da23f90
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 22 deletions.
4 changes: 2 additions & 2 deletions client/components/editor/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import CameraIcon from 'icons/camera.svg';

export default function Video(props) {
const sampleFields = {
posterFrame: document.getElementById('id_poster_frame') as HTMLInputElement,
sampleStart: document.getElementById('id_sample_start') as HTMLInputElement,
};
const settings = useContext(FinderSettings);
const style = {
Expand All @@ -36,7 +36,7 @@ export default function Video(props) {

const setPosterFrame = () => {
const currentTime = videoRef.current.getCurrentTime();
sampleFields.posterFrame.value = currentTime;
sampleFields.sampleStart.value = currentTime;
}

const controlButtons = [(isPlaying ?
Expand Down
4 changes: 3 additions & 1 deletion client/components/folder/Video.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export default function Video(props) {
}

return (
props.sampleUrl ?
<ReactPlayer
onMouseEnter={() => onPlayPause(false)}
onMouseLeave={() => onPlayPause(true)}
Expand All @@ -26,6 +27,7 @@ export default function Video(props) {
ref={videoRef}
width="100%"
height="100%"
/>
/> :
<img src={props.thumbnailUrl} />
);
}
7 changes: 4 additions & 3 deletions finder/contrib/video/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.contrib import admin
from django.forms.fields import CharField, FloatField
from django.forms.widgets import TextInput, NumberInput
from django.utils.translation import gettext_lazy as _

from entangled.forms import EntangledModelForm

Expand All @@ -12,15 +13,15 @@ class VideoForm(EntangledModelForm):
name = CharField(
widget=TextInput(attrs={'size': 100}),
)
poster_frame = FloatField(
initial=0,
sample_start = FloatField(
label=_("Poster timestamp"),
widget=NumberInput(attrs={'readonly': 'readonly'}),
required=False,
)

class Meta:
model = VideoFileModel
entangled_fields = {'meta_data': ['poster_frame']}
entangled_fields = {'meta_data': ['sample_start']}
untangled_fields = ['name']

class Media:
Expand Down
31 changes: 15 additions & 16 deletions finder/contrib/video/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ class Meta:
app_label = 'finder'

def get_sample_url(self):
poster_frame = self.meta_data.get('poster_frame')
if poster_frame is None:
sample_start = self.meta_data.get('sample_start')
if sample_start is None:
return
sample_path = self.get_sample_path(poster_frame)
sample_path = self.get_sample_path(sample_start)
if not default_storage.exists(sample_path):
(default_storage.base_location / sample_path.parent).mkdir(parents=True, exist_ok=True)
stream = ffmpeg.input(default_storage.path(self.file_path), ss=poster_frame)
stream = ffmpeg.input(default_storage.path(self.file_path), ss=sample_start)
video_stream = ffmpeg.trim(stream.video, duration=SAMPLE_DURATION)
video_stream = ffmpeg.filter(video_stream, 'crop', 'min(iw,ih)', 'min(iw,ih)')
video_stream = ffmpeg.filter(video_stream, 'scale', self.thumbnail_size, -1)
Expand All @@ -45,32 +45,31 @@ def get_sample_url(self):
return default_storage.url(sample_path)

def get_thumbnail_url(self):
poster_frame = self.meta_data.get('poster_frame')
if poster_frame is None:
sample_start = self.meta_data.get('sample_start')
if sample_start is None:
return self.fallback_thumbnail_url
poster_path = self.get_sample_path(poster_frame, suffix='.jpg')
poster_path = self.get_sample_path(sample_start, suffix='.jpg')
if not default_storage.exists(poster_path):
(default_storage.base_location / poster_path.parent).mkdir(parents=True, exist_ok=True)
stream = ffmpeg.input(default_storage.path(self.file_path), ss=poster_frame)
stream = ffmpeg.filter(stream, 'crop', 'min(iw,ih)', 'min(iw,ih)')
stream = ffmpeg.filter(stream, 'scale', self.thumbnail_size, -1)
stream = ffmpeg.output(stream, default_storage.path(poster_path), vframes=1)
stream = ffmpeg.input(default_storage.path(self.file_path), ss=sample_start)
video_stream = ffmpeg.filter(stream.video, 'crop', 'min(iw,ih)', 'min(iw,ih)')
video_stream = ffmpeg.filter(video_stream, 'scale', self.thumbnail_size, -1)
stream = ffmpeg.output(video_stream, default_storage.path(poster_path), vframes=1)
try:
ffmpeg.run(stream)
except ffmpeg.Error as exp:

Check failure on line 60 in finder/contrib/video/models.py

View workflow job for this annotation

GitHub Actions / flake8

local variable 'exp' is assigned to but never used
print(exp)
return self.fallback_thumbnail_url
return default_storage.url(poster_path)

def get_sample_path(self, poster_frame, suffix=None):
def get_sample_path(self, sample_start, suffix=None):
id = str(self.id)
thumbnail_folder = self.filer_public_thumbnails / f'{id[0:2]}/{id[2:4]}/{id}'
thumbnail_path = Path(self.file_name)
poster_frame = int(poster_frame * 100)
poster_path_template = '{stem}__{poster_frame}{suffix}'
sample_start = int(sample_start * 100)
poster_path_template = '{stem}__{sample_start}{suffix}'
suffix = suffix or thumbnail_path.suffix
return thumbnail_folder / poster_path_template.format(
stem=thumbnail_path.stem,
poster_frame=poster_frame,
sample_start=sample_start,
suffix=suffix,
)

0 comments on commit da23f90

Please sign in to comment.