Merge branch 'media_panel'

This commit is contained in:
Boris Bobrov 2016-02-05 04:33:05 +03:00
commit 3b6b009077
11 changed files with 233 additions and 87 deletions

View File

@ -57,4 +57,5 @@ base_url = /mgoblin_media/
[plugins] [plugins]
[[mediagoblin.plugins.geolocation]] [[mediagoblin.plugins.geolocation]]
[[mediagoblin.plugins.basic_auth]] [[mediagoblin.plugins.basic_auth]]
[[mediagoblin.plugins.processing_info]]
[[mediagoblin.media_types.image]] [[mediagoblin.media_types.image]]

View File

@ -0,0 +1,5 @@
==============
sampleplugin
==============
A plugin to insert some useful information about processing to templates

View File

@ -0,0 +1,50 @@
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import logging
import os
from mediagoblin.tools.pluginapi import get_config
from mediagoblin.db.models import MediaEntry
from mediagoblin.tools import pluginapi
_log = logging.getLogger(__name__)
PLUGIN_DIR = os.path.dirname(__file__)
def setup_plugin():
pluginapi.register_template_path(os.path.join(PLUGIN_DIR, 'templates'))
pluginapi.register_template_hooks(
{'header_left': 'mediagoblin/processing_info/header_left.html'})
return
def make_stats(context):
request = context['request']
user = request.user
num_queued = MediaEntry.query.filter_by(
actor=user.id, state=u'processing').count()
context['num_queued'] = num_queued
num_failed = MediaEntry.query.filter_by(
actor=user.id, state=u'failed').count()
context['num_failed'] = num_failed
return context
hooks = {
'setup': setup_plugin,
'template_context_prerender': make_stats
}

View File

@ -0,0 +1,32 @@
{#
# GNU MediaGoblin -- federated, autonomous media hosting
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#}
{#This injects some information about entries in processing #}
{% if request.user and request.user.has_privilege('active') %}
{% if num_queued is defined and num_queued != 0 %}
<span class="num_queued status_icon">
<a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
user=request.user.username, state="processing") }}">&#9203; {{ num_queued }}</a>
</span>
{% endif %}
{% if num_failed is defined and num_failed != 0 %}
<span class="num_failed status_icon">
<a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
user=request.user.username, state="failed") }}">&#9785; {{ num_failed }}</a>
</span>
{% endif %}
{% endif %}

View File

@ -325,14 +325,13 @@ def mark_entry_failed(entry_id, exc):
u'fail_metadata': exc.metadata}) u'fail_metadata': exc.metadata})
else: else:
_log.warn("No idea what happened here, but it failed: %r", exc) _log.warn("No idea what happened here, but it failed: %r", exc)
# Looks like no, so just mark it as failed and don't record a # Looks like no, let's record it so that admin could ask us about the
# failure_error (we'll assume it wasn't handled) and don't record # reason
# metadata (in fact overwrite it if somehow it had previous info
# here)
atomic_update(mgg.database.MediaEntry, atomic_update(mgg.database.MediaEntry,
{'id': entry_id}, {'id': entry_id},
{u'state': u'failed', {u'state': u'failed',
u'fail_error': None, u'fail_error': u'Unhandled exception: {0}'.format(
six.text_type(exc)),
u'fail_metadata': {}}) u'fail_metadata': {}})
@ -410,8 +409,11 @@ class BaseProcessingFail(Exception):
return u"%s:%s" % ( return u"%s:%s" % (
self.__class__.__module__, self.__class__.__name__) self.__class__.__module__, self.__class__.__name__)
def __init__(self, **metadata): def __init__(self, message=None, **metadata):
self.metadata = metadata or {} if message is not None:
super(BaseProcessingFail, self).__init__(message)
metadata['message'] = message
self.metadata = metadata
class BadMediaFail(BaseProcessingFail): class BadMediaFail(BaseProcessingFail):
""" """

View File

@ -171,6 +171,26 @@ header {
a.logo { a.logo {
color: #fff; color: #fff;
font-weight: bold; font-weight: bold;
text-decoration: none;
}
.status_icon {
border-radius: 2px;
padding: 4px;
margin: 0px 4px;
}
.num_queued {
background: #56446F;
}
.num_failed {
background: #87453B;
}
.status_icon a {
display: inline-block;
color: #C3C3C3;
} }
.logo img { .logo img {
@ -754,6 +774,42 @@ table.media_panel th {
padding-bottom: 4px; padding-bottom: 4px;
text-align: left; text-align: left;
} }
.thumb-overlay-status {
position: absolute;
margin: auto;
top: 0; bottom: 0; left: 0; right: 0;
width: 180px;
height: 20px;
display: inline;
text-align: center;
background-color: rgba(255, 255, 255, 0.8);
}
.thumb-processing {
color: black;
font-weight: bold;
}
.thumb-failed {
color: red;
font-weight: bold;
}
.thumb-wrapper {
position: relative;
/* for proportional thumb resizing */
width: auto;
height: auto;
display: inline-block;
}
.thumb-wrapper img {
max-height: 180px;
max-width: 180px;
}
.media_panel td {
vertical-align: middle;
}
/* moderator panels */ /* moderator panels */

View File

@ -72,6 +72,7 @@
<div class="row foot"> <div class="row foot">
<div class="header_left"> <div class="header_left">
{%- include "mediagoblin/bits/logo.html" -%} {%- include "mediagoblin/bits/logo.html" -%}
{% template_hook("header_left") %}
{% block mediagoblin_header_title %}{% endblock %} {% block mediagoblin_header_title %}{% endblock %}
</div> </div>
<div class="header_right"> <div class="header_right">

View File

@ -43,8 +43,18 @@
</tr> </tr>
{% for media_entry in processing_entries %} {% for media_entry in processing_entries %}
<tr> <tr>
<td>
<div class="thumb-wrapper">
<img src="{{ media_entry.thumb_url }}" alt="{{ media_entry.title }}" />
<div class="thumb-overlay-status thumb-processing">Processing...</div>
</div>
</td>
<td>{{ media_entry.id }}</td> <td>{{ media_entry.id }}</td>
<td>{{ media_entry.get_actor.username }}</td> <td>
<a href="{{ request.urlgen('mediagoblin.moderation.users_detail', user=media_entry.get_actor.username) }}">
{{ media_entry.get_actor.username }}
</a>
</td>
<td>{{ media_entry.title }}</td> <td>{{ media_entry.title }}</td>
<td>{{ media_entry.created.strftime("%F %R") }}</td> <td>{{ media_entry.created.strftime("%F %R") }}</td>
{% if media_entry.transcoding_progress %} {% if media_entry.transcoding_progress %}

View File

@ -17,6 +17,8 @@
#} #}
{% extends "mediagoblin/base.html" %} {% extends "mediagoblin/base.html" %}
{% from "mediagoblin/utils/pagination.html" import render_pagination %}
{% block title -%} {% block title -%}
{% trans %}Media processing panel{% endtrans %} &mdash; {{ super() }} {% trans %}Media processing panel{% endtrans %} &mdash; {{ super() }}
{%- endblock %} {%- endblock %}
@ -29,19 +31,52 @@
{% trans %}You can track the state of media being processed for your gallery here.{% endtrans %} {% trans %}You can track the state of media being processed for your gallery here.{% endtrans %}
</p> </p>
<h2>{% trans %}Media in-processing{% endtrans %}</h2> <p>
Show:
<a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
user=request.user.username) }}">All</a>,
<a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
user=request.user.username, state="processing") }}">In processing</a>,
<a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
user=request.user.username, state="failed") }}">Failed</a>,
<a href="{{ request.urlgen('mediagoblin.user_pages.processing_panel',
user=request.user.username, state="processed") }}">Succesful</a>
</p>
{% if processing_entries.count() %} {% if entries.count() %}
{{ render_pagination(request, pagination) }}
<table class="media_panel processing"> <table class="media_panel processing">
<tr> <tr>
<th>ID</th> <th width="210">Thumbnail</th>
<th>Title</th> <th>Title</th>
<th>When submitted</th> <th width="20%">When submitted</th>
<th>Transcoding progress</th> <th width="200">Transcoding progress</th>
</tr> </tr>
{% for media_entry in processing_entries %} {% for media_entry in entries %}
<tr> <tr>
<td>{{ media_entry.id }}</td> {% if media_entry.state == 'processed' %}
{% set entry_url = media_entry.url_for_self(request.urlgen) %}
<td>
<div class="thumb-wrapper">
<a href="{{ entry_url }}">
<img src="{{ media_entry.thumb_url }}" alt="{{ media_entry.title }}" />
</a>
</div>
</td>
<td><a href="{{ entry_url }}">{{ media_entry.title }}</a></td>
<td>{{ media_entry.created.strftime("%F %R") }}</td>
<td>Ready</td>
{% else %}
<td>
<div class="thumb-wrapper">
<img src="{{ media_entry.thumb_url }}" alt="{{ media_entry.title }}" />
{% if media_entry.state == 'processing' %}
<div class="thumb-overlay-status thumb-processing">Processing...</div>
{% elif media_entry.state == 'failed' %}
<div class="thumb-overlay-status thumb-failed">Failed!</div>
{% endif %}
</div>
</td>
<td>{{ media_entry.title }}</td> <td>{{ media_entry.title }}</td>
<td>{{ media_entry.created.strftime("%F %R") }}</td> <td>{{ media_entry.created.strftime("%F %R") }}</td>
{% if media_entry.transcoding_progress %} {% if media_entry.transcoding_progress %}
@ -49,61 +84,12 @@
{% else %} {% else %}
<td>Unknown</td> <td>Unknown</td>
{% endif %} {% endif %}
{% endif %}
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{{ render_pagination(request, pagination) }}
{% else %} {% else %}
<p><em>{% trans %}No media in-processing{% endtrans %}</em></p> <p><em>{% trans %}You have not uploaded anything yet!{% endtrans %}</em></p>
{% endif %}
<h2>{% trans %}These uploads failed to process:{% endtrans %}</h2>
{% if failed_entries.count() %}
<table class="media_panel failed">
<tr>
<th>ID</th>
<th>Title</th>
<th>When submitted</th>
<th>Reason for failure</th>
<th>Failure metadata</th>
</tr>
{% for media_entry in failed_entries %}
<tr>
<td>{{ media_entry.id }}</td>
<td>{{ media_entry.title }}</td>
<td>{{ media_entry.created.strftime("%F %R") }}</td>
{% if media_entry.get_fail_exception() %}
<td>{{ media_entry.get_fail_exception().general_message }}</td>
<td>{{ media_entry.fail_metadata }}</td>
{% else %}
<td>&nbsp;</td>
<td>&nbsp;</td>
{% endif %}
</tr>
{% endfor %}
</table>
{% else %}
<p><em>{% trans %}No failed entries!{% endtrans %}</em></p>
{% endif %}
<h2>{% trans %}Your last 10 successful uploads{% endtrans %}</h2>
{% if processed_entries.count() %}
<table class="media_panel processed">
<tr>
<th>ID</th>
<th>Title</th>
<th>Submitted</th>
</tr>
{% for entry in processed_entries %}
<tr>
<td>{{ entry.id }}</td>
<td><a href="{{ entry.url_for_self(request.urlgen) }}">{{ entry.title }}</a></td>
<td>{{ entry.created.strftime("%F %R") }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<p><em>{% trans %}No processed entries, yet!{% endtrans %}</em></p>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -97,6 +97,11 @@ add_route('mediagoblin.user_pages.processing_panel',
'/u/<string:user>/panel/', '/u/<string:user>/panel/',
'mediagoblin.user_pages.views:processing_panel') 'mediagoblin.user_pages.views:processing_panel')
add_route('mediagoblin.user_pages.processing_panel',
'/u/<string:user>/panel/<any(processed, processing, failed):state>/',
'mediagoblin.user_pages.views:processing_panel')
# Stray edit routes # Stray edit routes
add_route('mediagoblin.edit.edit_media', add_route('mediagoblin.edit.edit_media',
'/u/<string:user>/m/<int:media_id>/edit/', '/u/<string:user>/m/<int:media_id>/edit/',

View File

@ -637,8 +637,10 @@ def collection_atom_feed(request):
return feed.get_response() return feed.get_response()
@active_user_from_url
@uses_pagination
@require_active_login @require_active_login
def processing_panel(request): def processing_panel(request, page, url_user):
""" """
Show to the user what media is still in conversion/processing... Show to the user what media is still in conversion/processing...
and what failed, and why! and what failed, and why!
@ -653,33 +655,29 @@ def processing_panel(request):
return redirect( return redirect(
request, 'mediagoblin.user_pages.user_home', request, 'mediagoblin.user_pages.user_home',
user=user.username) user=user.username)
# Get media entries which are in-processing # Get media entries which are in-processing
processing_entries = MediaEntry.query.\ entries = (MediaEntry.query.filter_by(actor=user.id)
filter_by(actor = user.id, .order_by(MediaEntry.created.desc()))
state = u'processing').\
order_by(MediaEntry.created.desc())
# Get media entries which have failed to process try:
failed_entries = MediaEntry.query.\ state = request.matchdict['state']
filter_by(actor = user.id, # no exception was thrown, filter entries by state
state = u'failed').\ entries = entries.filter_by(state=state)
order_by(MediaEntry.created.desc()) except KeyError:
# show all entries
pass
processed_entries = MediaEntry.query.\ pagination = Pagination(page, entries)
filter_by(actor = user.id, pagination.per_page = 30
state = u'processed').\ entries_on_a_page = pagination()
order_by(MediaEntry.created.desc()).\
limit(10)
# Render to response # Render to response
return render_to_response( return render_to_response(
request, request,
'mediagoblin/user_pages/processing_panel.html', 'mediagoblin/user_pages/processing_panel.html',
{'user': user, {'user': user,
'processing_entries': processing_entries, 'entries': entries_on_a_page,
'failed_entries': failed_entries, 'pagination': pagination})
'processed_entries': processed_entries})
@allow_reporting @allow_reporting
@get_user_media_entry @get_user_media_entry