def s3_create_default_thumbs_for(item): """Create copies of the default thumbs for the given item. This copies the default files (all named with an id of 'new') to use the given item's id. This means there could be lots of duplicate copies of the default thumbs, but at least we can always use the same url when rendering. :param item: A 2-tuple with a subdir name and an ID. If given a ORM mapped class with _thumb_dir and id attributes, the info can be extracted automatically. :type item: ``tuple`` or mapped class instance """ # fallback to normal thumb handling if no S3 engine is enabled. storage = get_s3_storage() if not storage: return None bucket = storage.connect_to_bucket() image_dir, item_id = normalize_thumb_item(item) for key in config['thumb_sizes'][image_dir].iterkeys(): src_file = os.path.join(config['cache.dir'], 'images', thumb_path((image_dir, 'new'), key)) dst_file = thumb_path(item, key) key = Key(bucket) key.key = dst_file key.set_metadata('is_default_thumb', '1') key.set_contents_from_filename(src_file, {'Content-Type': 'image/jpeg'}) key.set_acl('public-read') return True
def s3_create_thumbs_for(item, image_file, image_filename): """Creates thumbnails in all sizes for a given Media or Podcast object. Side effects: Closes the open file handle passed in as image_file. :param item: A 2-tuple with a subdir name and an ID. If given a ORM mapped class with _thumb_dir and id attributes, the info can be extracted automatically. :type item: ``tuple`` or mapped class instance :param image_file: An open file handle for the original image file. :type image_file: file :param image_filename: The original filename of the thumbnail image. :type image_filename: unicode """ # fallback to normal thumb handling if no S3 engine is enabled. storage = get_s3_storage() if not storage: return None bucket = storage.connect_to_bucket() image_dir, item_id = normalize_thumb_item(item) img = Image.open(image_file) # TODO: Allow other formats? for key, xy in config['thumb_sizes'][item._thumb_dir].iteritems(): path = thumb_path(item, key) thumb_img = resize_thumb(img, xy) if thumb_img.mode != "RGB": thumb_img = thumb_img.convert("RGB") tmpfile = TemporaryFile() thumb_img.save(tmpfile, 'JPEG') key = Key(bucket) key.key = path key.set_contents_from_file(tmpfile, {'Content-Type': 'image/jpeg'}) key.set_acl('public-read') # Backup the original image, ensuring there's no odd chars in the ext. # Thumbs from DailyMotion include an extra query string that needs to be # stripped off here. ext = os.path.splitext(image_filename)[1].lower() ext_match = _ext_filter.match(ext) if ext_match: backup_type = ext_match.group(1) backup_path = thumb_path(item, 'orig', ext=backup_type) image_file.seek(0) key = Key(bucket) key.key = backup_path key.set_contents_from_file(image_file, {'Content-Type': mimetypes.guess_type(backup_path)[0]}) key.set_acl('public-read') image_file.close() return True
def s3_has_default_thumbs(item): """Return True if the thumbs for the given item are the defaults. :param item: A 2-tuple with a subdir name and an ID. If given a ORM mapped class with _thumb_dir and id attributes, the info can be extracted automatically. :type item: ``tuple`` or mapped class instance """ # fallback to normal thumb handling if no S3 engine is enabled. storage = get_s3_storage() if not storage: return None bucket = storage.connect_to_bucket() image_dir, item_id = normalize_thumb_item(item) key = bucket.get_key(thumb_path(item, 's')) return key and key.get_metadata('is_default_thumb') == '1'
def test_add_vimeo_video(self): pylons.app_globals.settings['use_embed_thumbnails'] = 'true' media = save_media_obj( u'Fake Name', u'*****@*****.**', u'Python Code Swarm', u'A visualization of all activity in the Python repository.', u'', None, u'http://www.vimeo.com/1093745' ) # XXX: The following values are based on the values provided by the # remote site at the time this test was written. They may change # in future. assert media.duration == 282 thumbnail_path = thumb_path(media, 's', exists=True) assert thumbnail_path is not None img = open(thumbnail_path) s = sha1(img.read()).hexdigest() img.close() assert s == '1eb9442b7864841e0f48270de7e3e871050b3876'
def test_add_google_video(self): pylons.app_globals.settings['use_embed_thumbnails'] = 'true' media = save_media_obj( u'Fake Name', u'*****@*****.**', u'Pictures at an Exhibition', u'A nice, long, production of the orchestrated Pictures...', u'', None, u'http://video.google.com/videoplay?docid=8997593004077118819' ) # XXX: The following values are based on the values provided by the # remote site at the time this test was written. They may change # in future. assert media.duration == 1121 thumbnail_path = thumb_path(media, 's', exists=True) assert thumbnail_path is not None img = open(thumbnail_path) s = sha1(img.read()).hexdigest() img.close() assert s == 'f8e84e4a487c9ff6ea69ac696c199ae6ac222e38'
def test_add_youtube_video(self): pylons.app_globals.settings['use_embed_thumbnails'] = 'true' media = save_media_obj( u'Fake Name', u'*****@*****.**', u'Old Spice', u'Isiah Mustafa stars in...', u'', None, u'http://www.youtube.com/watch?v=uLTIowBF0kE' ) # XXX: The following values are based on the values provided by the # remote site at the time this test was written. They may change # in future. assert media.duration == 32 thumbnail_path = thumb_path(media, 's', exists=True) assert thumbnail_path is not None img = open(thumbnail_path) s = sha1(img.read()).hexdigest() img.close() assert s == 'f0a3f5991fa032077faf2d3c698a6cf3e9dcadc1'
def merge_stubs(self, orig_id, input_id, **kwargs): """Merge in a newly created media item. This is merges media that has just been created. It must have: 1. a non-default thumbnail, or 2. a file, or 3. a title, description, etc :param orig_id: Media ID to copy data to :type orig_id: ``int`` :param input_id: Media ID to source files, thumbs, etc from :type input_id: ``int`` :returns: JSON dict """ orig = fetch_row(Media, orig_id) input = fetch_row(Media, input_id) merged_files = [] # Merge in the file(s) from the input stub if input.slug.startswith('_stub_') and input.files: for file in input.files[:]: # XXX: The filename will still use the old ID file.media = orig merged_files.append(file) DBSession.delete(input) # The original is a file or thumb stub, copy in the new values elif orig.slug.startswith('_stub_') \ and not input.slug.startswith('_stub_'): DBSession.delete(input) DBSession.flush() orig.podcast = input.podcast orig.title = input.title orig.subtitle = input.subtitle orig.slug = input.slug orig.author = input.author orig.description = input.description orig.notes = input.notes orig.duration = input.duration orig.views = input.views orig.likes = input.likes orig.publish_on = input.publish_on orig.publish_until = input.publish_until orig.categories = input.categories orig.tags = input.tags orig.update_popularity() # Copy the input thumb over the default thumbnail elif input.slug.startswith('_stub_') \ and has_default_thumbs(orig) \ and not has_default_thumbs(input): for key, dst_path in thumb_paths(orig).iteritems(): src_path = thumb_path(input, key) # This will raise an OSError on Windows, but not *nix os.rename(src_path, dst_path) DBSession.delete(input) # Report an error else: return dict( success = False, message = u'No merge operation fits.', ) orig.update_status() status_form_xhtml = unicode(update_status_form.display( action=url_for(action='update_status', id=orig.id), media=orig)) file_xhtml = {} for file in merged_files: file_xhtml[file.id] = unicode(edit_file_form.display( action=url_for(action='edit_file', id=orig.id), file=file)) return dict( success = True, media_id = orig.id, title = orig.title, link = url_for(action='edit', id=orig.id), status_form = status_form_xhtml, file_forms = file_xhtml, )