allow media to be skipped before its downloaded, add a master reset-all-tasks button, tweak signal and task behaviour ordering

This commit is contained in:
meeb 2020-12-11 17:14:34 +11:00
parent dc5a8271e1
commit 3489d07289
10 changed files with 144 additions and 19 deletions

View File

@ -37,3 +37,8 @@ class SkipMediaForm(forms.Form):
class EnableMediaForm(forms.Form):
pass
class ResetTasksForm(forms.Form):
pass

View File

@ -30,21 +30,21 @@ def source_pre_save(sender, instance, **kwargs):
repeat=instance.index_schedule,
queue=str(instance.pk),
priority=5,
verbose_name=verbose_name.format(instance.name)
verbose_name=verbose_name.format(instance.name),
remove_existing_tasks=True
)
@receiver(post_save, sender=Source)
def source_post_save(sender, instance, created, **kwargs):
# Triggered after a source is saved, Create a new task to check the directory exists
verbose_name = _('Check download directory exists for source "{}"')
check_source_directory_exists(
str(instance.pk),
priority=0,
verbose_name=verbose_name.format(instance.name)
)
# Check directory exists and create an indexing task for newly created sources
if created:
# Create a new indexing task for newly created sources
verbose_name = _('Check download directory exists for source "{}"')
check_source_directory_exists(
str(instance.pk),
priority=0,
verbose_name=verbose_name.format(instance.name)
)
delete_task_by_source('sync.tasks.index_source_task', instance.pk)
log.info(f'Scheduling media indexing for source: {instance.name}')
verbose_name = _('Index media from source "{}"')
@ -53,7 +53,8 @@ def source_post_save(sender, instance, created, **kwargs):
repeat=instance.index_schedule,
queue=str(instance.pk),
priority=5,
verbose_name=verbose_name.format(instance.name)
verbose_name=verbose_name.format(instance.name),
remove_existing_tasks=True
)
# Trigger the post_save signal for each media item linked to this source as various
# flags may need to be recalculated
@ -102,6 +103,8 @@ def media_post_save(sender, instance, created, **kwargs):
instance.save()
post_save.connect(media_post_save, sender=Media)
# If the media is missing a thumbnail schedule it to be downloaded
if not instance.thumb_file_exists:
instance.thumb = None
if not instance.thumb:
thumbnail_url = instance.thumbnail
if thumbnail_url:
@ -113,9 +116,13 @@ def media_post_save(sender, instance, created, **kwargs):
thumbnail_url,
queue=str(instance.source.pk),
priority=10,
verbose_name=verbose_name.format(instance.name)
verbose_name=verbose_name.format(instance.name),
remove_existing_tasks=True
)
# If the media has not yet been downloaded schedule it to be downloaded
if not instance.media_file_exists:
instance.downloaded = False
instance.media_file = None
if not instance.downloaded and instance.can_download and not instance.skip:
delete_task_by_media('sync.tasks.download_media', (str(instance.pk),))
verbose_name = _('Downloading media for "{}"')
@ -123,7 +130,8 @@ def media_post_save(sender, instance, created, **kwargs):
str(instance.pk),
queue=str(instance.source.pk),
priority=15,
verbose_name=verbose_name.format(instance.name)
verbose_name=verbose_name.format(instance.name),
remove_existing_tasks=True
)

View File

@ -266,6 +266,11 @@ def download_media(media_id):
except Media.DoesNotExist:
# Task triggered but the media no longer exists, do nothing
return
if media.skip:
# Media was toggled to be skipped after the task was scheduled
log.warn(f'Download task triggeredd media: {media} (UUID: {media.pk}) but it '
f'is now marked to be skipped, not downloading')
return
log.info(f'Downloading media: {media} (UUID: {media.pk}) to: "{media.filepath}"')
format_str, container = media.download_media()
if os.path.exists(media.filepath):

View File

@ -143,10 +143,14 @@
<a href="{% url 'sync:skip-media' pk=media.pk %}" class="btn delete-button">Delete and skip media <i class="fas fa-times-circle"></i></a>
</div>
</div>
{% elif media.skip %}
{% else %}
<div class="row">
<div class="col s12">
<a href="{% url 'sync:enable-media' pk=media.pk %}" class="btn">Enable (unskip) media <i class="fas fa-cloud-download-alt"></i></a>
{% if media.skip %}
<a href="{% url 'sync:enable-media' pk=media.pk %}" class="btn">Enable (unskip) media <i class="fas fa-cloud-download-alt"></i></a>
{% else %}
<a href="{% url 'sync:skip-media' pk=media.pk %}" class="btn delete-button">Skip media <i class="fas fa-times-circle"></i></a>
{% endif %}
</div>
</div>
{% endif %}

View File

@ -13,7 +13,7 @@
{% for m in media %}
<div class="col s12 m6 l4 xl3">
<div class="card mediacard">
<a href="{% url 'sync:media-item' pk=m.pk %}" class="">
<a href="{% url 'sync:media-item' pk=m.pk %}" title="{{ m.source.name }} / {{ m.name }}">
<div class="card-image">
<img src="{% if m.thumb %}{% url 'sync:media-thumb' pk=m.pk %}{% else %}{% static 'images/nothumb.png' %}{% endif %}">
<span class="card-title truncate">{{ m.source }}<br>

View File

@ -0,0 +1,31 @@
{% extends 'base.html' %}
{% block headtitle %}Reset tasks{% endblock %}
{% block content %}
<div class="row no-margin-bottom">
<div class="col s12">
<h1>Reset tasks</h1>
<p>
If your TubeSync installation has gotten itself into any form of synchronisation
state issues (such you moved then restored files on disk) and the state in
TubeSync isn't up to date you can use this button to force a state reset.
</p>
<p>
This will delete all current tasks then all souces will be checked for their
states and new tasks to be created where required.
</p>
</div>
</div>
<div class="row">
<form method="post" action="{% url 'sync:reset-tasks' %}" 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 reset tasks <i class="fas fa-history"></i></button>
</div>
</div>
</form>
</div>
{% endblock %}

View File

@ -14,6 +14,7 @@
</p>
</div>
</div>
{% include 'infobox.html' with message=message %}
<div class="row">
<div class="col s12">
<h2>{{ running|length }} Running</h2>
@ -84,4 +85,13 @@
<a href="{% url 'sync:tasks-completed' %}" class="btn"><span class="hide-on-med-and-down">View </span>Completed tasks <i class="fas fa-check-double"></i></a>
</div>
</div>
<div class="row">
<div class="col s12">
<h2>Reset</h2>
<p>
If you need to, you can reset and reschedule all tasks using the button below.
</p>
<a href="{% url 'sync:reset-tasks' %}" class="btn">Reset tasks <i class="fas fa-history"></i></a>
</div>
</div>
{% endblock %}

View File

@ -2,7 +2,7 @@ from django.urls import path
from .views import (DashboardView, SourcesView, ValidateSourceView, AddSourceView,
SourceView, UpdateSourceView, DeleteSourceView, MediaView,
MediaThumbView, MediaItemView, MediaRedownloadView, MediaSkipView,
MediaEnableView, TasksView, CompletedTasksView)
MediaEnableView, TasksView, CompletedTasksView, ResetTasks)
app_name = 'sync'
@ -78,4 +78,8 @@ urlpatterns = [
CompletedTasksView.as_view(),
name='tasks-completed'),
path('tasks-reset',
ResetTasks.as_view(),
name='reset-tasks'),
]

View File

@ -16,11 +16,11 @@ from common.utils import append_uri_params
from background_task.models import Task, CompletedTask
from .models import Source, Media
from .forms import (ValidateSourceForm, ConfirmDeleteSourceForm, RedownloadMediaForm,
SkipMediaForm, EnableMediaForm)
SkipMediaForm, EnableMediaForm, ResetTasksForm)
from .utils import validate_url, delete_file
from .tasks import (map_task_to_instance, get_error_message,
get_source_completed_tasks, get_media_download_task,
delete_task_by_media)
delete_task_by_media, index_source_task)
from . import signals
from . import youtube
@ -380,6 +380,9 @@ class MediaThumbView(DetailView):
class MediaItemView(DetailView):
'''
A single media item overview page.
'''
template_name = 'sync/media-item.html'
model = Media
@ -419,6 +422,9 @@ class MediaItemView(DetailView):
class MediaRedownloadView(FormView, SingleObjectMixin):
'''
Confirm that the media file should be deleted and redownloaded.
'''
template_name = 'sync/media-redownload.html'
form_class = RedownloadMediaForm
@ -461,6 +467,9 @@ class MediaRedownloadView(FormView, SingleObjectMixin):
class MediaSkipView(FormView, SingleObjectMixin):
'''
Confirm that the media file should be deleted and marked to skip.
'''
template_name = 'sync/media-skip.html'
form_class = SkipMediaForm
@ -500,6 +509,9 @@ class MediaSkipView(FormView, SingleObjectMixin):
class MediaEnableView(FormView, SingleObjectMixin):
'''
Confirm that the media item should be re-enabled (marked as unskipped).
'''
template_name = 'sync/media-enable.html'
form_class = EnableMediaForm
@ -532,12 +544,25 @@ class TasksView(ListView):
template_name = 'sync/tasks.html'
context_object_name = 'tasks'
messages = {
'reset': _('All tasks have been reset'),
}
def __init__(self, *args, **kwargs):
self.message = None
super().__init__(*args, **kwargs)
def dispatch(self, request, *args, **kwargs):
message_key = request.GET.get('message', '')
self.message = self.messages.get(message_key, '')
return super().dispatch(request, *args, **kwargs)
def get_queryset(self):
return Task.objects.all().order_by('run_at')
def get_context_data(self, *args, **kwargs):
data = super().get_context_data(*args, **kwargs)
data['message'] = self.message
data['running'] = []
data['errors'] = []
data['scheduled'] = []
@ -610,3 +635,36 @@ class CompletedTasksView(ListView):
data['message'] = message.format(name=self.filter_source.name)
data['source'] = self.filter_source
return data
class ResetTasks(FormView):
'''
Confirm that all tasks should be reset. As all tasks are triggered from
signals by checking for files existing etc. this can be done by just deleting
all tasks and then calling every Source objects .save() method.
'''
template_name = 'sync/tasks-reset.html'
form_class = ResetTasksForm
def form_valid(self, form):
# Delete all tasks
Task.objects.all().delete()
# Iter all tasks
for source in Source.objects.all():
# Recreate the initial indexing task
verbose_name = _('Index media from source "{}"')
index_source_task(
str(source.pk),
repeat=source.index_schedule,
queue=str(source.pk),
priority=5,
verbose_name=verbose_name.format(source.name)
)
# This also chains down to call each Media objects .save() as well
source.save()
return super().form_valid(form)
def get_success_url(self):
url = reverse_lazy('sync:tasks')
return append_uri_params(url, {'message': 'reset'})

View File

@ -119,7 +119,7 @@ MAX_RUN_TIME = 1800 # Maximum amount of time in seconds
BACKGROUND_TASK_RUN_ASYNC = False # Run tasks async in the background
BACKGROUND_TASK_ASYNC_THREADS = 1 # Number of async tasks to run at once
BACKGROUND_TASK_PRIORITY_ORDERING = 'ASC' # Use 'niceness' task priority ordering
COMPLETED_TASKS_DAYS_TO_KEEP = 30 # Number of days to keep completed tasks
COMPLETED_TASKS_DAYS_TO_KEEP = 7 # Number of days to keep completed tasks
SOURCES_PER_PAGE = 36