refactor: unify video resolutions and clean up code

This commit is contained in:
Chneemann 2025-04-26 22:24:05 +02:00
parent bf4b1da3f9
commit df8305806f
7 changed files with 58 additions and 75 deletions

View file

@ -51,9 +51,9 @@ def delete_converted_files(video_file_path):
""" """
Delete all converted video files related to the original video file. Delete all converted video files related to the original video file.
""" """
resolutions = ["360", "720", "1080"] resolutions = ["360p", "720p", "1080p"]
for resolution in resolutions: for resolution in resolutions:
converted_video_path = video_file_path + f'_{resolution}p.mp4' converted_video_path = video_file_path + f'_{resolution}.mp4'
converted_video_path = remove_first_mp4(converted_video_path) converted_video_path = remove_first_mp4(converted_video_path)
if os.path.isfile(converted_video_path): if os.path.isfile(converted_video_path):
os.remove(converted_video_path) os.remove(converted_video_path)

View file

@ -30,7 +30,7 @@ def check_video_resolutions(request, id):
""" """
Check if a specific video exists in different resolutions (360p, 720p, 1080p). Check if a specific video exists in different resolutions (360p, 720p, 1080p).
""" """
resolutions = ['360', '720', '1080'] resolutions = ['360p', '720p', '1080p']
result = {} result = {}
try: try:
@ -41,7 +41,7 @@ def check_video_resolutions(request, id):
video_dir = os.path.join(settings.MEDIA_ROOT, 'videos', str(id)) video_dir = os.path.join(settings.MEDIA_ROOT, 'videos', str(id))
for res in resolutions: for res in resolutions:
video_file_name = f"{video.file_name}_{res}p.m3u8" video_file_name = f"{video.file_name}_{res}.m3u8"
video_file_path = os.path.join(video_dir, video_file_name) video_file_path = os.path.join(video_dir, video_file_name)
result[res] = os.path.exists(video_file_path) result[res] = os.path.exists(video_file_path)

View file

@ -42,9 +42,9 @@ export class HeroBannerComponent implements OnChanges {
playUrl: string = ''; playUrl: string = '';
environmentBaseUrl: string = environment.baseUrl; environmentBaseUrl: string = environment.baseUrl;
movieIsUploaded: { [resolution: string]: boolean } = { movieIsUploaded: { [resolution: string]: boolean } = {
'320': true, '320p': true,
'720': true, '720p': true,
'1080': true, '1080p': true,
}; };
constructor( constructor(
private movieService: MovieService, private movieService: MovieService,
@ -123,9 +123,9 @@ export class HeroBannerComponent implements OnChanges {
isAnyResolutionUploaded(): boolean { isAnyResolutionUploaded(): boolean {
return ( return (
this.movieIsUploaded['320'] || this.movieIsUploaded['320p'] ||
this.movieIsUploaded['720'] || this.movieIsUploaded['720p'] ||
this.movieIsUploaded['1080'] this.movieIsUploaded['1080p']
); );
} }

View file

@ -44,52 +44,30 @@
</div> </div>
<div class="center"> <div class="center">
<div class="resolution-controls"> <div class="resolution-controls">
<!-- Resolution buttons -->
@for (resolution of availableResolutions; track $index) {
<button <button
[ngClass]="{ [ngClass]="{
'resolution-btn': true, 'resolution-btn': true,
active: currentResolution === '360p', active: currentResolution === resolution,
'not-available': !movieIsUploaded['360'] 'not-available': !movieIsUploaded[resolution]
}"
[disabled]="!movieIsUploaded['360'] || currentResolution === '360p'"
(click)="changeResolution('360p')"
>
360p
</button>
<button
[ngClass]="{
'resolution-btn': true,
active: currentResolution === '720p',
'not-available': !movieIsUploaded['720']
}"
[disabled]="!movieIsUploaded['720'] || currentResolution === '720p'"
(click)="changeResolution('720p')"
>
720p
</button>
<button
[ngClass]="{
'resolution-btn': true,
active: currentResolution === '1080p',
'not-available': !movieIsUploaded['1080']
}" }"
[disabled]=" [disabled]="
!movieIsUploaded['1080'] || currentResolution === '1080p' !movieIsUploaded[resolution] || currentResolution === resolution
" "
(click)="changeResolution('1080p')" (click)="changeResolution(resolution)"
> >
1080p {{ resolution }}
</button> </button>
}
</div> </div>
<p <!-- Resolution instructions -->
*ngIf=" @if (isAnyResolutionUnavailable()) {
!movieIsUploaded['360'] || <p>
!movieIsUploaded['720'] ||
!movieIsUploaded['1080']
"
>
(If a button cannot be selected, the video will be converted, which (If a button cannot be selected, the video will be converted, which
may take a few seconds.) may take a few seconds.)
</p> </p>
}
</div> </div>
<div class="logo"> <div class="logo">
<img src="./../../../../assets/img/logo_ci.svg" alt="Logo" /> <img src="./../../../../assets/img/logo_ci.svg" alt="Logo" />

View file

@ -33,11 +33,12 @@ export class HomeComponent implements OnInit {
playMovie: string = ''; playMovie: string = '';
isLoading: boolean = true; isLoading: boolean = true;
uploadMovieOverview: boolean = false; uploadMovieOverview: boolean = false;
currentResolution: '360p' | '720p' | '1080p' = '720p'; availableResolutions: string[] = ['360p', '720p', '1080p'];
currentResolution = '720p';
movieIsUploaded: { [resolution: string]: boolean } = { movieIsUploaded: { [resolution: string]: boolean } = {
'360': false, '360p': false,
'720': false, '720p': false,
'1080': false, '1080p': false,
}; };
constructor( constructor(
@ -98,8 +99,8 @@ export class HomeComponent implements OnInit {
this.updateLikeMovies(); this.updateLikeMovies();
} }
changeResolution(resolution: '360p' | '720p' | '1080p') { changeResolution(resolution: string) {
if (this.videoPlayer && this.movieIsUploaded[resolution.replace('p', '')]) { if (this.videoPlayer && this.movieIsUploaded[resolution]) {
this.videoPlayer.switchResolution(resolution); this.videoPlayer.switchResolution(resolution);
this.currentResolution = resolution; this.currentResolution = resolution;
} }
@ -109,6 +110,7 @@ export class HomeComponent implements OnInit {
this.isLoading = true; this.isLoading = true;
try { try {
this.movies = await this.movieService.getAllMovies(); this.movies = await this.movieService.getAllMovies();
console.log(this.movies);
} finally { } finally {
this.isLoading = false; this.isLoading = false;
} }
@ -136,6 +138,12 @@ export class HomeComponent implements OnInit {
} }
} }
isAnyResolutionUnavailable(): boolean {
return this.availableResolutions.some(
(resolution) => !this.movieIsUploaded[resolution]
);
}
toggleUploadMovieOverview(value: any) { toggleUploadMovieOverview(value: any) {
this.uploadMovieOverview = value; this.uploadMovieOverview = value;
} }

View file

@ -62,7 +62,7 @@ export class VideoPlayerComponent implements OnInit, OnDestroy {
} }
} }
public switchResolution(resolution: '360p' | '720p' | '1080p') { public switchResolution(resolution: string) {
if (this.resolutionUrls[resolution]) { if (this.resolutionUrls[resolution]) {
if (this.hls) { if (this.hls) {
this.hls.loadSource(this.resolutionUrls[resolution]); this.hls.loadSource(this.resolutionUrls[resolution]);

View file

@ -1,12 +1,5 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { import { catchError, firstValueFrom, map, Observable, of } from 'rxjs';
BehaviorSubject,
catchError,
firstValueFrom,
map,
Observable,
of,
} from 'rxjs';
import { ApiService } from './api.service'; import { ApiService } from './api.service';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
@ -14,7 +7,10 @@ import { AuthService } from './auth.service';
providedIn: 'root', providedIn: 'root',
}) })
export class MovieService { export class MovieService {
private movieCache: { [key: number]: { [resolution: string]: boolean } } = {}; private movieCache: {
[key: number]: { [resolution: string]: boolean };
} = {};
private readonly availableResolutions = ['360p', '720p', '1080p'];
constructor( constructor(
private apiService: ApiService, private apiService: ApiService,
@ -53,35 +49,36 @@ export class MovieService {
} }
/** /**
* Checks if a movie with the given video ID has been uploaded in the following resolutions: 360p, 720p, 1080p. * Check if a movie is available in all resolutions on the server.
* This method caches the result to avoid unnecessary requests.
* *
* @param videoID the ID of the movie to check * @param videoID the ID of the movie to check
* @returns an observable that emits an object with the following properties: * @returns a promise resolving to an object with the available resolutions
* - '360': a boolean indicating whether the movie has been uploaded in 360p resolution * as keys and booleans indicating the availability as values.
* - '720': a boolean indicating whether the movie has been uploaded in 720p resolution
* - '1080': a boolean indicating whether the movie has been uploaded in 1080p resolution
*/ */
isMovieResolutionUploaded( isMovieResolutionUploaded(
videoID: number videoID: number
): Observable<{ [resolution: string]: boolean }> { ): Observable<{ [resolution: string]: boolean }> {
const cachedRes = this.movieCache[videoID]; const cachedResolutions = this.movieCache[videoID];
if (cachedRes && Object.values(cachedRes).every(Boolean)) {
return new BehaviorSubject(cachedRes).asObservable(); if (cachedResolutions && Object.values(cachedResolutions).every(Boolean)) {
return of(cachedResolutions);
} }
return this.apiService.get(`/video/movie/${videoID}/`, true).pipe( return this.apiService.get(`/video/movie/${videoID}/`, true).pipe(
map((res: any) => { map((res: any) => {
const resolutions = { const resolutions = Object.fromEntries(
'360': res['360'] || false, this.availableResolutions.map((r) => [r, !!res[r]])
'720': res['720'] || false, );
'1080': res['1080'] || false,
};
this.movieCache[videoID] = resolutions; this.movieCache[videoID] = resolutions;
return resolutions; return resolutions;
}), }),
catchError(() => { catchError((error) => {
console.error('Failed to fetch movie resolutions', error);
this.authService.logout(); this.authService.logout();
return of({ '360': false, '720': false, '1080': false }); return of(
Object.fromEntries(this.availableResolutions.map((r) => [r, false]))
);
}) })
); );
} }