when the thumbnail is created the page is refreshed
This commit is contained in:
parent
f20fd0566f
commit
58208d11dd
7 changed files with 97 additions and 41 deletions
|
|
@ -9,6 +9,7 @@ class Video(models.Model):
|
||||||
film_genre = models.CharField(max_length=20, choices=FILM_GENRES, blank=True, null=True)
|
film_genre = models.CharField(max_length=20, choices=FILM_GENRES, blank=True, null=True)
|
||||||
video_file = models.FileField(upload_to='videos/', blank=True, null=True)
|
video_file = models.FileField(upload_to='videos/', blank=True, null=True)
|
||||||
file_name = models.CharField(max_length=50, blank=True, null=True)
|
file_name = models.CharField(max_length=50, blank=True, null=True)
|
||||||
|
thumbnail_created = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'({self.id}) {self.title}'
|
return f'({self.id}) {self.title}'
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from .models import Video
|
from .models import Video
|
||||||
from .tasks import convert_video_to_hls, create_thumbnails, delete_original_video
|
from .tasks import convert_video_to_hls, create_thumbnails, delete_original_video, update_thumbnail_status
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.db.models.signals import post_save, post_delete
|
from django.db.models.signals import post_save, post_delete
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
@ -16,12 +16,11 @@ def video_post_save(sender, instance, created, **kwargs):
|
||||||
queue = django_rq.get_queue("default", autocommit=True)
|
queue = django_rq.get_queue("default", autocommit=True)
|
||||||
|
|
||||||
#Create thumbnail
|
#Create thumbnail
|
||||||
queue.enqueue(create_thumbnails, instance, instance.id)
|
create_thumbnails(instance, instance.id)
|
||||||
|
|
||||||
#Convert video
|
#Convert video
|
||||||
queue.enqueue(convert_video_to_hls, instance.video_file.path, "480", instance.id)
|
for resolution in ["480", "720", "1080"]:
|
||||||
queue.enqueue(convert_video_to_hls, instance.video_file.path, "720", instance.id)
|
queue.enqueue(convert_video_to_hls, instance.video_file.path, resolution, instance.id)
|
||||||
queue.enqueue(convert_video_to_hls, instance.video_file.path, "1080", instance.id)
|
|
||||||
|
|
||||||
# Delete the original video file
|
# Delete the original video file
|
||||||
queue.enqueue(delete_original_video, instance.video_file.path)
|
queue.enqueue(delete_original_video, instance.video_file.path)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import ffmpeg
|
||||||
import os
|
import os
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from .models import Video
|
from .models import Video
|
||||||
|
from django_rq import get_queue
|
||||||
|
|
||||||
def convert_video_to_hls(source, resolution, model_id):
|
def convert_video_to_hls(source, resolution, model_id):
|
||||||
target_dir = os.path.join(os.path.dirname(source), str(model_id))
|
target_dir = os.path.join(os.path.dirname(source), str(model_id))
|
||||||
|
|
@ -38,8 +39,8 @@ def delete_original_video(source):
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
print(f"Error deleting original file: {e}")
|
print(f"Error deleting original file: {e}")
|
||||||
|
|
||||||
def create_thumbnails(self, model_id):
|
def create_thumbnails(instance, model_id):
|
||||||
video_file_path = self.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))
|
||||||
|
|
||||||
base_filename = os.path.splitext(os.path.basename(video_file_path))[0]
|
base_filename = os.path.splitext(os.path.basename(video_file_path))[0]
|
||||||
|
|
@ -52,8 +53,19 @@ def create_thumbnails(self, model_id):
|
||||||
os.makedirs(thumbnail_dir)
|
os.makedirs(thumbnail_dir)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Generate the 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)
|
||||||
|
# Update thumbnail status
|
||||||
|
update_thumbnail_status(model_id)
|
||||||
|
|
||||||
except ffmpeg._run.Error as e:
|
except ffmpeg._run.Error as e:
|
||||||
print(f"Ein Fehler ist aufgetreten: {e.stderr.decode()}")
|
print(f"Ein Fehler ist aufgetreten: {e.stderr.decode()}")
|
||||||
|
|
||||||
|
def update_thumbnail_status(video_id):
|
||||||
|
try:
|
||||||
|
video = Video.objects.get(id=video_id)
|
||||||
|
video.thumbnail_created = True
|
||||||
|
video.save()
|
||||||
|
except Video.DoesNotExist:
|
||||||
|
print(f"Video with id {video_id} does not exist.")
|
||||||
|
|
@ -16,12 +16,25 @@ CACHETTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
#@cache_page(CACHETTL)
|
#@cache_page(CACHETTL)
|
||||||
def video_list(request):
|
def video_list(request):
|
||||||
|
"""
|
||||||
if request.method == 'GET':
|
List all videos.
|
||||||
video = Video.objects.all()
|
"""
|
||||||
serializer = VideoSerializer(video, many=True)
|
videos = Video.objects.all()
|
||||||
|
serializer = VideoSerializer(videos, many=True)
|
||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
@api_view(['GET'])
|
||||||
|
@permission_classes([IsAuthenticated])
|
||||||
|
def check_thumbnail_status(request, video_id):
|
||||||
|
"""
|
||||||
|
Check the status of the thumbnail creation for a specific video.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
video = Video.objects.get(id=video_id)
|
||||||
|
return Response({'thumbnail_created': video.thumbnail_created})
|
||||||
|
except Video.DoesNotExist:
|
||||||
|
return Response({'error': 'Video not found'}, status=404)
|
||||||
|
|
||||||
@api_view(['POST'])
|
@api_view(['POST'])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
def video_upload(request):
|
def video_upload(request):
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ urlpatterns = [
|
||||||
# Content URLs
|
# Content URLs
|
||||||
path('content/', content_views.video_list),
|
path('content/', content_views.video_list),
|
||||||
path('content/upload/', content_views.video_upload),
|
path('content/upload/', content_views.video_upload),
|
||||||
|
path('content/<int:video_id>/status/', content_views.check_thumbnail_status),
|
||||||
|
|
||||||
# Users URLs
|
# Users URLs
|
||||||
path('users/', user_views.user_list),
|
path('users/', user_views.user_list),
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,30 @@ export class UploadMovieComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
async onSubmit(ngForm: NgForm) {
|
async onSubmit(ngForm: NgForm) {
|
||||||
if (ngForm.submitted && ngForm.form.valid) {
|
if (!ngForm.submitted || !ngForm.form.valid) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.movieData.send = true;
|
||||||
|
|
||||||
|
const formData = this.createFormData();
|
||||||
|
const response = await this.movieService
|
||||||
|
.uploadMovie(formData)
|
||||||
|
.toPromise();
|
||||||
|
const videoId = response.id;
|
||||||
|
|
||||||
|
await this.waitForThumbnailCreation(videoId);
|
||||||
|
|
||||||
|
ngForm.resetForm();
|
||||||
|
this.uploadMovieOverview();
|
||||||
|
this.movieData.send = false;
|
||||||
|
window.location.reload();
|
||||||
|
this.errorService.clearError();
|
||||||
|
} catch (error) {
|
||||||
|
this.errorService.handleError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createFormData(): FormData {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('title', this.movieData.title);
|
formData.append('title', this.movieData.title);
|
||||||
formData.append('description', this.movieData.description);
|
formData.append('description', this.movieData.description);
|
||||||
|
|
@ -52,19 +75,16 @@ export class UploadMovieComponent {
|
||||||
if (this.movieData.videoFile) {
|
if (this.movieData.videoFile) {
|
||||||
formData.append('video_file', this.movieData.videoFile);
|
formData.append('video_file', this.movieData.videoFile);
|
||||||
}
|
}
|
||||||
try {
|
return formData;
|
||||||
await this.movieService.uploadMovie(formData);
|
}
|
||||||
this.movieData.send = true;
|
|
||||||
setTimeout(() => {
|
private async waitForThumbnailCreation(videoId: number) {
|
||||||
ngForm.resetForm();
|
while (true) {
|
||||||
this.uploadMovieOverview();
|
const statusResponse = await this.movieService
|
||||||
this.movieData.send = false;
|
.checkThumbnailStatus(videoId)
|
||||||
window.location.reload();
|
.toPromise();
|
||||||
}, 1000);
|
if (statusResponse?.thumbnail_created) break;
|
||||||
this.errorService.clearError();
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
} catch (error) {
|
|
||||||
this.errorService.handleError(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,22 @@ export class MovieService {
|
||||||
return lastValueFrom(this.http.get(url, { headers }));
|
return lastValueFrom(this.http.get(url, { headers }));
|
||||||
}
|
}
|
||||||
|
|
||||||
async uploadMovie(formData: FormData) {
|
uploadMovie(formData: FormData): Observable<any> {
|
||||||
const headers = this.getAuthHeaders();
|
const headers = this.getAuthHeaders();
|
||||||
await lastValueFrom(
|
return this.http.post<any>(
|
||||||
this.http.post(`${environment.baseUrl}/content/upload/`, formData, {
|
`${environment.baseUrl}/content/upload/`,
|
||||||
headers,
|
formData,
|
||||||
})
|
{ headers }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
checkThumbnailStatus(
|
||||||
|
videoId: number
|
||||||
|
): Observable<{ thumbnail_created: boolean }> {
|
||||||
|
const headers = this.getAuthHeaders();
|
||||||
|
return this.http.get<{ thumbnail_created: boolean }>(
|
||||||
|
`${environment.baseUrl}/content/${videoId}/status/`,
|
||||||
|
{ headers }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue