feat: create GenreService to load film genres from API

This commit is contained in:
Chneemann 2025-05-01 08:03:32 +02:00
parent 75c99b28ad
commit 6bfd167122
4 changed files with 72 additions and 23 deletions

View file

@ -3,6 +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 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
@ -24,6 +25,15 @@ 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 genre_list(request):
"""
Return a list of film genres from static choices.
"""
genres = [{'code': code, 'name': name} for code, name in FILM_GENRES]
return Response(genres)
@api_view(['GET']) @api_view(['GET'])
@permission_classes([IsAuthenticated]) @permission_classes([IsAuthenticated])
def check_video_resolutions(request, id): def check_video_resolutions(request, id):

View file

@ -23,8 +23,9 @@ urlpatterns = [
# Content URLs # Content URLs
path('videos/', video_views.video_list, name='video_list'), path('videos/', video_views.video_list, name='video_list'),
path('video/upload/', video_views.video_upload, name='video_upload'), path('video/upload/', video_views.video_upload, name='video_upload'),
path('video/genres/', video_views.genre_list, name='genre_list'),
path('video/<int:id>/', video_views.check_video_resolutions, name='check_video_resolutions'), path('video/<int:id>/', video_views.check_video_resolutions, name='check_video_resolutions'),
# Users URLs # Users URLs
path('users/', user_views.user_list, name='user_list'), path('users/', user_views.user_list, name='user_list'),
path('users/<int:id>/', user_views.user_detail, name='user_detail'), path('users/<int:id>/', user_views.user_detail, name='user_detail'),

View file

@ -5,10 +5,13 @@ import {
EventEmitter, EventEmitter,
HostListener, HostListener,
Input, Input,
OnInit,
Output, Output,
} 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 { FilmGenre, GenreService } from '../../../services/genre.service';
import { Subject, takeUntil } from 'rxjs';
@Component({ @Component({
selector: 'app-categories', selector: 'app-categories',
@ -17,7 +20,7 @@ import { VideoListComponent } from './video-list/video-list.component';
templateUrl: './categories.component.html', templateUrl: './categories.component.html',
styleUrl: './categories.component.scss', styleUrl: './categories.component.scss',
}) })
export class CategoriesComponent implements AfterViewInit { export class CategoriesComponent implements OnInit, AfterViewInit {
@Input() videos: any[] = []; @Input() videos: any[] = [];
@Input() currentVideo: number = 0; @Input() currentVideo: number = 0;
@Input() favoriteVideos: any[] = []; @Input() favoriteVideos: any[] = [];
@ -26,32 +29,34 @@ export class CategoriesComponent implements AfterViewInit {
environmentBaseUrl: string = environment.baseUrl; environmentBaseUrl: string = environment.baseUrl;
isScrollable: boolean = false; isScrollable: boolean = false;
filmGenres: FilmGenre[] = [];
filmGenres = [ private destroy$ = new Subject<void>();
{ code: 'action', name: 'Action' },
{ code: 'adventure', name: 'Adventure' },
{ code: 'animation', name: 'Animation' },
{ code: 'anime', name: 'Anime' },
{ code: 'comedy', name: 'Comedy' },
{ code: 'crime', name: 'Crime' },
{ code: 'documentary', name: 'Documentary' },
{ code: 'drama', name: 'Drama' },
{ code: 'fantasy', name: 'Fantasy' },
{ code: 'horror', name: 'Horror' },
{ code: 'musical', name: 'Musical' },
{ code: 'mystery', name: 'Mystery' },
{ code: 'other', name: 'Miscellaneous' },
{ code: 'romance', name: 'Romance' },
{ code: 'science_fiction', name: 'Science Fiction' },
{ code: 'thriller', name: 'Thriller' },
{ code: 'war', name: 'War' },
{ code: 'western', name: 'Western' },
];
ngAfterViewInit() { constructor(private genreService: GenreService) {}
ngOnInit(): void {
this.getFilmGenreNames();
}
ngAfterViewInit(): void {
this.checkScroll(); this.checkScroll();
} }
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
getFilmGenreNames(): void {
this.genreService
.getGenres()
.pipe(takeUntil(this.destroy$))
.subscribe((genres) => {
this.filmGenres = genres;
});
}
openCurrentVideo(videoId: number) { openCurrentVideo(videoId: number) {
this.currentVideo = videoId; this.currentVideo = videoId;
this.currentVideoId.emit(videoId); this.currentVideoId.emit(videoId);

View file

@ -0,0 +1,33 @@
import { Injectable } from '@angular/core';
import { Observable, of, tap } from 'rxjs';
import { ApiService } from './api.service';
export interface FilmGenre {
code: string;
name: string;
}
@Injectable({
providedIn: 'root',
})
export class GenreService {
private genres: FilmGenre[] = [];
constructor(private apiService: ApiService) {}
/**
* Returns the genres either from the cache or from the API endpoint.
* @returns observable of movie genres
*/
getGenres(): Observable<FilmGenre[]> {
if (this.genres.length > 0) {
return of(this.genres);
}
return this.apiService.get<FilmGenre[]>('/video/genres/', true).pipe(
tap((data) => {
this.genres = data;
})
);
}
}