refactor: improve code readability and maintainability across components
This commit is contained in:
parent
039711b18f
commit
87b026ff35
14 changed files with 50 additions and 50 deletions
|
|
@ -3,7 +3,7 @@ certifi==2024.7.4
|
||||||
charset-normalizer==3.3.2
|
charset-normalizer==3.3.2
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
diff-match-patch==20230430
|
diff-match-patch==20230430
|
||||||
Django==5.0.10
|
Django==5.2
|
||||||
django-cors-headers==4.4.0
|
django-cors-headers==4.4.0
|
||||||
django-environ==0.12.0
|
django-environ==0.12.0
|
||||||
django-grip==3.5.0
|
django-grip==3.5.0
|
||||||
|
|
@ -30,7 +30,7 @@ rq==1.16.2
|
||||||
sentry-sdk==2.14.0
|
sentry-sdk==2.14.0
|
||||||
setuptools==72.1.0
|
setuptools==72.1.0
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
sqlparse==0.5.1
|
sqlparse==0.5.3
|
||||||
tablib==3.5.0
|
tablib==3.5.0
|
||||||
urllib3==2.2.2
|
urllib3==2.2.2
|
||||||
Werkzeug==3.0.6
|
Werkzeug==3.0.6
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ class VideoResource(resources.ModelResource):
|
||||||
@admin.register(Video)
|
@admin.register(Video)
|
||||||
class VideoAdmin(ImportExportModelAdmin):
|
class VideoAdmin(ImportExportModelAdmin):
|
||||||
resource_class = VideoResource
|
resource_class = VideoResource
|
||||||
readonly_fields = ('file_name',)
|
readonly_fields = ('file_path',)
|
||||||
list_display = ('title', 'file_name', 'created_at')
|
list_display = ('title', 'file_path', 'created_at')
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
FILM_GENRES = [
|
VIDEO_GENRES = [
|
||||||
('action', 'Action'),
|
('action', 'Action'),
|
||||||
('adventure', 'Adventure'),
|
('adventure', 'Adventure'),
|
||||||
('animation', 'Animation'),
|
('animation', 'Animation'),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from .class_assets import FILM_GENRES
|
from .class_assets import VIDEO_GENRES
|
||||||
import os
|
import os
|
||||||
|
|
||||||
class Video(models.Model):
|
class Video(models.Model):
|
||||||
|
|
@ -9,17 +9,17 @@ class Video(models.Model):
|
||||||
created_at = models.DateField(default=date.today)
|
created_at = models.DateField(default=date.today)
|
||||||
title = models.CharField(max_length=80)
|
title = models.CharField(max_length=80)
|
||||||
description = models.CharField(max_length=500)
|
description = models.CharField(max_length=500)
|
||||||
film_genre = models.CharField(max_length=20, choices=FILM_GENRES, blank=True, null=True)
|
genre = models.CharField(max_length=20, choices=VIDEO_GENRES, blank=True, null=True)
|
||||||
video_file = models.FileField(upload_to='videos/', blank=True, null=True)
|
file_path = 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)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'({self.id}) {self.title}'
|
return f'({self.id}) {self.title}'
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.video_file and not self.file_name:
|
if self.file_path and not self.file_name:
|
||||||
video_file_path = self.video_file.path
|
file_path = self.file_path.path
|
||||||
base_filename, _ = os.path.splitext(os.path.basename(video_file_path))
|
base_filename, _ = os.path.splitext(os.path.basename(file_path))
|
||||||
self.file_name = base_filename
|
self.file_name = base_filename
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
@ -19,10 +19,10 @@ def video_post_save(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
#Convert video
|
#Convert video
|
||||||
for resolution in ["1280x720", "640x360", "1920x1080"]:
|
for resolution in ["1280x720", "640x360", "1920x1080"]:
|
||||||
queue.enqueue(convert_video_to_hls, instance.video_file.path, resolution, instance.id)
|
queue.enqueue(convert_video_to_hls, instance.file_path.path, resolution, 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.file_path.path)
|
||||||
|
|
||||||
@receiver(post_delete, sender=Video)
|
@receiver(post_delete, sender=Video)
|
||||||
def auto_delete_file_on_delete(sender, instance, **kwargs):
|
def auto_delete_file_on_delete(sender, instance, **kwargs):
|
||||||
|
|
@ -30,7 +30,7 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
|
||||||
Deletes the video and all converted files from the file system,
|
Deletes the video and all converted files from the file system,
|
||||||
when the corresponding `Video` object is deleted.
|
when the corresponding `Video` object is deleted.
|
||||||
"""
|
"""
|
||||||
main_directory = os.path.dirname(instance.video_file.path)
|
main_directory = os.path.dirname(instance.file_path.path)
|
||||||
parent_directory = os.path.dirname(main_directory)
|
parent_directory = os.path.dirname(main_directory)
|
||||||
model_directory = os.path.join(main_directory, str(instance.id))
|
model_directory = os.path.join(main_directory, str(instance.id))
|
||||||
thumbnail_directory = os.path.join(parent_directory, 'thumbnails', str(instance.id))
|
thumbnail_directory = os.path.join(parent_directory, 'thumbnails', str(instance.id))
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ 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.file_path.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]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from .serializer import VideoSerializer
|
from .serializer import VideoSerializer
|
||||||
from .models import Video
|
from .models import Video
|
||||||
from .class_assets import FILM_GENRES
|
from .class_assets import VIDEO_GENRES
|
||||||
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
|
||||||
|
|
@ -29,9 +29,9 @@ def video_list(request):
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
def genre_list(request):
|
def genre_list(request):
|
||||||
"""
|
"""
|
||||||
Return a list of film genres from static choices.
|
Return a list of video genres from static choices.
|
||||||
"""
|
"""
|
||||||
genres = [{'code': code, 'name': name} for code, name in FILM_GENRES]
|
genres = [{'code': code, 'name': name} for code, name in VIDEO_GENRES]
|
||||||
return Response(genres)
|
return Response(genres)
|
||||||
|
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,14 @@
|
||||||
@if (genres$ | async; as genres) {
|
@if (genres$ | async; as genres) {
|
||||||
|
|
||||||
<!-- Loop through genres -->
|
<!-- Loop through genres -->
|
||||||
@for (filmGenre of genres; track filmGenre) {
|
@for (genre of genres; track genre) {
|
||||||
|
|
||||||
<!-- If genre has videos -->
|
<!-- If genre has videos -->
|
||||||
@if (getAllVideos(filmGenre.code).length > 0) {
|
@if (getAllVideos(genre.code).length > 0) {
|
||||||
<div class="category">
|
<div class="category">
|
||||||
<p>{{ filmGenre.name }}</p>
|
<p>{{ genre.name }}</p>
|
||||||
<app-video-list
|
<app-video-list
|
||||||
[videos]="getAllVideos(filmGenre.code)"
|
[videos]="getAllVideos(genre.code)"
|
||||||
[currentVideo]="currentVideo"
|
[currentVideo]="currentVideo"
|
||||||
[watchedVideos]="watchedVideos"
|
[watchedVideos]="watchedVideos"
|
||||||
(currentVideoId)="openCurrentVideo($event)"
|
(currentVideoId)="openCurrentVideo($event)"
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ import {
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { environment } from '../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
import { VideoListComponent } from './video-list/video-list.component';
|
import { VideoListComponent } from './video-list/video-list.component';
|
||||||
import { FilmGenreService } from '../../../services/film-genre.service';
|
|
||||||
import { Video } from '../../../interfaces/video.interface';
|
import { Video } from '../../../interfaces/video.interface';
|
||||||
|
import { GenreService } from '../../../services/genre.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-categories',
|
selector: 'app-categories',
|
||||||
|
|
@ -30,9 +30,9 @@ export class CategoriesComponent implements AfterViewInit {
|
||||||
environmentBaseUrl: string = environment.baseUrl;
|
environmentBaseUrl: string = environment.baseUrl;
|
||||||
isScrollable: boolean = false;
|
isScrollable: boolean = false;
|
||||||
|
|
||||||
genres$ = this.filmGenreService.getGenres();
|
genres$ = this.genreService.getGenres();
|
||||||
|
|
||||||
constructor(private filmGenreService: FilmGenreService) {}
|
constructor(private genreService: GenreService) {}
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.checkScroll();
|
this.checkScroll();
|
||||||
|
|
@ -46,8 +46,8 @@ export class CategoriesComponent implements AfterViewInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllVideos(filmGenre: string) {
|
getAllVideos(genre: string) {
|
||||||
return this.videos.filter((video) => video.film_genre === filmGenre);
|
return this.videos.filter((video) => video.genre === genre);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFavoriteVideos() {
|
getFavoriteVideos() {
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,11 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<select
|
<select
|
||||||
id="filmGenre"
|
id="genre"
|
||||||
name="filmGenre"
|
name="genre"
|
||||||
#filmGenre="ngModel"
|
#genre="ngModel"
|
||||||
[(ngModel)]="videoData.filmGenre"
|
[(ngModel)]="videoData.genre"
|
||||||
[class.error-border]="!filmGenre.valid && filmGenre.touched"
|
[class.error-border]="!genre.valid && genre.touched"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="action">Action</option>
|
<option value="action">Action</option>
|
||||||
|
|
@ -66,8 +66,8 @@
|
||||||
<option value="western">Western</option>
|
<option value="western">Western</option>
|
||||||
</select>
|
</select>
|
||||||
<div class="error-msg">
|
<div class="error-msg">
|
||||||
@if (!filmGenre.valid && filmGenre.touched) {
|
@if (!genre.valid && genre.touched) {
|
||||||
<p>Please enter a movie genre</p>
|
<p>Please enter a genre</p>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
|
@ -94,8 +94,8 @@
|
||||||
[disabled]="
|
[disabled]="
|
||||||
!videoData.title ||
|
!videoData.title ||
|
||||||
!videoData.description ||
|
!videoData.description ||
|
||||||
!videoData.filmGenre ||
|
!videoData.genre ||
|
||||||
!videoData.videoFile ||
|
!videoData.file ||
|
||||||
videoData.send
|
videoData.send
|
||||||
"
|
"
|
||||||
></app-btn-large>
|
></app-btn-large>
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,8 @@ export class UploadVideoComponent {
|
||||||
videoData = {
|
videoData = {
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
filmGenre: '',
|
genre: '',
|
||||||
videoFile: null as File | null,
|
file: null as File | null,
|
||||||
send: false,
|
send: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ export class UploadVideoComponent {
|
||||||
isOneFile(event: any) {
|
isOneFile(event: any) {
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
this.videoData.videoFile = file;
|
this.videoData.file = file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
isFileSize(event: any) {
|
isFileSize(event: any) {
|
||||||
|
|
@ -93,9 +93,9 @@ export class UploadVideoComponent {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('title', this.videoData.title);
|
formData.append('title', this.videoData.title);
|
||||||
formData.append('description', this.videoData.description);
|
formData.append('description', this.videoData.description);
|
||||||
formData.append('film_genre', this.videoData.filmGenre);
|
formData.append('genre', this.videoData.genre);
|
||||||
if (this.videoData.videoFile) {
|
if (this.videoData.file) {
|
||||||
formData.append('video_file', this.videoData.videoFile);
|
formData.append('file_path', this.videoData.file);
|
||||||
}
|
}
|
||||||
return formData;
|
return formData;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
export interface FilmGenre {
|
export interface Genre {
|
||||||
code: string;
|
code: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ export interface Video {
|
||||||
created_at: string;
|
created_at: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
film_genre: string;
|
genre: string;
|
||||||
video_file: string;
|
file_path: string;
|
||||||
file_name: string;
|
file_name: string;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable, of, tap } from 'rxjs';
|
import { Observable, of, tap } from 'rxjs';
|
||||||
import { ApiService } from './api.service';
|
import { ApiService } from './api.service';
|
||||||
import { FilmGenre } from '../interfaces/film-genre.interface';
|
import { Genre } from '../interfaces/genre.interface';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class FilmGenreService {
|
export class GenreService {
|
||||||
private genres: FilmGenre[] = [];
|
private genres: Genre[] = [];
|
||||||
|
|
||||||
constructor(private apiService: ApiService) {}
|
constructor(private apiService: ApiService) {}
|
||||||
|
|
||||||
|
|
@ -15,12 +15,12 @@ export class FilmGenreService {
|
||||||
* Returns the genres either from the cache or from the API endpoint.
|
* Returns the genres either from the cache or from the API endpoint.
|
||||||
* @returns observable of movie genres
|
* @returns observable of movie genres
|
||||||
*/
|
*/
|
||||||
getGenres(): Observable<FilmGenre[]> {
|
getGenres(): Observable<Genre[]> {
|
||||||
if (this.genres.length > 0) {
|
if (this.genres.length > 0) {
|
||||||
return of(this.genres);
|
return of(this.genres);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.apiService.get<FilmGenre[]>('/video/genres/', true).pipe(
|
return this.apiService.get<Genre[]>('/video/genres/', true).pipe(
|
||||||
tap((data) => {
|
tap((data) => {
|
||||||
this.genres = data;
|
this.genres = data;
|
||||||
})
|
})
|
||||||
Loading…
Reference in a new issue