diff --git a/backend/videoflix/content/models.py b/backend/videoflix/content/models.py index 90749aa..52ea286 100644 --- a/backend/videoflix/content/models.py +++ b/backend/videoflix/content/models.py @@ -5,8 +5,9 @@ class Video(models.Model): created_at = models.DateField(default=date.today) title = models.CharField(max_length=80) description = models.CharField(max_length=500) + film_genre = models.CharField(max_length=50, blank=True, null=True) video_file = models.FileField(upload_to='videos', blank=True, null=True) - thumbnail = models.FileField(upload_to='thumbnails/', null=True, blank=True) + thumbnail = models.FileField(upload_to='img', null=True, blank=True) def __str__(self): return self.title \ No newline at end of file diff --git a/backend/videoflix/content/serializer.py b/backend/videoflix/content/serializer.py new file mode 100644 index 0000000..d9bad7f --- /dev/null +++ b/backend/videoflix/content/serializer.py @@ -0,0 +1,7 @@ +from rest_framework import serializers +from .models import Video + +class VideoSerializer(serializers.ModelSerializer): + class Meta: + model = Video + fields = "__all__" \ No newline at end of file diff --git a/backend/videoflix/content/signals.py b/backend/videoflix/content/signals.py index c803b39..337fd1f 100644 --- a/backend/videoflix/content/signals.py +++ b/backend/videoflix/content/signals.py @@ -22,7 +22,7 @@ def video_post_save(sender, instance, created, **kwargs): os.makedirs(thumbnail_dir) try: - ffmpeg.input(video_file_path, ss=1).output(absolute_thumbnail_path, vframes=1).run(overwrite_output=True) + ffmpeg.input(video_file_path, ss=1).output(absolute_thumbnail_path, vf='scale=300:-1', vframes=1).run(overwrite_output=True) instance.thumbnail = os.path.relpath(thumbnail_path, start=settings.MEDIA_ROOT) instance.save() diff --git a/backend/videoflix/content/views.py b/backend/videoflix/content/views.py index 91ea44a..3d42bb4 100644 --- a/backend/videoflix/content/views.py +++ b/backend/videoflix/content/views.py @@ -1,3 +1,16 @@ from django.shortcuts import render +from rest_framework.decorators import api_view, permission_classes +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from .serializer import VideoSerializer +from .models import Video # Create your views here. +@api_view(['GET']) +@permission_classes([IsAuthenticated]) +def video_list(request): + + if request.method == 'GET': + video = Video.objects.all() + serializer = VideoSerializer(video, many=True) + return Response(serializer.data) \ No newline at end of file diff --git a/backend/videoflix/videoflix/urls.py b/backend/videoflix/videoflix/urls.py index c9a0e99..4019a93 100644 --- a/backend/videoflix/videoflix/urls.py +++ b/backend/videoflix/videoflix/urls.py @@ -15,20 +15,35 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path, include +from django.urls import path from django.conf import settings from django.conf.urls.static import static -from users import views -from auth.views import LoginView, RegisterView, VerifyEmailView, AuthView, ForgotPasswordView,ChangePasswordView +from users import views as user_views +from auth.views import ( + LoginView, + RegisterView, + VerifyEmailView, + AuthView, + ForgotPasswordView, + ChangePasswordView +) +from content import views as content_views urlpatterns = [ path('admin/', admin.site.urls), - path('users/', views.user_list), - path('users//', views.user_detail), + + # Content URLs + path('content/', content_views.video_list), + + # Users URLs + path('users/', user_views.user_list), + path('users//', user_views.user_detail), + + # Authentication URLs path('auth/', AuthView.as_view()), path('auth/login/', LoginView.as_view()), path('auth/register/', RegisterView.as_view()), path('auth/verify-email/', VerifyEmailView.as_view()), path('auth/forgot-password/', ForgotPasswordView.as_view()), - path('auth/change-password/', ChangePasswordView.as_view()), -] + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT) + path('auth/change-password/', ChangePasswordView.as_view()), +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) \ No newline at end of file diff --git a/frontend/src/app/components/home/browse/browse.component.html b/frontend/src/app/components/home/browse/browse.component.html index 8fda278..bd2cef2 100644 --- a/frontend/src/app/components/home/browse/browse.component.html +++ b/frontend/src/app/components/home/browse/browse.component.html @@ -7,7 +7,7 @@ > } @else { @@ -21,10 +21,7 @@ diff --git a/frontend/src/app/components/home/browse/browse.component.ts b/frontend/src/app/components/home/browse/browse.component.ts index 310a311..c5dde03 100644 --- a/frontend/src/app/components/home/browse/browse.component.ts +++ b/frontend/src/app/components/home/browse/browse.component.ts @@ -3,47 +3,42 @@ import { HeaderComponent } from '../../../shared/components/header/header.compon import { HeroBannerComponent } from './hero-banner/hero-banner.component'; import { CategoriesComponent } from './categories/categories.component'; import { AuthService } from '../../../services/auth.service'; +import { MovieService } from '../../../services/movie.service'; +import { Observable, Subscription } from 'rxjs'; +import { CommonModule } from '@angular/common'; @Component({ selector: 'app-browse', standalone: true, - imports: [HeaderComponent, HeroBannerComponent, CategoriesComponent], + imports: [ + CommonModule, + HeaderComponent, + HeroBannerComponent, + CategoriesComponent, + ], templateUrl: './browse.component.html', styleUrl: './browse.component.scss', }) export class BrowseComponent implements OnInit { - movies = [ - { - id: 1, - title: 'Our Galaxy', - description: - 'Experience the fascinating journey through the infinite expanse of our galaxy. "Our Galaxy" offers a detailed insight into the breathtaking structures, stars and planets of our cosmic home. Through stunning visual effects and comprehensive explanations, this documentary guides viewers through the mysteries of the universe and reveals the beauty and complexity of outer space. Perfect for astronomy lovers and the curious who want to learn more about the wonders of the universe', - category: 'Documentary', - imgPath: 'galaxy.png', - videoPath: 'galaxy.mp4', - create: '08.03.2024', - }, - { - id: 2, - title: 'Star Trek', - description: - 'Join Captain Kirk and the crew of the starship USS Enterprise as they embark on thrilling adventures across the universe. "Star Trek" is a legendary sci-fi saga that explores the final frontier, filled with action, exploration, and unforgettable encounters with alien species. This epic journey showcases the spirit of discovery and the unwavering courage of the Starfleet members as they confront the unknown and protect the galaxy from various threats.', - category: 'Science Fiction', - imgPath: 'star-trek.png', - videoPath: 'star-trek.mp4', - create: '08.02.2024', - }, - ]; + movies: any[] = []; - constructor(private authService: AuthService) {} + constructor( + private authService: AuthService, + private movieService: MovieService + ) {} currentMovie: any[] = []; playMovie: string = ''; - ngOnInit(): void { + async ngOnInit() { + await this.loadAllMovies(); this.currentMovie.length === 0 ? this.loadRandomMovie() : null; } + async loadAllMovies() { + this.movies = await this.movieService.getAllMovies(); + } + closeVideo(): void { this.playMovie = ''; } 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 c5916e1..e79d779 100644 --- a/frontend/src/app/components/home/browse/categories/categories.component.html +++ b/frontend/src/app/components/home/browse/categories/categories.component.html @@ -1,13 +1,13 @@ -
+
-
+

Added in the last 7 days

@for (movie of recentMovies(); track movie) {
@@ -25,7 +25,7 @@
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 235119f..0f02157 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,6 @@ import { CommonModule } from '@angular/common'; import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { environment } from '../../../../environments/environment.development'; @Component({ selector: 'app-categories', @@ -13,6 +14,8 @@ export class CategoriesComponent { @Input() currentMovie: number = 0; @Output() currentMovieId = new EventEmitter(); + environmentBaseUrl: string = environment.baseUrl.slice(0, -1); + filmGenres = [ 'Action', 'Adventure', @@ -38,7 +41,7 @@ export class CategoriesComponent { } allMovies(filmGenre: string) { - return this.movies.filter((movie) => movie.category === filmGenre); + return this.movies.filter((movie) => movie.film_genre === filmGenre); } recentMovies() { diff --git a/frontend/src/app/components/home/browse/hero-banner/hero-banner.component.html b/frontend/src/app/components/home/browse/hero-banner/hero-banner.component.html index 8bce778..8c2a2e3 100644 --- a/frontend/src/app/components/home/browse/hero-banner/hero-banner.component.html +++ b/frontend/src/app/components/home/browse/hero-banner/hero-banner.component.html @@ -1,18 +1,21 @@
@if (currentMovie.length > 0) {
-
{{ currentMovie[0].title }}
+
{{ currentMovie[0]?.title }}
- {{ currentMovie[0].description }} + {{ currentMovie[0]?.description }}
} diff --git a/frontend/src/app/components/home/browse/hero-banner/hero-banner.component.ts b/frontend/src/app/components/home/browse/hero-banner/hero-banner.component.ts index 3f4034f..b301fca 100644 --- a/frontend/src/app/components/home/browse/hero-banner/hero-banner.component.ts +++ b/frontend/src/app/components/home/browse/hero-banner/hero-banner.component.ts @@ -7,6 +7,8 @@ import { Output, } from '@angular/core'; import { BtnLargeComponent } from '../../../../shared/components/btn-large/btn-large.component'; +import { MovieService } from '../../../../services/movie.service'; +import { environment } from '../../../../environments/environment.development'; @Component({ selector: 'app-hero-banner', @@ -19,28 +21,15 @@ export class HeroBannerComponent { @Input() currentMovie: any[] = []; @Output() playMovie = new EventEmitter(); - constructor(private el: ElementRef) {} + environmentBaseUrl: string = environment.baseUrl.slice(0, -1); - ngAfterViewInit() { - this.truncateText(); - } + constructor(private el: ElementRef, private movieService: MovieService) {} playMovieId(videoPath: string) { this.playMovie.emit(videoPath); } - getImagePath(): string { - return `./../../../../../assets/movies/banner/${this.currentMovie[0]?.imgPath}`; - } - - truncateText() { - const descriptionEl = this.el.nativeElement.querySelector('.description'); - const maxHeight = descriptionEl.offsetHeight; - let text = descriptionEl.innerText; - - while (descriptionEl.scrollHeight > maxHeight) { - text = text.slice(0, -1); - descriptionEl.innerText = text + '...'; - } + getImagePath(): any { + return '123'; } } diff --git a/frontend/src/app/services/movie.service.ts b/frontend/src/app/services/movie.service.ts new file mode 100644 index 0000000..b187036 --- /dev/null +++ b/frontend/src/app/services/movie.service.ts @@ -0,0 +1,33 @@ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { lastValueFrom, Observable, of } from 'rxjs'; +import { environment } from '../environments/environment.development'; + +@Injectable({ + providedIn: 'root', +}) +export class MovieService { + constructor(private http: HttpClient) {} + + getAllMovies(): Promise { + const url = environment.baseUrl + '/content/'; + const headers = this.getAuthHeaders(); + return lastValueFrom(this.http.get(url, { headers })); + } + + getMovieFiles(videoUrl: number): Promise { + const url = environment.baseUrl + `${videoUrl}`; + const headers = this.getAuthHeaders(); + return lastValueFrom(this.http.get(url, { headers })); + } + + private getAuthHeaders(): HttpHeaders { + let authToken = localStorage.getItem('authToken'); + if (!authToken) { + authToken = sessionStorage.getItem('authToken'); + } + return new HttpHeaders({ + Authorization: `Token ${authToken}`, // Ensure this matches your backend + }); + } +}