diff --git a/backend/videoflix/content/views.py b/backend/videoflix/content/views.py index 28eb2a8..822d78d 100644 --- a/backend/videoflix/content/views.py +++ b/backend/videoflix/content/views.py @@ -30,8 +30,7 @@ def video_list(request): @permission_classes([IsAuthenticated]) def check_video(request, id): """ - Check if a specific video exists in 480p, 720p, and 1080p resolutions - URL: /content/movie// + Check if a specific video exists in different resolutions (480p, 720p, 1080p). """ resolutions = ['480', '720', '1080'] result = {} @@ -49,9 +48,13 @@ def check_video(request, id): result[res] = os.path.exists(video_file_path) return JsonResponse(result) + @api_view(['POST']) @permission_classes([IsAuthenticated]) def video_upload(request): + """ + Handle the upload of a video file. + """ parser_classes = (MultiPartParser, FormParser) if request.method == 'POST': serializer = VideoSerializer(data=request.data) diff --git a/frontend/src/app/components/home/browse/categories/categories.component.html b/frontend/src/app/components/home/browse/categories/categories.component.html index e4a4fa7..e998411 100644 --- a/frontend/src/app/components/home/browse/categories/categories.component.html +++ b/frontend/src/app/components/home/browse/categories/categories.component.html @@ -3,6 +3,7 @@

Added in the last 7 days

+ @for (movie of recentMovies(); track movie) {
} + +
+ + +
+ @for (filmGenre of filmGenres; track filmGenre) { - @if (allMovies(filmGenre).length > 0) { + @if (allMovies(filmGenre.code).length > 0) {
-

{{ filmGenre }}

+

{{ filmGenre.name }}

- @for (movie of allMovies(filmGenre); track movie) { + @for (movie of allMovies(filmGenre.code); track movie) {
} + +
+ + +
} } diff --git a/frontend/src/app/components/home/browse/categories/categories.component.scss b/frontend/src/app/components/home/browse/categories/categories.component.scss index d54c8e3..d27b971 100644 --- a/frontend/src/app/components/home/browse/categories/categories.component.scss +++ b/frontend/src/app/components/home/browse/categories/categories.component.scss @@ -1,22 +1,21 @@ @import "./../../../../../assets/style/colors.scss"; section { - padding: 0 48px; margin-top: 12px; + padding: 0 24px; + margin: 0 24px; } .category { + position: relative; height: 165px; + padding-top: 12px; p { font-size: 22px; font-weight: 700; } } -.movies { - display: flex; -} - .movie { width: 213px; height: 120px; @@ -38,11 +37,71 @@ section { border: 2px solid $blue !important; } +// Movies + +.movies { + display: flex; + overflow-x: auto; + overflow-y: hidden; + -ms-overflow-style: none; + scrollbar-width: none; + padding: 0 24x; +} + +.movies::-webkit-scrollbar { + display: none; +} + +.scroll-buttons { + position: absolute; + top: 50%; + width: 100%; + display: flex; + opacity: 0; + justify-content: space-between; + pointer-events: none; + transform: translateY(-50%); +} + +.scroll-buttons.show { + opacity: 1; /* Sichtbar machen, wenn Klasse 'show' hinzugefügt wird */ +} + +.scroll-left, +.scroll-right { + background: url("./../../../../../assets/img/arrow_forward.svg") no-repeat + center; + background-size: 20px 30px; + border: none; + width: 40px; + height: 40px; + cursor: pointer; + pointer-events: auto; + &:hover { + transition: 125ms ease-in-out; + filter: brightness(0) saturate(100%) invert(26%) sepia(77%) saturate(2342%) + hue-rotate(228deg) brightness(83%) contrast(115%); + } +} + +.scroll-left { + position: absolute; + left: -32px; + background-image: url("./../../../../../assets/img/arrow_back.svg"); +} + +.scroll-right { + position: absolute; + right: -38px; + background-image: url("./../../../../../assets/img/arrow_forward.svg"); +} + // Hide scrollbar .hide-scrollbar { - overflow: scroll; - height: 60%; + overflow-y: auto; + overflow-x: hidden; + height: 50%; -ms-overflow-style: none; scrollbar-width: none; } @@ -50,3 +109,14 @@ section { .hide-scrollbar::-webkit-scrollbar { width: 0px; } + +/*------------- RESPONSIVE -------------*/ + +@media screen and (max-width: 400px) { + .category { + p { + font-size: 18px; + font-weight: 700; + } + } +} diff --git a/frontend/src/app/components/home/browse/categories/categories.component.ts b/frontend/src/app/components/home/browse/categories/categories.component.ts index 54ac3af..37e4384 100644 --- a/frontend/src/app/components/home/browse/categories/categories.component.ts +++ b/frontend/src/app/components/home/browse/categories/categories.component.ts @@ -1,5 +1,15 @@ import { CommonModule } from '@angular/common'; -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { + ChangeDetectorRef, + Component, + ElementRef, + EventEmitter, + HostListener, + Input, + Output, + Renderer2, + ViewChild, +} from '@angular/core'; import { environment } from '../../../../environments/environment'; @Component({ @@ -15,26 +25,32 @@ export class CategoriesComponent { @Output() currentMovieId = new EventEmitter(); environmentBaseUrl: string = environment.baseUrl; + isScrollable: boolean = false; filmGenres = [ - 'action', - 'adventure', - 'comedy', - 'drama', - 'horror', - 'science_fiction', - 'fantasy', - 'romance', - 'thriller', - 'mystery', - 'crime', - 'animation', - 'documentary', - 'musical', - 'war', - 'western', + { code: 'action', name: 'Action' }, + { code: 'adventure', name: 'Adventure' }, + { code: 'comedy', name: 'Comedy' }, + { code: 'drama', name: 'Drama' }, + { code: 'horror', name: 'Horror' }, + { code: 'science_fiction', name: 'Science Fiction' }, + { code: 'fantasy', name: 'Fantasy' }, + { code: 'romance', name: 'Romance' }, + { code: 'thriller', name: 'Thriller' }, + { code: 'mystery', name: 'Mystery' }, + { code: 'crime', name: 'Crime' }, + { code: 'animation', name: 'Animation' }, + { code: 'documentary', name: 'Documentary' }, + { code: 'musical', name: 'Musical' }, + { code: 'war', name: 'War' }, + { code: 'western', name: 'Western' }, + { code: 'other', name: 'Miscellaneous' }, ]; + ngAfterViewInit() { + this.checkScroll(); + } + openCurrentMovie(movieId: number) { this.currentMovie = movieId; this.currentMovieId.emit(movieId); @@ -50,8 +66,53 @@ export class CategoriesComponent { sevenDaysAgo.setDate(today.getDate() - 7); return this.movies.filter((movie) => { - const movieDate = new Date(movie.create); + const movieDate = new Date(movie.created_at); return movieDate >= sevenDaysAgo; }); } + + // Category scroll + + @HostListener('window:resize') + onResize() { + this.checkScroll(); + } + + checkScroll() { + const containers = document.querySelectorAll( + '.movies' + ) as NodeListOf; + containers.forEach((container) => { + const scrollButtons = container.parentElement?.querySelector( + '.scroll-buttons' + ) as HTMLElement; + if (container && scrollButtons) { + if (container.scrollWidth > container.clientWidth) { + scrollButtons.classList.add('show'); + } else { + scrollButtons.classList.remove('show'); + } + } + }); + } + + scrollLeft(event: MouseEvent) { + const button = event.target as HTMLElement; + const container = button + .closest('.category') + ?.querySelector('.movies') as HTMLElement; + if (container) { + container.scrollLeft -= 217; + } + } + + scrollRight(event: MouseEvent) { + const button = event.target as HTMLElement; + const container = button + .closest('.category') + ?.querySelector('.movies') as HTMLElement; + if (container) { + container.scrollLeft += 217; + } + } } diff --git a/frontend/src/assets/img/arrow_back.svg b/frontend/src/assets/img/arrow_back.svg new file mode 100644 index 0000000..16aa612 --- /dev/null +++ b/frontend/src/assets/img/arrow_back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/img/arrow_forward.svg b/frontend/src/assets/img/arrow_forward.svg new file mode 100644 index 0000000..2c983e6 --- /dev/null +++ b/frontend/src/assets/img/arrow_forward.svg @@ -0,0 +1 @@ + \ No newline at end of file