check whether the uploaded videos are available in the resolution
This commit is contained in:
parent
012161a316
commit
a93e225872
8 changed files with 140 additions and 4 deletions
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
[value]="'Play'"
|
[value]="'Play'"
|
||||||
[imgPath]="'play'"
|
[imgPath]="'play'"
|
||||||
[imgReverse]="true"
|
[imgReverse]="true"
|
||||||
|
[disabled]="!isAnyResolutionUploaded()"
|
||||||
(click)="
|
(click)="
|
||||||
playMovieId(
|
playMovieId(
|
||||||
environmentBaseUrl +
|
environmentBaseUrl +
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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',
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue