Panel improvements
authorJoar Wandborg <git@wandborg.com>
Tue, 10 Jul 2012 22:36:42 +0000 (00:36 +0200)
committerJoar Wandborg <git@wandborg.com>
Tue, 10 Jul 2012 22:36:42 +0000 (00:36 +0200)
- Added progress meter for video and audio media types.
- Changed the __repr__ method of a MediaEntry to display a bit more
  useful explanation.
- Added a new MediaEntry.state, 'processing', which means that the task
  is running the processor on the item currently.
- Fixed some PEP8 issues in user_pages/views.py
- Fixed the ATOM TAG URI to show the correct year.

mediagoblin/db/sql/migrations.py
mediagoblin/db/sql/models.py
mediagoblin/media_types/audio/processing.py
mediagoblin/media_types/audio/transcoders.py
mediagoblin/media_types/video/processing.py
mediagoblin/media_types/video/transcoders.py
mediagoblin/processing/__init__.py
mediagoblin/processing/task.py
mediagoblin/templates/mediagoblin/user_pages/processing_panel.html
mediagoblin/user_pages/views.py

index d6b709b2c483c0f392c7627e5a5c37c07f1ceb79..49798a541d878e915f3959313c83585549144ceb 100644 (file)
@@ -14,7 +14,7 @@
 # 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/>.
 
-from sqlalchemy import MetaData, Table, Column, Boolean
+from sqlalchemy import MetaData, Table, Column, Boolean, SmallInteger
 
 from mediagoblin.db.sql.util import RegisterMigration
 
@@ -47,3 +47,15 @@ def add_wants_notification_column(db_conn):
             default=True, nullable=True)
     col.create(users, populate_defaults=True)
     db_conn.commit()
+
+
+@RegisterMigration(3, MIGRATIONS)
+def add_transcoding_progress(db_conn):
+    metadata = MetaData(bind=db_conn.bind)
+
+    media_entry = Table('core__media_entries', metadata, autoload=True,
+            autoload_with=db_conn.bind)
+
+    col = Column('transcoding_progress', SmallInteger)
+    col.create(media_entry)
+    db_conn.commit()
index 9815fcf952817fcc7384b6c541b39c35549c6e3b..6ce4e06fafdf93296552f68c10f6dab8afd17619 100644 (file)
@@ -107,6 +107,8 @@ class MediaEntry(Base, MediaEntryMixin):
     fail_error = Column(Unicode)
     fail_metadata = Column(JSONEncoded)
 
+    transcoding_progress = Column(SmallInteger)
+
     queued_media_file = Column(PathTupleWithSlashes)
 
     queued_task_id = Column(Unicode)
@@ -209,6 +211,12 @@ class MediaEntry(Base, MediaEntryMixin):
         __import__(models_module)
         return sys.modules[models_module].DATA_MODEL
 
+    def __repr__(self):
+        return '<{classname} {id}: {title}>'.format(
+                classname=self.__class__.__name__,
+                id=self.id,
+                title=self.title)
+
 
 class FileKeynames(Base):
     """
index 558a37f048dbe3397cb1c2ae1289a944034a51f6..18edbf1af37796f3a31c3c051c9c1e67019412c9 100644 (file)
@@ -20,13 +20,14 @@ import os
 
 from mediagoblin import mg_globals as mgg
 from mediagoblin.processing import (create_pub_filepath, BadMediaFail,
-    FilenameBuilder)
+    FilenameBuilder, ProgressCallback)
 
 from mediagoblin.media_types.audio.transcoders import (AudioTranscoder,
     AudioThumbnailer)
 
 _log = logging.getLogger(__name__)
 
+
 def sniff_handler(media_file, **kw):
     try:
         transcoder = AudioTranscoder()
@@ -40,6 +41,7 @@ def sniff_handler(media_file, **kw):
 
     return False
 
+
 def process_audio(entry):
     audio_config = mgg.global_config['media_type:mediagoblin.media_types.audio']
 
@@ -72,11 +74,13 @@ def process_audio(entry):
     transcoder = AudioTranscoder()
 
     with tempfile.NamedTemporaryFile() as webm_audio_tmp:
+        progress_callback = ProgressCallback(entry)
 
         transcoder.transcode(
             queued_filename,
             webm_audio_tmp.name,
-            quality=audio_config['quality'])
+            quality=audio_config['quality'],
+            progress_callback=progress_callback)
 
         transcoder.discover(webm_audio_tmp.name)
 
index be80aa0e9841eef1d73f35f195b9a9678a0fd51b..f3d49c309edf06b735f2cb0038432e31351bb5df 100644 (file)
@@ -206,7 +206,7 @@ class AudioTranscoder(object):
             data = dict(message.structure)
 
             if self.__on_progress:
-                self.__on_progress(data)
+                self.__on_progress(data.get('percent'))
 
             _log.info('{0}% done...'.format(
                     data.get('percent')))
index 85e833521bf22865bcae2c233399b38261530c2d..abd14eed2fadeb0a289c660b48242e8e6fd93df7 100644 (file)
@@ -19,7 +19,7 @@ import logging
 
 from mediagoblin import mg_globals as mgg
 from mediagoblin.processing import \
-    create_pub_filepath, FilenameBuilder, BaseProcessingFail
+    create_pub_filepath, FilenameBuilder, BaseProcessingFail, ProgressCallback
 from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
 
 from . import transcoders
@@ -78,11 +78,13 @@ def process_video(entry):
 
     with tmp_dst:
         # Transcode queued file to a VP8/vorbis file that fits in a 640x640 square
+        progress_callback = ProgressCallback(entry)
         transcoder = transcoders.VideoTranscoder()
         transcoder.transcode(queued_filename, tmp_dst.name,
                 vp8_quality=video_config['vp8_quality'],
                 vp8_threads=video_config['vp8_threads'],
-                vorbis_quality=video_config['vorbis_quality'])
+                vorbis_quality=video_config['vorbis_quality'],
+                progress_callback=progress_callback)
 
         # Push transcoded video to public storage
         _log.debug('Saving medium...')
index e03f721f0f978f10d4c0dc53056a2aa2be0e02b4..25846ffab9a41cdbdab790e6fcbf2e71b37c73b6 100644 (file)
@@ -625,7 +625,7 @@ class VideoTranscoder:
                 data = dict(message.structure)
 
                 if self._progress_callback:
-                    self._progress_callback(data)
+                    self._progress_callback(data.get('percent'))
 
                 _log.info('{percent}% done...'.format(
                         percent=data.get('percent')))
index 85b618803447c0550756df39cc90b513bff4755c..6b2d50e2808a116f39eeaaf67998a6de799fd401 100644 (file)
@@ -25,12 +25,23 @@ from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
 _log = logging.getLogger(__name__)
 
 
+class ProgressCallback(object):
+    def __init__(self, entry):
+        self.entry = entry
+
+    def __call__(self, progress):
+        if progress:
+            self.entry.transcoding_progress = progress
+            self.entry.save()
+
+
 def create_pub_filepath(entry, filename):
     return mgg.public_store.get_unique_filepath(
             ['media_entries',
              unicode(entry._id),
              filename])
 
+
 class FilenameBuilder(object):
     """Easily slice and dice filenames.
 
index af815362adca7d9d3c08c250346fd845203ec3cb..58e36a113f4fd140cf2556b49861e307729375c8 100644 (file)
@@ -44,20 +44,24 @@ class ProcessMedia(Task):
         entry = mgg.database.MediaEntry.one(
             {'_id': ObjectId(media_id)})
 
-        _log.info('Running task {0} on media {1}: {2}'.format(
-            self.name,
-            entry._id,
-            entry.title))
-
         # Try to process, and handle expected errors.
         try:
-            #__import__(entry.media_type)
             manager = get_media_manager(entry.media_type)
+
+            entry.state = u'processing'
+            entry.save()
+
             _log.debug('Processing {0}'.format(entry))
+
             manager['processor'](entry)
+
+            entry.state = u'processed'
+            entry.save()
+
         except BaseProcessingFail as exc:
             mark_entry_failed(entry._id, exc)
             return
+
         except ImportError as exc:
             _log.error(
                 'Entry {0} failed to process due to an import error: {1}'\
@@ -67,9 +71,6 @@ class ProcessMedia(Task):
 
             mark_entry_failed(entry._id, exc)
 
-        entry.state = u'processed'
-        entry.save()
-
     def on_failure(self, exc, task_id, args, kwargs, einfo):
         """
         If the processing failed we should mark that in the database.
index e868456b1462925d83cba730f868979029e62c25..ac2fd44c1047b29c0365d26c900038376d5a0dde 100644 (file)
       <th>ID</th>
       <th>Title</th>
       <th>When submitted</th>
-      <th>Status</th>
+      <th>Transcoding progress</th>
     </tr>
     {% for media_entry in processing_entries %}
       <tr>
         <td>{{ media_entry._id }}</td>
         <td>{{ media_entry.title }}</td>
         <td>{{ media_entry.created.strftime("%m-%d-%Y %I:%M %p") }}</td>
-        <td></td>
+        {% if media_entry.transcoding_progress %}
+        <td>{{ media_entry.transcoding_progress }}%</td>
+        {% else %}
+        <td>Unknown</td>
+        {% endif %}
       </tr>
     {% endfor %}
   </table>
 {% else %}
-  <p><i>{% trans %}No media in-processing{% endtrans %}</i></p>
+  <p><em>{% trans %}No media in-processing{% endtrans %}</em></p>
 {% endif %}  
 
 {% if failed_entries.count() %}
       </tr>
     {% endfor %}
   </table>
-{% endif %}  
+{% else %}
+  <p><em>{% trans %}No failed entries!{% endtrans %}</em></p>
+{% endif %}
+{% if processed_entries.count() %}
+  <h2>{% trans %}Your last 10 successful uploads{% endtrans %}</h2>
+
+  <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("%m-%d-%Y %I:%M %p") }}</td>
+      </tr>
+    {% endfor %}
+  </table>
+{% else %}
+  <p><em>{% trans %}No processed entries, yet!{% endtrans %}</em></p>
+{% endif %}
 {% endblock %}
index 0e061c4671d47622e05c46de135f10bdd1fcdabb..4c86aadcb3f713a899e0e8b6123a25e148f00f57 100644 (file)
@@ -16,6 +16,7 @@
 
 from webob import exc
 import logging
+import datetime
 
 from mediagoblin import messages, mg_globals
 from mediagoblin.db.util import DESCENDING, ObjectId
@@ -37,6 +38,7 @@ from mediagoblin.media_types import get_media_manager
 _log = logging.getLogger(__name__)
 _log.setLevel(logging.DEBUG)
 
+
 @uses_pagination
 def user_home(request, page):
     """'Homepage' of a User()"""
@@ -251,10 +253,11 @@ def atom_feed(request):
     atomlinks = [{
            'href': request.urlgen(
                'mediagoblin.user_pages.user_home',
-               qualified=True,user=request.matchdict['user']),
+               qualified=True, user=request.matchdict['user']),
            'rel': 'alternate',
            'type': 'text/html'
-           }];
+           }]
+
     if mg_globals.app_config["push_urls"]:
         for push_url in mg_globals.app_config["push_urls"]:
             atomlinks.append({
@@ -264,14 +267,16 @@ def atom_feed(request):
     feed = AtomFeed(
                "MediaGoblin: Feed for user '%s'" % request.matchdict['user'],
                feed_url=request.url,
-               id='tag:'+request.host+',2011:gallery.user-'+request.matchdict['user'],
+               id='tag:{host},{year}:gallery.user-{user}'.format(
+                   host=request.host,
+                   year=datetime.datetime.today().strftime('%Y'),
+                   user=request.matchdict['user']),
                links=atomlinks)
 
-
     for entry in cursor:
         feed.add(entry.get('title'),
             entry.description_html,
-            id=entry.url_for_self(request.urlgen,qualified=True),
+            id=entry.url_for_self(request.urlgen, qualified=True),
             content_type='html',
             author={
                 'name': entry.get_uploader.username,
@@ -323,17 +328,22 @@ def processing_panel(request):
     # Get media entries which are in-processing
     processing_entries = request.db.MediaEntry.find(
         {'uploader': user._id,
-         'state': u'unprocessed'}).sort('created', DESCENDING)
+         'state': u'processing'}).sort('created', DESCENDING)
 
     # Get media entries which have failed to process
     failed_entries = request.db.MediaEntry.find(
         {'uploader': user._id,
          'state': u'failed'}).sort('created', DESCENDING)
 
+    processed_entries = request.db.MediaEntry.find(
+            {'uploader': user._id,
+                'state': u'processed'}).sort('created', DESCENDING).limit(10)
+
     # Render to response
     return render_to_response(
         request,
         'mediagoblin/user_pages/processing_panel.html',
         {'user': user,
          'processing_entries': processing_entries,
-         'failed_entries': failed_entries})
+         'failed_entries': failed_entries,
+         'processed_entries': processed_entries})