diff --git a/app/YtManagerApp/models.py b/app/YtManagerApp/models.py index 93d3913..08a920c 100644 --- a/app/YtManagerApp/models.py +++ b/app/YtManagerApp/models.py @@ -1,4 +1,5 @@ import logging +import mimetypes import os from typing import Callable, Union, Any, Optional @@ -218,6 +219,19 @@ class Video(models.Model): if file.startswith(file_pattern): yield os.path.join(directory, file) + def find_video(self): + """ + Finds the video file from the downloaded files, and + returns + :return: Tuple containing file path and mime type + """ + for file in self.get_files(): + mime, _ = mimetypes.guess_type(file) + if mime is not None and mime.startswith('video/'): + return (file, mime) + + return None, None + def delete_files(self): if self.downloaded_path is not None: from YtManagerApp.management.jobs.delete_video import schedule_delete_video diff --git a/app/YtManagerApp/templates/YtManagerApp/index_videos.html b/app/YtManagerApp/templates/YtManagerApp/index_videos.html index 691d7c8..d885711 100644 --- a/app/YtManagerApp/templates/YtManagerApp/index_videos.html +++ b/app/YtManagerApp/templates/YtManagerApp/index_videos.html @@ -6,13 +6,17 @@ {% for video in videos %}
- Thumbnail + + Thumbnail +
{% if not video.watched %} New {% endif %} - {{ video.name }} + + {{ video.name }} +

{{ video.views | intcomma }} views diff --git a/app/YtManagerApp/templates/YtManagerApp/video.html b/app/YtManagerApp/templates/YtManagerApp/video.html new file mode 100644 index 0000000..823e002 --- /dev/null +++ b/app/YtManagerApp/templates/YtManagerApp/video.html @@ -0,0 +1,83 @@ +{% extends "YtManagerApp/master_default.html" %} +{% load static %} +{% load humanize %} +{% load ratings %} + +{% block body %} + +

+ +

{{ object.name }}

+ +
+ +
+ {% if video_mime != None %} + + {% else %} + + {% endif %} + +
+

+ {{ object.views | intcomma }} views + + {{ object.publish_date | naturaltime }} +

+
+ {% starrating object.rating %} +
+
+ +
+ {{ object.description | linebreaks | urlize }} +
+ +

+
+ +
+
Manage video
+

+ + Watch on YouTube + +

+ + {% if object.watched %} +

+ + Mark not watched + +

+ {% else %} +

+ + Mark watched + +

+ {% endif %} + + {% if object.downloaded_path %} +

+ + Delete downloaded + +

+ {% else %} +

+ + Download + +

+ {% endif %} +
+
+ +
+ +{% endblock %} diff --git a/app/YtManagerApp/urls.py b/app/YtManagerApp/urls.py index b6e7d8d..d619a22 100644 --- a/app/YtManagerApp/urls.py +++ b/app/YtManagerApp/urls.py @@ -26,6 +26,7 @@ from .views.index import index, ajax_get_tree, ajax_get_videos, CreateFolderModa CreateSubscriptionModal, UpdateSubscriptionModal, DeleteSubscriptionModal, ImportSubscriptionsModal from .views.notifications import ajax_get_notifications from .views.settings import SettingsView, AdminSettingsView +from .views.video import VideoDetailView, video_detail_view urlpatterns = [ # Authentication URLs @@ -63,8 +64,10 @@ urlpatterns = [ path('', index, name='home'), path('settings/', SettingsView.as_view(), name='settings'), path('admin_settings/', AdminSettingsView.as_view(), name='admin_settings'), + path('video//', VideoDetailView.as_view(), name='video'), + path('video-src//', video_detail_view, name='video-src'), - # First time setup + # First time setup path('first_time/step0_welcome', first_time.Step0WelcomeView.as_view(), name='first_time_0'), path('first_time/step1_apikey', first_time.Step1ApiKeyView.as_view(), name='first_time_1'), path('first_time/step2_admin', first_time.Step2SetupAdminUserView.as_view(), name='first_time_2'), diff --git a/app/YtManagerApp/views/auth.py b/app/YtManagerApp/views/auth.py index ec668fe..cbea096 100644 --- a/app/YtManagerApp/views/auth.py +++ b/app/YtManagerApp/views/auth.py @@ -20,7 +20,7 @@ class RegisterView(FormView): success_url = reverse_lazy('register_done') def form_valid(self, form): - + form.apply_session_expiry(self.request) form.save() username = form.cleaned_data.get('username') diff --git a/app/YtManagerApp/views/first_time.py b/app/YtManagerApp/views/first_time.py index 6e3ce0c..645653f 100644 --- a/app/YtManagerApp/views/first_time.py +++ b/app/YtManagerApp/views/first_time.py @@ -11,8 +11,8 @@ from django.views.generic import FormView from YtManagerApp.management.appconfig import appconfig from YtManagerApp.management.jobs.synchronize import schedule_synchronize_global from YtManagerApp.scheduler import initialize_scheduler -from YtManagerApp.views.forms.auth import ExtendedAuthenticationForm -from YtManagerApp.views.forms.first_time import WelcomeForm, ApiKeyForm, PickAdminUserForm, ServerConfigForm, DoneForm, UserCreationForm +from YtManagerApp.views.forms.first_time import WelcomeForm, ApiKeyForm, PickAdminUserForm, ServerConfigForm, DoneForm, \ + UserCreationForm, LoginForm logger = logging.getLogger("FirstTimeWizard") @@ -85,38 +85,35 @@ class Step2SetupAdminUserView(WizardStepMixin, FormView): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.__form_class = UserCreationForm def get_form_class(self): - return self.__form_class - - def get(self, request, *args, **kwargs): - have_users = User.objects.count() > 0 have_admin = User.objects.filter(is_superuser=True).count() > 0 + # Check if an admin user already exists + if have_admin: + logger.debug("Admin user already exists and is not logged in!") + return LoginForm + + elif have_users and 'register' not in self.kwargs: + logger.debug("There are users but no admin!") + return PickAdminUserForm + + logger.debug("No admin user exists, will register a new account!") + return UserCreationForm + + def get(self, request, *args, **kwargs): + # Skip if admin is already logged in if request.user.is_authenticated and request.user.is_superuser: logger.debug("Admin user already exists and is logged in!") return redirect(self.success_url) - # Check if an admin user already exists - elif have_admin: - logger.debug("Admin user already exists and is not logged in!") - self.__form_class = ExtendedAuthenticationForm - - elif have_users and 'register' not in kwargs: - logger.debug("There are users but no admin!") - self.__form_class = PickAdminUserForm - - else: - logger.debug("No admin user exists, will register a new account!") - self.__form_class = UserCreationForm - return super().get(request, *args, **kwargs) def form_valid(self, form): - if isinstance(form, ExtendedAuthenticationForm): + if isinstance(form, LoginForm): + form.apply_session_expiry(self.request) login(self.request, form.get_user()) elif isinstance(form, UserCreationForm): diff --git a/app/YtManagerApp/views/forms/auth.py b/app/YtManagerApp/views/forms/auth.py index c90c10f..6fa9148 100644 --- a/app/YtManagerApp/views/forms/auth.py +++ b/app/YtManagerApp/views/forms/auth.py @@ -8,15 +8,14 @@ from django.urls import reverse_lazy class ExtendedAuthenticationForm(AuthenticationForm): remember_me = forms.BooleanField(label='Remember me', required=False, initial=False) - def clean(self): + def apply_session_expiry(self, request): remember_me = self.cleaned_data.get('remember_me') if remember_me: expiry = 3600 * 24 * 30 else: expiry = 0 - self.request.session.set_expiry(expiry) - return super().clean() + request.session.set_expiry(expiry) class ExtendedUserCreationForm(UserCreationForm): diff --git a/app/YtManagerApp/views/forms/first_time.py b/app/YtManagerApp/views/forms/first_time.py index 6f4dfae..e710a4e 100644 --- a/app/YtManagerApp/views/forms/first_time.py +++ b/app/YtManagerApp/views/forms/first_time.py @@ -6,7 +6,7 @@ from django import forms from django.contrib.auth.models import User from django.urls import reverse_lazy -from YtManagerApp.views.forms.auth import ExtendedUserCreationForm +from YtManagerApp.views.forms.auth import ExtendedUserCreationForm, ExtendedAuthenticationForm logger = logging.getLogger("FirstTimeWizard") @@ -30,7 +30,7 @@ class ApiKeyForm(forms.Form): 'api_key', Column( Submit('submit', value='Continue'), - HTML('Skip') + HTML('Skip') ) ) @@ -39,6 +39,22 @@ class UserCreationForm(ExtendedUserCreationForm): form_action = reverse_lazy('first_time_2') +class LoginForm(ExtendedAuthenticationForm): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.layout = Layout( + 'username', + 'password', + 'remember_me', + Column( + Submit('submit', value='Continue'), + HTML('Register new admin account') + ) + ) + + class PickAdminUserForm(forms.Form): admin_user = forms.ModelChoiceField( User.objects.order_by('username'), diff --git a/app/YtManagerApp/views/video.py b/app/YtManagerApp/views/video.py new file mode 100644 index 0000000..f2e073b --- /dev/null +++ b/app/YtManagerApp/views/video.py @@ -0,0 +1,30 @@ +from django.contrib.auth.decorators import login_required +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import HttpRequest, StreamingHttpResponse, FileResponse +from django.urls import reverse, reverse_lazy +from django.views import View +from django.views.generic import DetailView + +from YtManagerApp.models import Video + + +class VideoDetailView(LoginRequiredMixin, DetailView): + template_name = 'YtManagerApp/video.html' + model = Video + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + video, mime = self.object.find_video() + if video is not None: + context['video_mime'] = mime + + return context + + +@login_required +def video_detail_view(request: HttpRequest, pk): + video = Video.objects.get(id = pk) + video_file, _ = video.find_video() + + f = open(video_file, 'rb') + return FileResponse(f)