added image reprocessing
authorRodney Ewing <ewing.rj@gmail.com>
Fri, 2 Aug 2013 18:40:41 +0000 (11:40 -0700)
committerRodney Ewing <ewing.rj@gmail.com>
Fri, 16 Aug 2013 22:30:14 +0000 (15:30 -0700)
mediagoblin/gmg_commands/reprocess.py
mediagoblin/media_types/image/__init__.py
mediagoblin/media_types/image/processing.py
mediagoblin/processing/__init__.py
mediagoblin/processing/task.py
mediagoblin/submit/lib.py

index 0390c48d8ea1de5ea13527af5815bd20f388a143..f6b9e653c2d67ac6e612baf9741c4ee769a46bd1 100644 (file)
@@ -13,6 +13,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 mediagoblin import mg_globals
 from mediagoblin.db.models import MediaEntry
 from mediagoblin.gmg_commands import util as commands_util
 from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
@@ -143,6 +144,8 @@ def reprocess(args):
     _set_media_state(args)
     _set_media_type(args)
 
+    import ipdb
+    ipdb.set_trace()
     if not args[0].media_id:
         return _reprocess_all(args)
 
index a1b4347909c127a47dbd1d9f65e528ac9a90b7d3..3a05671894bd7e3ca39da0ca0d98fdd681251da9 100644 (file)
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 import argparse
 import datetime
+import logging
 
+from mediagoblin.db.models import MediaEntry
 from mediagoblin.media_types import MediaManagerBase
 from mediagoblin.media_types.image.processing import process_image, \
     sniff_handler
+from mediagoblin.submit.lib import run_process_media
 from mediagoblin.tools import pluginapi
 from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
 
+_log = logging.getLogger(__name__)
+
 
 ACCEPTED_EXTENSIONS = ["jpg", "jpeg", "png", "gif", "tiff"]
 MEDIA_TYPE = 'mediagoblin.media_types.image'
@@ -69,7 +74,7 @@ def get_media_type_and_manager(ext):
 def reprocess_action(args):
     if args[0].state == 'processed':
         print _('\n Available reprocessing actions for processed images:'
-                '\n \t --resize: thumbnail or medium'
+                '\n \t --resize: thumb or medium'
                 '\n Options:'
                 '\n \t --size: max_width max_height (defaults to config specs)')
         return True
@@ -78,8 +83,7 @@ def reprocess_action(args):
 def _parser(args):
     parser = argparse.ArgumentParser()
     parser.add_argument(
-        '--resize',
-    action='store_true')
+        '--resize')
     parser.add_argument(
         '--size',
         nargs=2)
@@ -100,6 +104,9 @@ def _check_eligible(entry_args, reprocess_args):
         if reprocess_args.resize:
             raise Exception(_('You can not run --resize on media that has not'
                               'been processed.'))
+        if reprocess_args.size:
+            _log.warn('With --initial_processing, the --size flag will be'
+                      ' ignored.')
 
     if entry_args.state == 'processing':
         raise Exception(_('We currently do not support reprocessing on media'
@@ -111,8 +118,38 @@ def media_reprocess(args):
     entry_args = args[0]
 
     _check_eligible(entry_args, reprocess_args)
-    import ipdb
-    ipdb.set_trace()
+    if reprocess_args.initial_processing:
+        for id in entry_args.media_id:
+            entry = MediaEntry.query.filter_by(id=id).first()
+            # Should we get the feed_url?
+            run_process_media(entry)
+
+    elif reprocess_args.resize:
+        if reprocess_args.resize == 'medium' or reprocess_args.resize == \
+           'thumb':
+            for id in entry_args.media_id:
+                entry = MediaEntry.query.filter_by(id=id).first()
+
+                # For now we can only reprocess with the original file
+                if not entry.media_files.get('original'):
+                    raise Exception(_('The original file for this media entry'
+                                      'does not exist.'))
+
+                reprocess_info = {'resize': reprocess_args.resize}
+
+                if reprocess_args.size and len(reprocess_args.size) == 2:
+                    reprocess_info['max_width'] = reprocess_args.size[0]
+                    reprocess_info['max_height'] = reprocess_args.size[1]
+
+                run_process_media(entry, reprocess_info)
+
+        else:
+            raise Exception(_('The --resize flag must set either "thumb"'
+                              ' or "medium".'))
+
+    else:
+        _log.warn('You must set either --resize or --initial_processing flag'
+                  ' to reprocess an image.')
 
 
 hooks = {
index baf2ac7e991a34de360d8b35d66d7df565bf806d..4f619f47f4611e16e6e5c8ba206884dbc64393d3 100644 (file)
@@ -74,11 +74,13 @@ def resize_image(proc_state, resized, keyname, target_name, new_size,
 
 
 def resize_tool(proc_state, force, keyname, target_name,
-                conversions_subdir, exif_tags):
+                conversions_subdir, exif_tags, new_size=None):
     # filename -- the filename of the original image being resized
     filename = proc_state.get_queued_filename()
-    max_width = mgg.global_config['media:' + keyname]['max_width']
-    max_height = mgg.global_config['media:' + keyname]['max_height']
+    if not new_size:
+        max_width = mgg.global_config['media:' + keyname]['max_width']
+        max_height = mgg.global_config['media:' + keyname]['max_height']
+        new_size = (max_width, max_height)
     # If the size of the original file exceeds the specified size for the desized
     # file, a target_name file is created and later associated with the media
     # entry.
@@ -93,7 +95,7 @@ def resize_tool(proc_state, force, keyname, target_name,
         or exif_image_needs_rotation(exif_tags):
         resize_image(
             proc_state, im, unicode(keyname), target_name,
-            (max_width, max_height),
+            new_size,
             exif_tags, conversions_subdir)
 
 
@@ -119,7 +121,7 @@ def sniff_handler(media_file, **kw):
     return None
 
 
-def process_image(proc_state):
+def process_image(proc_state, reprocess_info=None):
     """Code to process an image. Will be run by celery.
 
     A Workbench() represents a local tempory dir. It is automatically
@@ -127,45 +129,75 @@ def process_image(proc_state):
     """
     entry = proc_state.entry
     workbench = proc_state.workbench
-
+    import ipdb
+    ipdb.set_trace()
     # Conversions subdirectory to avoid collisions
     conversions_subdir = os.path.join(
         workbench.dir, 'conversions')
     os.mkdir(conversions_subdir)
 
-    queued_filename = proc_state.get_queued_filename()
-    name_builder = FilenameBuilder(queued_filename)
+    if reprocess_info:
+        _reprocess_image(proc_state, reprocess_info, conversions_subdir)
+
+    else:
+        queued_filename = proc_state.get_queued_filename()
+        name_builder = FilenameBuilder(queued_filename)
 
-    # EXIF extraction
-    exif_tags = extract_exif(queued_filename)
-    gps_data = get_gps_data(exif_tags)
+        # EXIF extraction
+        exif_tags = extract_exif(queued_filename)
+        gps_data = get_gps_data(exif_tags)
 
-    # Always create a small thumbnail
-    resize_tool(proc_state, True, 'thumb',
-                name_builder.fill('{basename}.thumbnail{ext}'),
-                conversions_subdir, exif_tags)
+        # Always create a small thumbnail
+        resize_tool(proc_state, True, 'thumb',
+                    name_builder.fill('{basename}.thumbnail{ext}'),
+                    conversions_subdir, exif_tags)
+
+        # Possibly create a medium
+        resize_tool(proc_state, False, 'medium',
+                    name_builder.fill('{basename}.medium{ext}'),
+                    conversions_subdir, exif_tags)
+
+        # Copy our queued local workbench to its final destination
+        proc_state.copy_original(name_builder.fill('{basename}{ext}'))
+
+        # Remove queued media file from storage and database
+        proc_state.delete_queue_file()
 
-    # Possibly create a medium
-    resize_tool(proc_state, False, 'medium',
-                name_builder.fill('{basename}.medium{ext}'),
-                conversions_subdir, exif_tags)
+        # Insert exif data into database
+        exif_all = clean_exif(exif_tags)
 
-    # Copy our queued local workbench to its final destination
-    proc_state.copy_original(name_builder.fill('{basename}{ext}'))
+        if len(exif_all):
+            entry.media_data_init(exif_all=exif_all)
 
-    # Remove queued media file from storage and database
-    proc_state.delete_queue_file()
+        if len(gps_data):
+            for key in list(gps_data.keys()):
+                gps_data['gps_' + key] = gps_data.pop(key)
+            entry.media_data_init(**gps_data)
 
-    # Insert exif data into database
-    exif_all = clean_exif(exif_tags)
 
-    if len(exif_all):
-        entry.media_data_init(exif_all=exif_all)
+def _reprocess_image(proc_state, reprocess_info, conversions_subdir):
+    reprocess_filename = proc_state.get_reprocess_filename()
+    name_builder = FilenameBuilder(reprocess_filename)
 
-    if len(gps_data):
-        for key in list(gps_data.keys()):
-            gps_data['gps_' + key] = gps_data.pop(key)
-        entry.media_data_init(**gps_data)
+    exif_tags = extract_exif(reprocess_filename)
+
+    if reprocess_info.get('max_width'):
+        max_width = reprocess_info['max_width']
+    else:
+        max_width = mgg.global_config \
+            ['media:' + reprocess_info['resize']]['max_width']
+
+    if reprocess_info.get('max_height'):
+        max_height = reprocess_info['max_height']
+    else:
+        max_height = mgg.global_config \
+            ['media:' + reprocess_info['resize']]['max_height']
+
+    new_size = (max_width, max_height)
+
+    resize_tool(proc_state, False, reprocess_info['resize'],
+                name_builder.fill('{basename}.thumbnail{ext}'),
+                conversions_subdir, exif_tags, new_size)
 
 
 if __name__ == '__main__':
index f3a85940ca74f454e49932f99f0bb95330c4f7a5..bbe9f364999b22b038b1165a39a65b7d297ac6d6 100644 (file)
@@ -87,6 +87,7 @@ class ProcessingState(object):
         self.entry = entry
         self.workbench = None
         self.queued_filename = None
+        self.reprocess_filename = None
 
     def set_workbench(self, wb):
         self.workbench = wb
@@ -128,6 +129,22 @@ class ProcessingState(object):
         mgg.queue_store.delete_dir(queued_filepath[:-1])  # rm dir
         self.entry.queued_media_file = []
 
+    def get_reprocess_filename(self):
+        """
+        Get the filename to use during reprocessing
+        """
+        # Currently only returns the original file, but eventually will return
+        # the highest quality file if the original doesn't exist
+        if self.reprocess_filename is not None:
+            return self.reprocess_filename
+
+        reprocess_filepath = self.entry.media_files['original'][2]
+        reprocess_filename = self.workbench.local_file(
+            mgg.public_store, reprocess_filepath,
+            'original')
+        self.reprocess_filename = reprocess_filename
+        return reprocess_filename
+
 
 def mark_entry_failed(entry_id, exc):
     """
index 9af192edb97f0f902dc15b253c1918dcae40cd40..c0dfb9b4540e2d2b74f6022ddb5c1d84bd700d96 100644 (file)
@@ -68,13 +68,15 @@ class ProcessMedia(task.Task):
     """
     Pass this entry off for processing.
     """
-    def run(self, media_id, feed_url):
+    def run(self, media_id, feed_url, reprocess_info=None):
         """
         Pass the media entry off to the appropriate processing function
         (for now just process_image...)
 
         :param feed_url: The feed URL that the PuSH server needs to be
             updated for.
+        :param reprocess: A dict containing all of the necessary reprocessing
+            info for the media_type.
         """
         entry = MediaEntry.query.get(media_id)
 
@@ -89,7 +91,7 @@ class ProcessMedia(task.Task):
             with mgg.workbench_manager.create() as workbench:
                 proc_state.set_workbench(workbench)
                 # run the processing code
-                entry.media_manager.processor(proc_state)
+                entry.media_manager.processor(proc_state, reprocess_info)
 
             # We set the state to processed and save the entry here so there's
             # no need to save at the end of the processing stage, probably ;)
index 7e85696bed4fc68d82304229ba2e421553987527..3619a3290aab267f0032fb0ad1c5cef52847a129 100644 (file)
@@ -76,17 +76,19 @@ def prepare_queue_task(app, entry, filename):
     return queue_file
 
 
-def run_process_media(entry, feed_url=None):
+def run_process_media(entry, feed_url=None, reprocess_info=None):
     """Process the media asynchronously
 
     :param entry: MediaEntry() instance to be processed.
     :param feed_url: A string indicating the feed_url that the PuSH servers
         should be notified of. This will be sth like: `request.urlgen(
             'mediagoblin.user_pages.atom_feed',qualified=True,
-            user=request.user.username)`"""
+            user=request.user.username)`
+    :param reprocess: A dict containing all of the necessary reprocessing
+        info for the given media_type"""
     try:
         process_media.apply_async(
-            [entry.id, feed_url], {},
+            [entry.id, feed_url, reprocess_info], {},
             task_id=entry.queued_task_id)
     except BaseException as exc:
         # The purpose of this section is because when running in "lazy"