implement video thumbnail generation and display in movie banner

This commit is contained in:
Chneemann 2024-09-05 04:33:40 +02:00
parent 6f7c9d149b
commit 20a399b5c7
4 changed files with 73 additions and 13 deletions

View file

@ -44,7 +44,7 @@ def delete_original_video(source):
def create_thumbnails(instance, model_id): def create_thumbnails(instance, model_id):
""" """
Creates high-resolution and low-resolution thumbnails from a video file Creates high-resolution and low-resolution thumbnails from a video file.
""" """
video_file_path = instance.video_file.path video_file_path = instance.video_file.path
thumbnail_dir = os.path.join(settings.THUMBNAIL_DIR, str(model_id)) thumbnail_dir = os.path.join(settings.THUMBNAIL_DIR, str(model_id))
@ -59,10 +59,32 @@ def create_thumbnails(instance, model_id):
os.makedirs(thumbnail_dir) os.makedirs(thumbnail_dir)
try: try:
# Generate the thumbnails # Generate the image thumbnails
ffmpeg.input(video_file_path, ss=1).output(thumbnail_1080p_path, vf='scale=1920:-1', vframes=1).run(overwrite_output=True) ffmpeg.input(video_file_path, ss=1).output(thumbnail_1080p_path, vf='scale=1920:-1', vframes=1).run(overwrite_output=True)
ffmpeg.input(video_file_path, ss=1).output(thumbnail_480_path, vf='scale=720:-1', vframes=1).run(overwrite_output=True) ffmpeg.input(video_file_path, ss=1).output(thumbnail_480_path, vf='scale=720:-1', vframes=1).run(overwrite_output=True)
# Create the 10-second video thumbnail
create_video_thumbnail(video_file_path, thumbnail_dir, base_filename)
except ffmpeg._run.Error as e: except ffmpeg._run.Error as e:
error_message = e.stderr.decode() if e.stderr else "Unknown ffmpeg error" error_message = e.stderr.decode() if e.stderr else "Unknown ffmpeg error"
print(f"Ein Fehler ist aufgetreten: {error_message}") print(f"An error has occurred: {error_message}")
def create_video_thumbnail(video_file_path, thumbnail_dir, base_filename):
"""
Creates a 10-second video thumbnail from a video file without audio.
"""
video_thumbnail_filename = base_filename + '_video-thumbnail.mp4'
video_thumbnail_path = os.path.join(thumbnail_dir, video_thumbnail_filename)
try:
ffmpeg.input(video_file_path, ss=1, t=10).output(
video_thumbnail_path,
vf='scale=1280:-1',
vcodec='libx264',
an=None # Disable audio
).run(overwrite_output=True)
except ffmpeg._run.Error as e:
error_message = e.stderr.decode() if e.stderr else "Unknown ffmpeg error"
print(f"An error has occurred: {error_message}")

View file

@ -5,21 +5,39 @@
> >
<div <div
class="movie-banner" class="movie-banner"
[ngStyle]="{ [ngClass]="{
'background-image': fullVhBanner: !screenWidth
'url(' + }"
>
<video
#videoElement
*ngIf="currentMovie[0]?.file_name"
[src]="
environmentBaseUrl + environmentBaseUrl +
'/media/thumbnails/' + '/media/thumbnails/' +
currentMovie[0]?.id + currentMovie[0]?.id +
'/' + '/' +
currentMovie[0]?.file_name + currentMovie[0]?.file_name +
'_1080p.jpg' + '_video-thumbnail.mp4'
')', "
}" autoplay
[ngClass]="{ muted
fullVhBanner: !screenWidth loop
}" playsinline
></div> class="video-banner"
>
<img
[src]="
environmentBaseUrl +
'/media/thumbnails/' +
currentMovie[0]?.id +
'/' +
currentMovie[0]?.file_name +
'_1080p.jpg'
"
/>
</video>
</div>
@if (currentMovie.length > 0) { @if (currentMovie.length > 0) {
<div <div
class="content" class="content"

View file

@ -31,6 +31,12 @@ section {
} }
} }
.video-banner {
width: 100%;
height: 100%;
object-fit: cover;
}
.content { .content {
position: absolute; position: absolute;
width: 60%; width: 60%;

View file

@ -7,6 +7,7 @@ import {
OnChanges, OnChanges,
Output, Output,
SimpleChanges, SimpleChanges,
ViewChild,
} from '@angular/core'; } from '@angular/core';
import { BtnLargeComponent } from '../../../../shared/components/buttons/btn-large/btn-large.component'; import { BtnLargeComponent } from '../../../../shared/components/buttons/btn-large/btn-large.component';
import { MovieService } from '../../../../services/movie.service'; import { MovieService } from '../../../../services/movie.service';
@ -22,6 +23,7 @@ import { UserService } from '../../../../services/user.service';
styleUrl: './hero-banner.component.scss', styleUrl: './hero-banner.component.scss',
}) })
export class HeroBannerComponent implements OnChanges { export class HeroBannerComponent implements OnChanges {
@ViewChild('videoElement') videoElementRef!: ElementRef<HTMLVideoElement>;
@Input() currentMovie: any[] = []; @Input() currentMovie: any[] = [];
@Input() screenWidth: boolean = false; @Input() screenWidth: boolean = false;
@Input() favoriteMovies: any[] = []; @Input() favoriteMovies: any[] = [];
@ -44,6 +46,10 @@ export class HeroBannerComponent implements OnChanges {
public userService: UserService public userService: UserService
) {} ) {}
ngAfterViewInit() {
this.videoSpeed();
}
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (changes['currentMovie'] && this.currentMovie.length > 0) { if (changes['currentMovie'] && this.currentMovie.length > 0) {
const movieId = this.currentMovie[0]?.id; const movieId = this.currentMovie[0]?.id;
@ -54,10 +60,18 @@ export class HeroBannerComponent implements OnChanges {
this.movieIsUploaded = resolutions; this.movieIsUploaded = resolutions;
this.movieIsUploadedChange.emit(this.movieIsUploaded); this.movieIsUploadedChange.emit(this.movieIsUploaded);
}); });
setTimeout(() => this.videoSpeed(), 0);
} }
} }
} }
videoSpeed() {
if (this.videoElementRef) {
const videoElement = this.videoElementRef.nativeElement;
videoElement.playbackRate = 0.5;
}
}
toggleLikeMovie(movieId: number): void { toggleLikeMovie(movieId: number): void {
if (this.favoriteMovies.includes(movieId)) { if (this.favoriteMovies.includes(movieId)) {
this.favoriteMovies = this.favoriteMovies.filter((id) => id !== movieId); this.favoriteMovies = this.favoriteMovies.filter((id) => id !== movieId);