diff options
-rw-r--r-- | mediagoblin.example.ini | 1 | ||||
-rw-r--r-- | mediagoblin/plugins/processing_info/README.rst | 5 | ||||
-rw-r--r-- | mediagoblin/plugins/processing_info/__init__.py | 50 | ||||
-rw-r--r-- | mediagoblin/plugins/processing_info/templates/mediagoblin/processing_info/header_left.html | 32 | ||||
-rw-r--r-- | mediagoblin/processing/__init__.py | 16 | ||||
-rw-r--r-- | mediagoblin/static/css/base.css | 56 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/base.html | 1 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/moderation/media_panel.html | 12 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/user_pages/processing_panel.html | 106 | ||||
-rw-r--r-- | mediagoblin/user_pages/routing.py | 5 | ||||
-rw-r--r-- | mediagoblin/user_pages/views.py | 36 |
11 files changed, 233 insertions, 87 deletions
diff --git a/mediagoblin.example.ini b/mediagoblin.example.ini index 52331d82..7b528359 100644 --- a/mediagoblin.example.ini +++ b/mediagoblin.example.ini @@ -57,4 +57,5 @@ base_url = /mgoblin_media/ [plugins] [[mediagoblin.plugins.geolocation]] [[mediagoblin.plugins.basic_auth]] +[[mediagoblin.plugins.processing_info]] [[mediagoblin.media_types.image]] diff --git a/mediagoblin/plugins/processing_info/README.rst b/mediagoblin/plugins/processing_info/README.rst new file mode 100644 index 00000000..3bacbf50 --- /dev/null +++ b/mediagoblin/plugins/processing_info/README.rst @@ -0,0 +1,5 @@ +============== + sampleplugin +============== + +A plugin to insert some useful information about processing to templates diff --git a/mediagoblin/plugins/processing_info/__init__.py b/mediagoblin/plugins/processing_info/__init__.py new file mode 100644 index 00000000..e5ce0a1c --- /dev/null +++ b/mediagoblin/plugins/processing_info/__init__.py @@ -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 + } diff --git a/mediagoblin/plugins/processing_info/templates/mediagoblin/processing_info/header_left.html b/mediagoblin/plugins/processing_info/templates/mediagoblin/processing_info/header_left.html new file mode 100644 index 00000000..1d5e724d --- /dev/null +++ b/mediagoblin/plugins/processing_info/templates/mediagoblin/processing_info/header_left.html @@ -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") }}">⏳ {{ 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") }}">☹ {{ num_failed }}</a> +</span> +{% endif %} +{% endif %} diff --git a/mediagoblin/processing/__init__.py b/mediagoblin/processing/__init__.py index aa8f1447..29345227 100644 --- a/mediagoblin/processing/__init__.py +++ b/mediagoblin/processing/__init__.py @@ -325,14 +325,13 @@ def mark_entry_failed(entry_id, exc): u'fail_metadata': exc.metadata}) else: _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 - # failure_error (we'll assume it wasn't handled) and don't record - # metadata (in fact overwrite it if somehow it had previous info - # here) + # Looks like no, let's record it so that admin could ask us about the + # reason atomic_update(mgg.database.MediaEntry, {'id': entry_id}, {u'state': u'failed', - u'fail_error': None, + u'fail_error': u'Unhandled exception: {0}'.format( + six.text_type(exc)), u'fail_metadata': {}}) @@ -410,8 +409,11 @@ class BaseProcessingFail(Exception): return u"%s:%s" % ( self.__class__.__module__, self.__class__.__name__) - def __init__(self, **metadata): - self.metadata = metadata or {} + def __init__(self, message=None, **metadata): + if message is not None: + super(BaseProcessingFail, self).__init__(message) + metadata['message'] = message + self.metadata = metadata class BadMediaFail(BaseProcessingFail): """ diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css index f8726708..52c1f2dc 100644 --- a/mediagoblin/static/css/base.css +++ b/mediagoblin/static/css/base.css @@ -171,6 +171,26 @@ header { a.logo { color: #fff; 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 { @@ -754,6 +774,42 @@ table.media_panel th { padding-bottom: 4px; 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 */ diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index 778cc3f9..9b0b7168 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -72,6 +72,7 @@ <div class="row foot"> <div class="header_left"> {%- include "mediagoblin/bits/logo.html" -%} + {% template_hook("header_left") %} {% block mediagoblin_header_title %}{% endblock %} </div> <div class="header_right"> diff --git a/mediagoblin/templates/mediagoblin/moderation/media_panel.html b/mediagoblin/templates/mediagoblin/moderation/media_panel.html index 888e4feb..94d4a1a0 100644 --- a/mediagoblin/templates/mediagoblin/moderation/media_panel.html +++ b/mediagoblin/templates/mediagoblin/moderation/media_panel.html @@ -43,8 +43,18 @@ </tr> {% for media_entry in processing_entries %} <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.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.created.strftime("%F %R") }}</td> {% if media_entry.transcoding_progress %} diff --git a/mediagoblin/templates/mediagoblin/user_pages/processing_panel.html b/mediagoblin/templates/mediagoblin/user_pages/processing_panel.html index 2a449d45..96786937 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/processing_panel.html +++ b/mediagoblin/templates/mediagoblin/user_pages/processing_panel.html @@ -17,6 +17,8 @@ #} {% extends "mediagoblin/base.html" %} +{% from "mediagoblin/utils/pagination.html" import render_pagination %} + {% block title -%} {% trans %}Media processing panel{% endtrans %} — {{ super() }} {%- endblock %} @@ -28,20 +30,53 @@ <p> {% trans %}You can track the state of media being processed for your gallery here.{% endtrans %} </p> - -<h2>{% trans %}Media in-processing{% endtrans %}</h2> -{% if processing_entries.count() %} +<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 entries.count() %} + {{ render_pagination(request, pagination) }} <table class="media_panel processing"> <tr> - <th>ID</th> + <th width="210">Thumbnail</th> <th>Title</th> - <th>When submitted</th> - <th>Transcoding progress</th> + <th width="20%">When submitted</th> + <th width="200">Transcoding progress</th> </tr> - {% for media_entry in processing_entries %} + {% for media_entry in entries %} <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.created.strftime("%F %R") }}</td> {% if media_entry.transcoding_progress %} @@ -49,61 +84,12 @@ {% else %} <td>Unknown</td> {% endif %} + {% endif %} </tr> {% endfor %} </table> + {{ render_pagination(request, pagination) }} {% 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> </td> - <td> </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 %} {% endblock %} diff --git a/mediagoblin/user_pages/routing.py b/mediagoblin/user_pages/routing.py index 1a1d4139..68cb0a3b 100644 --- a/mediagoblin/user_pages/routing.py +++ b/mediagoblin/user_pages/routing.py @@ -97,6 +97,11 @@ add_route('mediagoblin.user_pages.processing_panel', '/u/<string:user>/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 add_route('mediagoblin.edit.edit_media', '/u/<string:user>/m/<int:media_id>/edit/', diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 88e077d4..547048d6 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -637,8 +637,10 @@ def collection_atom_feed(request): return feed.get_response() +@active_user_from_url +@uses_pagination @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... and what failed, and why! @@ -653,33 +655,29 @@ def processing_panel(request): return redirect( request, 'mediagoblin.user_pages.user_home', user=user.username) - # Get media entries which are in-processing - processing_entries = MediaEntry.query.\ - filter_by(actor = user.id, - state = u'processing').\ - order_by(MediaEntry.created.desc()) + entries = (MediaEntry.query.filter_by(actor=user.id) + .order_by(MediaEntry.created.desc())) - # Get media entries which have failed to process - failed_entries = MediaEntry.query.\ - filter_by(actor = user.id, - state = u'failed').\ - order_by(MediaEntry.created.desc()) + try: + state = request.matchdict['state'] + # no exception was thrown, filter entries by state + entries = entries.filter_by(state=state) + except KeyError: + # show all entries + pass - processed_entries = MediaEntry.query.\ - filter_by(actor = user.id, - state = u'processed').\ - order_by(MediaEntry.created.desc()).\ - limit(10) + pagination = Pagination(page, entries) + pagination.per_page = 30 + entries_on_a_page = pagination() # Render to response return render_to_response( request, 'mediagoblin/user_pages/processing_panel.html', {'user': user, - 'processing_entries': processing_entries, - 'failed_entries': failed_entries, - 'processed_entries': processed_entries}) + 'entries': entries_on_a_page, + 'pagination': pagination}) @allow_reporting @get_user_media_entry |