delete source page, start of youtube-dl wrapper

This commit is contained in:
meeb 2020-11-28 22:29:42 +11:00
parent c87adf86d5
commit 060691202b
9 changed files with 131 additions and 13 deletions

View File

@ -36,7 +36,7 @@ $form-error-text-colour: $colour-near-white;
$form-help-text-colour: $colour-light-blue; $form-help-text-colour: $colour-light-blue;
$form-delete-button-background-colour: $colour-red; $form-delete-button-background-colour: $colour-red;
$collection-no-items-text-colour: $colour-light-blue; $collection-no-items-text-colour: $colour-near-black;
$collection-background-hover-colour: $colour-orange; $collection-background-hover-colour: $colour-orange;
$collection-text-hover-colour: $colour-near-white; $collection-text-hover-colour: $colour-near-white;

View File

@ -1,5 +1,6 @@
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _
class ValidateSourceForm(forms.Form): class ValidateSourceForm(forms.Form):
@ -10,6 +11,14 @@ class ValidateSourceForm(forms.Form):
widget=forms.HiddenInput() widget=forms.HiddenInput()
) )
source_url = forms.URLField( source_url = forms.URLField(
label='Source URL', label=_('Source URL'),
required=True required=True
) )
class ConfirmDeleteSourceForm(forms.Form):
delete_media = forms.BooleanField(
label=_('Also delete downloaded media'),
required=False
)

View File

@ -66,7 +66,7 @@ class Source(models.Model):
} }
URLS = { URLS = {
SOURCE_TYPE_YOUTUBE_CHANNEL: 'https://www.youtube.com/{key}', SOURCE_TYPE_YOUTUBE_CHANNEL: 'https://www.youtube.com/c/{key}',
SOURCE_TYPE_YOUTUBE_PLAYLIST: 'https://www.youtube.com/playlist?list={key}', SOURCE_TYPE_YOUTUBE_PLAYLIST: 'https://www.youtube.com/playlist?list={key}',
} }
@ -173,10 +173,14 @@ class Source(models.Model):
def icon(self): def icon(self):
return self.ICONS.get(self.source_type) return self.ICONS.get(self.source_type)
@classmethod
def create_url(obj, source_type, key):
url = obj.URLS.get(source_type)
return url.format(key=key)
@property @property
def url(self): def url(self):
url = self.URLS.get(self.source_type) return Source.create_url(self.source_type, self.key)
return url.format(key=self.key)
@property @property
def directory_path(self): def directory_path(self):

View File

@ -0,0 +1,28 @@
{% extends 'base.html' %}
{% block headtitle %}Delete source - {{ source.name }}{% endblock %}
{% block content %}
<div class="row no-margin-bottom">
<div class="col s12">
<h1>Delete source <strong>{{ source.name }}</strong></h1>
<p>
Are you sure you want to delete this source? Deleting a source is permanent.
By default, deleting a source does not delete any saved media files. You can
tick the &quot;also delete downloaded media&quot; checkbox to also remove save
media when you delete the source. Deleting a source cannot be undone.
</p>
</div>
</div>
<div class="row">
<form method="post" action="{% url 'sync:delete-source' pk=source.pk %}" class="col s12 simpleform">
{% csrf_token %}
{% include 'simpleform.html' with form=form %}
<div class="row no-margin-bottom padding-top">
<div class="col s12">
<button class="btn" type="submit" name="action">Really delete source <i class="fas fa-trash-alt"></i></button>
</div>
</div>
</form>
</div>
{% endblock %}

View File

@ -80,7 +80,7 @@
<a href="{% url 'sync:update-source' pk=source.pk %}" class="btn">Edit source <i class="fas fa-pen-square"></i></a> <a href="{% url 'sync:update-source' pk=source.pk %}" class="btn">Edit source <i class="fas fa-pen-square"></i></a>
</div> </div>
<div class="col s12 l6 margin-bottom"> <div class="col s12 l6 margin-bottom">
<a href="" class="btn delete-button">Delete source <i class="fas fa-trash-alt"></i></a> <a href="{% url 'sync:delete-source' pk=source.pk %}" class="btn delete-button">Delete source <i class="fas fa-trash-alt"></i></a>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -1,6 +1,7 @@
from django.urls import path from django.urls import path
from .views import (DashboardView, SourcesView, ValidateSourceView, AddSourceView, from .views import (DashboardView, SourcesView, ValidateSourceView, AddSourceView,
SourceView, UpdateSourceView, MediaView, TasksView, LogsView) SourceView, UpdateSourceView, DeleteSourceView, MediaView,
TasksView, LogsView)
app_name = 'sync' app_name = 'sync'
@ -36,9 +37,13 @@ urlpatterns = [
UpdateSourceView.as_view(), UpdateSourceView.as_view(),
name='update-source'), name='update-source'),
# Media URLs path('source-delete/<uuid:pk>',
DeleteSourceView.as_view(),
name='delete-source'),
path('media', # Media URLs (note /media/ is the static media URL, don't use that)
path('mediafiles',
MediaView.as_view(), MediaView.as_view(),
name='media'), name='media'),

View File

@ -1,15 +1,17 @@
from django.conf import settings from django.conf import settings
from django.http import Http404 from django.http import Http404
from django.views.generic import TemplateView, ListView, DetailView from django.views.generic import TemplateView, ListView, DetailView
from django.views.generic.edit import FormView, CreateView, UpdateView from django.views.generic.edit import (FormView, FormMixin, CreateView, UpdateView,
DeleteView)
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.forms import ValidationError from django.forms import ValidationError
from django.utils.text import slugify from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from common.utils import append_uri_params from common.utils import append_uri_params
from .models import Source from .models import Source
from .forms import ValidateSourceForm from .forms import ValidateSourceForm, ConfirmDeleteSourceForm
from .utils import validate_url from .utils import validate_url
from . import youtube
class DashboardView(TemplateView): class DashboardView(TemplateView):
@ -155,7 +157,7 @@ class ValidateSourceView(FormView):
ValidationError(self.errors['invalid_source']) ValidationError(self.errors['invalid_source'])
) )
source_url = form.cleaned_data['source_url'] source_url = form.cleaned_data['source_url']
validation_url = self.validation_urls.get(self.source_type) validation_url = self.validation_urls.get(source_type)
try: try:
self.key = validate_url(source_url, validation_url) self.key = validate_url(source_url, validation_url)
except ValidationError as e: except ValidationError as e:
@ -246,6 +248,30 @@ class UpdateSourceView(UpdateView):
return append_uri_params(url, {'message': 'source-updated'}) return append_uri_params(url, {'message': 'source-updated'})
class DeleteSourceView(DeleteView, FormMixin):
'''
Confirm the deletion of a source with an option to delete all the media
associated with the source from disk when the source is deleted.
'''
template_name = 'sync/source-delete.html'
model = Source
form_class = ConfirmDeleteSourceForm
context_object_name = 'source'
def post(self, request, *args, **kwargs):
delete_media_val = request.POST.get('delete_media', False)
delete_media = True if delete_media_val is not False else False
if delete_media:
# TODO: delete media files from disk linked to this source
pass
return super().post(request, *args, **kwargs)
def get_success_url(self):
url = reverse_lazy('sync:sources')
return append_uri_params(url, {'message': 'source-deleted'})
class MediaView(TemplateView): class MediaView(TemplateView):
''' '''
A bare list of media added with their states. A bare list of media added with their states.

41
app/sync/youtube.py Normal file
View File

@ -0,0 +1,41 @@
'''
Wrapper for the youtube-dl library. Used so if there are any library interface
updates we only need to udpate them in one place.
'''
from django.conf import settings
import youtube_dl
_defaults = getattr(settings, 'YOUTUBE_DEFAULTS', {})
class YouTubeError(youtube_dl.utils.DownloadError):
'''
Generic wrapped error for all errors that could be raised by youtube-dl.
'''
pass
def extract_info(url):
'''
Extracts information from a YouTube URL and returns it as a dict. For a channel
or playlist this returns a dict of all the videos on the channel or playlist
as well as associated metadata.
'''
opts = _defaults.update({
'skip_download': True,
'forcejson': True,
'simulate': True,
'extract_flat': 'in_playlist',
'playlist_items': 1,
})
response = {}
with youtube_dl.YoutubeDL(opts) as y:
try:
response = y.extract_info(url, download=False)
except youtube_dl.utils.DownloadError as e:
raise YouTubeError(f'Failed to extract_info for "{url}": {e}') from e
return response

View File

@ -119,6 +119,11 @@ DJANGO_SIMPLE_TASK_WORKERS = 2
SOURCES_PER_PAGE = 25 SOURCES_PER_PAGE = 25
YOUTUBE_DEFAULTS = {
'age_limit': 99,
}
try: try:
from .local_settings import * from .local_settings import *
except ImportError as e: except ImportError as e: