mirror of
https://github.com/chibicitiberiu/ytsm.git
synced 2024-02-24 05:43:31 +00:00
Merge branch 'status-on-ui' of https://github.com/chibicitiberiu/ytsm into status-on-ui
This commit is contained in:
commit
eb60a1fc8e
@ -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
|
||||||
|
@ -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">
|
||||||
|
<a href="{% url 'video' video.id %}">
|
||||||
<img class="card-img-top" src="{{ video.icon_best }}" alt="Thumbnail">
|
<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 %}
|
||||||
|
<a href="{% url 'video' video.id %}">
|
||||||
{{ video.name }}
|
{{ 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>
|
||||||
|
83
app/YtManagerApp/templates/YtManagerApp/video.html
Normal file
83
app/YtManagerApp/templates/YtManagerApp/video.html
Normal 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>•</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 %}
|
@ -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,6 +64,8 @@ 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'),
|
||||||
|
@ -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')
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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'),
|
||||||
|
30
app/YtManagerApp/views/video.py
Normal file
30
app/YtManagerApp/views/video.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user