From ad9138ae1f46752ef55dd04cb89855513222fe5f Mon Sep 17 00:00:00 2001 From: Tiberiu Chibici Date: Tue, 1 Jan 2019 16:13:49 +0200 Subject: [PATCH 1/3] Added video detail page --- app/YtManagerApp/models.py | 14 ++++ .../templates/YtManagerApp/index_videos.html | 8 ++- .../templates/YtManagerApp/video.html | 69 +++++++++++++++++++ app/YtManagerApp/urls.py | 7 +- app/YtManagerApp/views/video.py | 30 ++++++++ 5 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 app/YtManagerApp/templates/YtManagerApp/video.html create mode 100644 app/YtManagerApp/views/video.py 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 cd10cbf..16e1468 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..2b1fe43 --- /dev/null +++ b/app/YtManagerApp/templates/YtManagerApp/video.html @@ -0,0 +1,69 @@ +{% 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
+ {% 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 dc487c1..8b383e4 100644 --- a/app/YtManagerApp/urls.py +++ b/app/YtManagerApp/urls.py @@ -18,13 +18,14 @@ from django.conf.urls import include from django.conf.urls.static import static from django.urls import path +from YtManagerApp.views.video import VideoDetailView, video_detail_view +from .views import first_time from .views.actions import SyncNowView, DeleteVideoFilesView, DownloadVideoFilesView, MarkVideoWatchedView, \ MarkVideoUnwatchedView from .views.auth import ExtendedLoginView, RegisterView, RegisterDoneView from .views.index import index, ajax_get_tree, ajax_get_videos, CreateFolderModal, UpdateFolderModal, DeleteFolderModal, \ CreateSubscriptionModal, UpdateSubscriptionModal, DeleteSubscriptionModal, ImportSubscriptionsModal from .views.settings import SettingsView, AdminSettingsView -from .views import first_time urlpatterns = [ # Authentication URLs @@ -60,8 +61,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/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) From 387358b07298cc64c0c262062086c23200ea9d10 Mon Sep 17 00:00:00 2001 From: Tiberiu Chibici Date: Tue, 1 Jan 2019 16:23:51 +0200 Subject: [PATCH 2/3] Added Watch On YouTube button --- .../templates/YtManagerApp/video.html | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/app/YtManagerApp/templates/YtManagerApp/video.html b/app/YtManagerApp/templates/YtManagerApp/video.html index 2b1fe43..823e002 100644 --- a/app/YtManagerApp/templates/YtManagerApp/video.html +++ b/app/YtManagerApp/templates/YtManagerApp/video.html @@ -42,24 +42,38 @@
Manage video
+

+ + Watch on YouTube + +

+ {% if object.watched %} - - Mark not watched - +

+ + Mark not watched + +

{% else %} - - Mark watched - +

+ + Mark watched + +

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

Delete downloaded +

{% else %} +

Download +

{% endif %}
From 43583e0fca19bdf1a5211d2ac8efeb124c3bf63e Mon Sep 17 00:00:00 2001 From: Tiberiu Chibici Date: Wed, 2 Jan 2019 23:11:39 +0200 Subject: [PATCH 3/3] Fixed bug in first time setup wizard at login/user creation step. If an admin user already exists, the login page didn't work properly. --- app/YtManagerApp/views/auth.py | 2 +- app/YtManagerApp/views/first_time.py | 39 ++++++++++------------ app/YtManagerApp/views/forms/auth.py | 5 ++- app/YtManagerApp/views/forms/first_time.py | 20 +++++++++-- 4 files changed, 39 insertions(+), 27 deletions(-) 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'),