Merge branch 'media_panel'
This commit is contained in:
commit
3b6b009077
@ -57,4 +57,5 @@ base_url = /mgoblin_media/
|
||||
[plugins]
|
||||
[[mediagoblin.plugins.geolocation]]
|
||||
[[mediagoblin.plugins.basic_auth]]
|
||||
[[mediagoblin.plugins.processing_info]]
|
||||
[[mediagoblin.media_types.image]]
|
||||
|
5
mediagoblin/plugins/processing_info/README.rst
Normal file
5
mediagoblin/plugins/processing_info/README.rst
Normal file
@ -0,0 +1,5 @@
|
||||
==============
|
||||
sampleplugin
|
||||
==============
|
||||
|
||||
A plugin to insert some useful information about processing to templates
|
50
mediagoblin/plugins/processing_info/__init__.py
Normal file
50
mediagoblin/plugins/processing_info/__init__.py
Normal 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
|
||||
}
|
@ -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 %}
|
@ -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):
|
||||
"""
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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">
|
||||
|
@ -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 %}
|
||||
|
@ -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 %}
|
||||
|
@ -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/',
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user