refactor: improve code readability and maintainability across components

This commit is contained in:
Chneemann 2025-05-02 17:20:17 +02:00
parent 039711b18f
commit 87b026ff35
14 changed files with 50 additions and 50 deletions

View file

@ -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

View file

@ -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')

View file

@ -1,4 +1,4 @@
FILM_GENRES = [ VIDEO_GENRES = [
('action', 'Action'), ('action', 'Action'),
('adventure', 'Adventure'), ('adventure', 'Adventure'),
('animation', 'Animation'), ('animation', 'Animation'),

View file

@ -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)

View file

@ -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))

View file

@ -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]

View file

@ -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'])

View file

@ -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)"

View file

@ -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() {

View file

@ -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>

View file

@ -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;
} }

View file

@ -1,4 +1,4 @@
export interface FilmGenre { export interface Genre {
code: string; code: string;
name: string; name: string;
} }

View file

@ -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;
} }

View file

@ -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;
}) })