Merge branch 'status-on-ui' of https://github.com/chibicitiberiu/ytsm into status-on-ui

This commit is contained in:
Tiberiu Chibici 2019-03-09 12:55:02 +02:00
commit eb60a1fc8e
9 changed files with 176 additions and 30 deletions

View File

@ -1,4 +1,5 @@
import logging import logging
import mimetypes
import os import os
from typing import Callable, Union, Any, Optional from typing import Callable, Union, Any, Optional
@ -218,6 +219,19 @@ class Video(models.Model):
if file.startswith(file_pattern): if file.startswith(file_pattern):
yield os.path.join(directory, file) 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): def delete_files(self):
if self.downloaded_path is not None: if self.downloaded_path is not None:
from YtManagerApp.management.jobs.delete_video import schedule_delete_video from YtManagerApp.management.jobs.delete_video import schedule_delete_video

View File

@ -6,13 +6,17 @@
{% for video in videos %} {% for video in videos %}
<div class="card-wrapper d-flex align-items-stretch" style="width: 18rem;"> <div class="card-wrapper d-flex align-items-stretch" style="width: 18rem;">
<div class="card mx-auto"> <div class="card mx-auto">
<img class="card-img-top" src="{{ video.icon_best }}" alt="Thumbnail"> <a href="{% url 'video' video.id %}">
<img class="card-img-top" src="{{ video.icon_best }}" alt="Thumbnail">
</a>
<div class="card-body"> <div class="card-body">
<h5 class="card-title"> <h5 class="card-title">
{% if not video.watched %} {% if not video.watched %}
<sup class="badge badge-primary">New</sup> <sup class="badge badge-primary">New</sup>
{% endif %} {% endif %}
{{ video.name }} <a href="{% url 'video' video.id %}">
{{ video.name }}
</a>
</h5> </h5>
<p class="card-text small text-muted"> <p class="card-text small text-muted">
<span>{{ video.views | intcomma }} views</span> <span>{{ video.views | intcomma }} views</span>

View File

@ -0,0 +1,83 @@
{% extends "YtManagerApp/master_default.html" %}
{% load static %}
{% load humanize %}
{% load ratings %}
{% block body %}
<div class="container">
<h2>{{ object.name }}</h2>
<div class="row">
<div class="col-8">
{% if video_mime != None %}
<video width="100%" controls autoplay>
<source src="{% url 'video-src' object.id %}" type="{{ video_mime }}">
</video>
{% else %}
<iframe id="ytplayer" type="text/html" width="100%" height="400"
src="https://www.youtube.com/embed/{{ object.video_id }}?autoplay=1"
frameborder="0"></iframe>
{% endif %}
<div class="row mx-0 mt-2">
<p class="text-muted mb-1">
<span>{{ object.views | intcomma }} views</span>
<span>&#x2022;</span>
<span>{{ object.publish_date | naturaltime }}</span>
</p>
<div class="ml-auto">
{% starrating object.rating %}
</div>
</div>
<div class="video-description">
{{ object.description | linebreaks | urlize }}
</div>
</p>
</div>
<div class="col-4">
<h5>Manage video</h5>
<p>
<a class="btn btn-secondary" href="https://youtube.com/watch?v={{ object.video_id }}">
Watch on YouTube <span class="typcn typcn-social-youtube"></span>
</a>
</p>
{% if object.watched %}
<p>
<a class="btn btn-secondary ajax-link" href="#" data-post-url="{% url 'ajax_action_mark_video_unwatched' object.id %}">
Mark not watched
</a>
</p>
{% else %}
<p>
<a class="btn btn-secondary ajax-link" href="#" data-post-url="{% url 'ajax_action_mark_video_watched' object.id %}">
Mark watched
</a>
</p>
{% endif %}
{% if object.downloaded_path %}
<p>
<a class="btn btn-secondary ajax-link" href="#" data-post-url="{% url 'ajax_action_delete_video_files' object.id %}">
Delete downloaded
</a>
</p>
{% else %}
<p>
<a class="btn btn-secondary ajax-link" href="#" data-post-url="{% url 'ajax_action_download_video_files' object.id %}" >
Download
</a>
</p>
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@ -26,6 +26,7 @@ from .views.index import index, ajax_get_tree, ajax_get_videos, CreateFolderModa
CreateSubscriptionModal, UpdateSubscriptionModal, DeleteSubscriptionModal, ImportSubscriptionsModal CreateSubscriptionModal, UpdateSubscriptionModal, DeleteSubscriptionModal, ImportSubscriptionsModal
from .views.notifications import ajax_get_notifications from .views.notifications import ajax_get_notifications
from .views.settings import SettingsView, AdminSettingsView from .views.settings import SettingsView, AdminSettingsView
from .views.video import VideoDetailView, video_detail_view
urlpatterns = [ urlpatterns = [
# Authentication URLs # Authentication URLs
@ -63,8 +64,10 @@ urlpatterns = [
path('', index, name='home'), path('', index, name='home'),
path('settings/', SettingsView.as_view(), name='settings'), path('settings/', SettingsView.as_view(), name='settings'),
path('admin_settings/', AdminSettingsView.as_view(), name='admin_settings'), path('admin_settings/', AdminSettingsView.as_view(), name='admin_settings'),
path('video/<int:pk>/', VideoDetailView.as_view(), name='video'),
path('video-src/<int:pk>/', 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/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/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'), path('first_time/step2_admin', first_time.Step2SetupAdminUserView.as_view(), name='first_time_2'),

View File

@ -20,7 +20,7 @@ class RegisterView(FormView):
success_url = reverse_lazy('register_done') success_url = reverse_lazy('register_done')
def form_valid(self, form): def form_valid(self, form):
form.apply_session_expiry(self.request)
form.save() form.save()
username = form.cleaned_data.get('username') username = form.cleaned_data.get('username')

View File

@ -11,8 +11,8 @@ from django.views.generic import FormView
from YtManagerApp.management.appconfig import appconfig from YtManagerApp.management.appconfig import appconfig
from YtManagerApp.management.jobs.synchronize import schedule_synchronize_global from YtManagerApp.management.jobs.synchronize import schedule_synchronize_global
from YtManagerApp.scheduler import initialize_scheduler 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, \
from YtManagerApp.views.forms.first_time import WelcomeForm, ApiKeyForm, PickAdminUserForm, ServerConfigForm, DoneForm, UserCreationForm UserCreationForm, LoginForm
logger = logging.getLogger("FirstTimeWizard") logger = logging.getLogger("FirstTimeWizard")
@ -85,38 +85,35 @@ class Step2SetupAdminUserView(WizardStepMixin, FormView):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.__form_class = UserCreationForm
def get_form_class(self): def get_form_class(self):
return self.__form_class
def get(self, request, *args, **kwargs):
have_users = User.objects.count() > 0 have_users = User.objects.count() > 0
have_admin = User.objects.filter(is_superuser=True).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 # Skip if admin is already logged in
if request.user.is_authenticated and request.user.is_superuser: if request.user.is_authenticated and request.user.is_superuser:
logger.debug("Admin user already exists and is logged in!") logger.debug("Admin user already exists and is logged in!")
return redirect(self.success_url) 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) return super().get(request, *args, **kwargs)
def form_valid(self, form): 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()) login(self.request, form.get_user())
elif isinstance(form, UserCreationForm): elif isinstance(form, UserCreationForm):

View File

@ -8,15 +8,14 @@ from django.urls import reverse_lazy
class ExtendedAuthenticationForm(AuthenticationForm): class ExtendedAuthenticationForm(AuthenticationForm):
remember_me = forms.BooleanField(label='Remember me', required=False, initial=False) 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') remember_me = self.cleaned_data.get('remember_me')
if remember_me: if remember_me:
expiry = 3600 * 24 * 30 expiry = 3600 * 24 * 30
else: else:
expiry = 0 expiry = 0
self.request.session.set_expiry(expiry)
return super().clean() request.session.set_expiry(expiry)
class ExtendedUserCreationForm(UserCreationForm): class ExtendedUserCreationForm(UserCreationForm):

View File

@ -6,7 +6,7 @@ from django import forms
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.urls import reverse_lazy 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") logger = logging.getLogger("FirstTimeWizard")
@ -30,7 +30,7 @@ class ApiKeyForm(forms.Form):
'api_key', 'api_key',
Column( Column(
Submit('submit', value='Continue'), Submit('submit', value='Continue'),
HTML('<a href="{% url \'first_time_2\' %}" class="btn">Skip</a>') HTML('<a href="{% url \'first_time_2\' %}" class="btn btn-secondary">Skip</a>')
) )
) )
@ -39,6 +39,22 @@ class UserCreationForm(ExtendedUserCreationForm):
form_action = reverse_lazy('first_time_2') 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('<a href="{% url \'first_time_2\' %}?register=1" class="btn">Register new admin account</a>')
)
)
class PickAdminUserForm(forms.Form): class PickAdminUserForm(forms.Form):
admin_user = forms.ModelChoiceField( admin_user = forms.ModelChoiceField(
User.objects.order_by('username'), User.objects.order_by('username'),

View File

@ -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)