check whether the uploaded videos are available in the resolution

This commit is contained in:
Chneemann 2024-08-20 19:40:32 +02:00
parent 012161a316
commit a93e225872
8 changed files with 140 additions and 4 deletions

View file

@ -7,7 +7,10 @@ from .models import Video
from django.core.cache.backends.base import DEFAULT_TIMEOUT from django.core.cache.backends.base import DEFAULT_TIMEOUT
from django.views.decorators.cache import cache_page from django.views.decorators.cache import cache_page
from django.conf import settings from django.conf import settings
from django.http import FileResponse, Http404
from rest_framework.parsers import MultiPartParser, FormParser from rest_framework.parsers import MultiPartParser, FormParser
from django.http import JsonResponse
import os
CACHETTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT) CACHETTL = getattr(settings, 'CACHE_TTL', DEFAULT_TIMEOUT)
@ -23,6 +26,29 @@ def video_list(request):
serializer = VideoSerializer(videos, many=True) serializer = VideoSerializer(videos, many=True)
return Response(serializer.data) return Response(serializer.data)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def check_video(request, id):
"""
Check if a specific video exists in 480p, 720p, and 1080p resolutions
URL: /content/movie/<video_id>/
"""
resolutions = ['480', '720', '1080']
result = {}
try:
video = Video.objects.get(id=id)
except Video.DoesNotExist:
return JsonResponse({'error': 'Video not found'}, status=404)
video_dir = os.path.join(settings.MEDIA_ROOT, 'videos', str(id))
for res in resolutions:
video_file_name = f"{video.file_name}_{res}p.m3u8"
video_file_path = os.path.join(video_dir, video_file_name)
result[res] = os.path.exists(video_file_path)
return JsonResponse(result)
@api_view(['POST']) @api_view(['POST'])
@permission_classes([IsAuthenticated]) @permission_classes([IsAuthenticated])
def video_upload(request): def video_upload(request):

View file

@ -38,6 +38,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/movie/<int:id>/', content_views.check_video),
# Users URLs # Users URLs
path('users/', user_views.user_list), path('users/', user_views.user_list),

View file

@ -6,6 +6,7 @@
<app-hero-banner <app-hero-banner
[currentMovie]="currentMovie" [currentMovie]="currentMovie"
(playMovie)="playVideo($event)" (playMovie)="playVideo($event)"
(movieIsUploadedChange)="onMovieIsUploadedChange($event)"
></app-hero-banner> ></app-hero-banner>
<app-categories <app-categories
[movies]="movies" [movies]="movies"

View file

@ -1,8 +1,10 @@
import { import {
Component, Component,
EventEmitter, EventEmitter,
OnChanges,
OnInit, OnInit,
Output, Output,
SimpleChanges,
ViewChild, ViewChild,
} from '@angular/core'; } from '@angular/core';
import { HeaderComponent } from '../../../shared/components/header/header.component'; import { HeaderComponent } from '../../../shared/components/header/header.component';
@ -37,6 +39,11 @@ export class BrowseComponent implements OnInit {
playMovie: string = ''; playMovie: string = '';
uploadMovieOverview: boolean = false; uploadMovieOverview: boolean = false;
currentResolution: '480p' | '720p' | '1080p' = '720p'; currentResolution: '480p' | '720p' | '1080p' = '720p';
movieIsUploaded: { [resolution: string]: boolean } = {
'480': false,
'720': false,
'1080': false,
};
constructor( constructor(
private authService: AuthService, private authService: AuthService,
@ -48,6 +55,10 @@ export class BrowseComponent implements OnInit {
this.currentMovie.length === 0 ? this.loadRandomMovie() : null; this.currentMovie.length === 0 ? this.loadRandomMovie() : null;
} }
onMovieIsUploadedChange(newStatus: { [resolution: string]: boolean }) {
this.movieIsUploaded = newStatus;
}
changeResolution(resolution: '480p' | '720p' | '1080p') { changeResolution(resolution: '480p' | '720p' | '1080p') {
if (this.videoPlayer) { if (this.videoPlayer) {
this.videoPlayer.switchResolution(resolution); this.videoPlayer.switchResolution(resolution);

View file

@ -22,6 +22,7 @@
[value]="'Play'" [value]="'Play'"
[imgPath]="'play'" [imgPath]="'play'"
[imgReverse]="true" [imgReverse]="true"
[disabled]="!isAnyResolutionUploaded()"
(click)=" (click)="
playMovieId( playMovieId(
environmentBaseUrl + environmentBaseUrl +

View file

@ -5,6 +5,7 @@ import {
EventEmitter, EventEmitter,
Input, Input,
Output, Output,
SimpleChanges,
} 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';
@ -20,11 +21,40 @@ import { environment } from '../../../../environments/environment';
export class HeroBannerComponent { export class HeroBannerComponent {
@Input() currentMovie: any[] = []; @Input() currentMovie: any[] = [];
@Output() playMovie = new EventEmitter<string>(); @Output() playMovie = new EventEmitter<string>();
@Output() movieIsUploadedChange = new EventEmitter<{
[resolution: string]: boolean;
}>();
environmentBaseUrl: string = environment.baseUrl; environmentBaseUrl: string = environment.baseUrl;
movieIsUploaded: { [resolution: string]: boolean } = {
'480': false,
'720': false,
'1080': false,
};
constructor(private el: ElementRef, private movieService: MovieService) {} constructor(private el: ElementRef, private movieService: MovieService) {}
ngOnChanges(changes: SimpleChanges) {
if (changes['currentMovie'] && this.currentMovie.length > 0) {
const movieId = this.currentMovie[0]?.id;
if (movieId) {
this.movieService
.isMovieResolutionUploaded(movieId)
.subscribe((resolutions) => {
this.movieIsUploaded = resolutions;
this.movieIsUploadedChange.emit(this.movieIsUploaded);
});
}
}
}
isAnyResolutionUploaded(): boolean {
return (
this.movieIsUploaded['480'] ||
this.movieIsUploaded['720'] ||
this.movieIsUploaded['1080']
);
}
playMovieId(videoPath: string) { playMovieId(videoPath: string) {
this.playMovie.emit(videoPath); this.playMovie.emit(videoPath);
} }

View file

@ -1,7 +1,7 @@
export const environment = { export const environment = {
// Development // Development
// baseUrl: 'http://127.0.0.1:8000', baseUrl: 'http://127.0.0.1:8000',
// Live // Live
baseUrl: 'https://videoflix-django.andre-kempf.com', // baseUrl: 'https://videoflix-django.andre-kempf.com',
}; };

View file

@ -1,12 +1,14 @@
import { HttpClient, HttpHeaders } from '@angular/common/http'; import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { lastValueFrom, Observable, of } from 'rxjs'; import { BehaviorSubject, lastValueFrom, Observable, of } from 'rxjs';
import { environment } from '../environments/environment'; import { environment } from '../environments/environment';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class MovieService { export class MovieService {
private movieCache: { [key: number]: { [resolution: string]: boolean } } = {};
constructor(private http: HttpClient) {} constructor(private http: HttpClient) {}
getAllMovies(): Promise<any> { getAllMovies(): Promise<any> {
@ -36,4 +38,68 @@ export class MovieService {
Authorization: `Token ${authToken}`, Authorization: `Token ${authToken}`,
}); });
} }
/**
* Checks if a movie is uploaded in multiple resolutions.
*
* @param {number} videoID - The ID of the movie to check.
* @returns {Observable<{ [resolution: string]: boolean }>} Observable emitting an object with the availability of each resolution.
*/
isMovieResolutionUploaded(
videoID: number
): Observable<{ [resolution: string]: boolean }> {
if (this.movieCache.hasOwnProperty(videoID)) {
const cachedResolutions = this.movieCache[videoID];
const missingResolutions = Object.keys(cachedResolutions).filter(
(res) => !cachedResolutions[res]
);
if (missingResolutions.length === 0) {
return new BehaviorSubject(cachedResolutions).asObservable();
} else {
return this.fetchAndCacheResolutions(videoID);
}
} else {
return this.fetchAndCacheResolutions(videoID);
}
}
/**
* Fetches the availability of multiple resolutions for a specific movie.
*
* @param {number} videoID - The ID of the movie to fetch resolutions for.
* @returns {Observable<{ [resolution: string]: boolean }>} Observable emitting an object with the availability of each resolution.
*/
private fetchAndCacheResolutions(
videoID: number
): Observable<{ [resolution: string]: boolean }> {
const url = `${environment.baseUrl}/content/movie/${videoID}/`;
const headers = this.getAuthHeaders();
return new Observable<{ [resolution: string]: boolean }>((observer) => {
this.http.get(url, { headers }).subscribe(
(response: any) => {
const resolutions = {
'480': response['480'] || false,
'720': response['720'] || false,
'1080': response['1080'] || false,
};
this.movieCache[videoID] = resolutions;
observer.next(resolutions);
observer.complete();
},
(error) => {
observer.next(
this.movieCache[videoID] || {
'480': false,
'720': false,
'1080': false,
}
);
observer.complete();
}
);
});
}
} }