def generate_alternate_formats(post: model.Post, content: bytes) \ -> List[Tuple[model.Post, List[model.Tag]]]: assert post assert content new_posts = [] if mime.is_animated_gif(content): tag_names = [tag.first_name for tag in post.tags] if config.config['convert']['gif']['to_mp4']: mp4_post, new_tags = create_post( images.Image(content).to_mp4(), tag_names, post.user) update_post_flags(mp4_post, ['loop']) update_post_safety(mp4_post, post.safety) update_post_source(mp4_post, post.source) new_posts += [(mp4_post, new_tags)] if config.config['convert']['gif']['to_webm']: webm_post, new_tags = create_post( images.Image(content).to_webm(), tag_names, post.user) update_post_flags(webm_post, ['loop']) update_post_safety(webm_post, post.safety) update_post_source(webm_post, post.source) new_posts += [(webm_post, new_tags)] db.session.flush() new_posts = [p for p in new_posts if p[0] is not None] new_relations = [p[0].post_id for p in new_posts] if len(new_relations) > 0: update_post_relations(post, new_relations) return new_posts
def test_update_post_thumbnail_with_broken_thumbnail( tmpdir, config_injector, read_asset, post_factory, is_existing): config_injector({ 'data_dir': str(tmpdir.mkdir('data')), 'thumbnails': { 'post_width': 300, 'post_height': 300, }, }) post = post_factory() db.session.add(post) if is_existing: db.session.flush() assert post.post_id else: assert not post.post_id generated_path = str(tmpdir) + '/data/generated-thumbnails/1.jpg' source_path = str(tmpdir) + '/data/posts/custom-thumbnails/1.dat' assert not os.path.exists(generated_path) assert not os.path.exists(source_path) posts.update_post_content(post, read_asset('png.png')) posts.update_post_thumbnail(post, read_asset('png-broken.png')) assert not os.path.exists(generated_path) assert not os.path.exists(source_path) db.session.flush() assert os.path.exists(generated_path) assert os.path.exists(source_path) with open(source_path, 'rb') as handle: assert handle.read() == read_asset('png-broken.png') with open(generated_path, 'rb') as handle: image = images.Image(handle.read()) assert image.width == 1 assert image.height == 1
def get_default_flags(content: bytes) -> List[str]: assert content ret = [] if mime.is_video(mime.get_mime_type(content)): ret.append(model.Post.FLAG_LOOP) if images.Image(content).check_for_sound(): ret.append(model.Post.FLAG_SOUND) return ret
def update_post_content(post: model.Post, content: Optional[bytes]) -> None: assert post if not content: raise InvalidPostContentError('Post content missing.') update_signature = False post.mime_type = mime.get_mime_type(content) if mime.is_flash(post.mime_type): post.type = model.Post.TYPE_FLASH elif mime.is_image(post.mime_type): update_signature = True if mime.is_animated_gif(content): post.type = model.Post.TYPE_ANIMATION else: post.type = model.Post.TYPE_IMAGE elif mime.is_video(post.mime_type): post.type = model.Post.TYPE_VIDEO else: raise InvalidPostContentError( 'Unhandled file type: %r' % post.mime_type) post.checksum = util.get_sha1(content) other_post = ( db.session .query(model.Post) .filter(model.Post.checksum == post.checksum) .filter(model.Post.post_id != post.post_id) .one_or_none()) if other_post \ and other_post.post_id \ and other_post.post_id != post.post_id: raise PostAlreadyUploadedError(other_post) if update_signature: purge_post_signature(post) post.signature = generate_post_signature(post, content) post.file_size = len(content) try: image = images.Image(content) post.canvas_width = image.width post.canvas_height = image.height except errors.ProcessingError: if not config.config['allow_broken_uploads']: raise InvalidPostContentError( 'Unable to process image metadata') else: post.canvas_width = None post.canvas_height = None if (post.canvas_width is not None and post.canvas_width <= 0) \ or (post.canvas_height is not None and post.canvas_height <= 0): if not config.config['allow_broken_uploads']: raise InvalidPostContentError( 'Invalid image dimensions returned during processing') else: post.canvas_width = None post.canvas_height = None setattr(post, '__content', content)
def test_sound(post: model.Post, content: bytes) -> None: assert post assert content if mime.is_video(mime.get_mime_type(content)): if images.Image(content).check_for_sound(): flags = [x for x in post.flags.split(',') if x] if model.Post.FLAG_SOUND not in flags: flags.append(model.Post.FLAG_SOUND) update_post_flags(post, flags)
def generate_post_thumbnail(post): if files.has(get_post_thumbnail_backup_path(post)): content = files.get(get_post_thumbnail_backup_path(post)) else: content = files.get(get_post_content_path(post)) try: image = images.Image(content) image.resize_fill( int(config.config['thumbnails']['post_width']), int(config.config['thumbnails']['post_height'])) files.save(get_post_thumbnail_path(post), image.to_jpeg()) except errors.ProcessingError: files.save(get_post_thumbnail_path(post), EMPTY_PIXEL)
def update_user_avatar(user, avatar_style, avatar_content): if avatar_style == 'gravatar': user.avatar_style = user.AVATAR_GRAVATAR elif avatar_style == 'manual': user.avatar_style = user.AVATAR_MANUAL if not avatar_content: raise InvalidAvatarError('Avatar content missing.') image = images.Image(avatar_content) image.resize_fill(int(config.config['thumbnails']['avatar_width']), int(config.config['thumbnails']['avatar_height'])) files.save('avatars/' + user.name.lower() + '.png', image.to_png()) else: raise InvalidAvatarError( 'Avatar style %r is invalid. Valid avatar styles: %r.' % (avatar_style, ['gravatar', 'manual']))
def generate_post_thumbnail(post: model.Post) -> None: assert post if files.has(get_post_thumbnail_backup_path(post)): content = files.get(get_post_thumbnail_backup_path(post)) else: content = files.get(get_post_content_path(post)) try: assert content image = images.Image(content) image.resize_fill( int(config.config["thumbnails"]["post_width"]), int(config.config["thumbnails"]["post_height"]), ) files.save(get_post_thumbnail_path(post), image.to_jpeg()) except errors.ProcessingError: files.save(get_post_thumbnail_path(post), EMPTY_PIXEL)
def update_post_content(post: model.Post, content: Optional[bytes]) -> None: assert post if not content: raise InvalidPostContentError('Post content missing.') post.mime_type = mime.get_mime_type(content) if mime.is_flash(post.mime_type): post.type = model.Post.TYPE_FLASH elif mime.is_image(post.mime_type): if mime.is_animated_gif(content): post.type = model.Post.TYPE_ANIMATION else: post.type = model.Post.TYPE_IMAGE elif mime.is_video(post.mime_type): post.type = model.Post.TYPE_VIDEO else: raise InvalidPostContentError('Unhandled file type: %r' % post.mime_type) post.checksum = util.get_sha1(content) other_post = db.session \ .query(model.Post) \ .filter(model.Post.checksum == post.checksum) \ .filter(model.Post.post_id != post.post_id) \ .one_or_none() if other_post \ and other_post.post_id \ and other_post.post_id != post.post_id: raise PostAlreadyUploadedError(other_post) post.file_size = len(content) try: image = images.Image(content) post.canvas_width = image.width post.canvas_height = image.height except errors.ProcessingError: post.canvas_width = None post.canvas_height = None if (post.canvas_width is not None and post.canvas_width <= 0) \ or (post.canvas_height is not None and post.canvas_height <= 0): post.canvas_width = None post.canvas_height = None setattr(post, '__content', content)
def test_update_post_thumbnail_with_broken_thumbnail( tmpdir, config_injector, read_asset, post_factory, is_existing ): config_injector( { "data_dir": str(tmpdir.mkdir("data")), "thumbnails": { "post_width": 300, "post_height": 300, }, "secret": "test", "allow_broken_uploads": False, } ) post = post_factory(id=1) db.session.add(post) if is_existing: db.session.flush() assert post.post_id generated_path = ( "{}/data/generated-thumbnails/".format(tmpdir) + "1_244c8840887984c4.jpg" ) source_path = ( "{}/data/posts/custom-thumbnails/".format(tmpdir) + "1_244c8840887984c4.dat" ) assert not os.path.exists(generated_path) assert not os.path.exists(source_path) posts.update_post_content(post, read_asset("png.png")) posts.update_post_thumbnail(post, read_asset("png-broken.png")) assert not os.path.exists(generated_path) assert not os.path.exists(source_path) db.session.flush() assert os.path.exists(generated_path) assert os.path.exists(source_path) with open(source_path, "rb") as handle: assert handle.read() == read_asset("png-broken.png") with open(generated_path, "rb") as handle: image = images.Image(handle.read()) assert image.width == 1 assert image.height == 1
def update_user_avatar(user: model.User, avatar_style: str, avatar_content: Optional[bytes] = None) -> None: assert user if avatar_style == 'gravatar': user.avatar_style = user.AVATAR_GRAVATAR elif avatar_style == 'manual': user.avatar_style = user.AVATAR_MANUAL avatar_path = 'avatars/' + user.name.lower() + '.png' if not avatar_content: if files.has(avatar_path): return raise InvalidAvatarError('아바타 컨텐츠가 누락되었습니다.') image = images.Image(avatar_content) image.resize_fill(int(config.config['thumbnails']['avatar_width']), int(config.config['thumbnails']['avatar_height'])) files.save(avatar_path, image.to_png()) else: raise InvalidAvatarError('아바타 스타일 %r 은(는) 잘못된 값입니다. 올바른 스타일 값: %r.' % (avatar_style, ['gravatar', 'manual']))
def generate_post_thumbnail(post: model.Post) -> None: assert post if files.has(get_post_thumbnail_backup_path(post)): content = files.get(get_post_thumbnail_backup_path(post)) else: content = files.get(get_post_content_path(post)) try: assert content if post.type == model.Post.TYPE_IMAGE: media = images.Image(content) elif post.type == model.Post.TYPE_VIDEO: media = images.Video(content) thumb = media.to_thumbnail( int(config.config["thumbnails"]["post_width"]), int(config.config["thumbnails"]["post_height"]), ) files.save(get_post_thumbnail_path(post), thumb) except errors.ProcessingError: files.save(get_post_thumbnail_path(post), EMPTY_PIXEL)
def update_user_avatar(user: model.User, avatar_style: str, avatar_content: Optional[bytes] = None) -> None: assert user if avatar_style == "gravatar": user.avatar_style = user.AVATAR_GRAVATAR elif avatar_style == "manual": user.avatar_style = user.AVATAR_MANUAL avatar_path = "avatars/" + user.name.lower() + ".png" if not avatar_content: if files.has(avatar_path): return raise InvalidAvatarError("Avatar content missing.") image = images.Image(avatar_content) image.resize_fill( int(config.config["thumbnails"]["avatar_width"]), int(config.config["thumbnails"]["avatar_height"]), ) files.save(avatar_path, image.to_png()) else: raise InvalidAvatarError( "Avatar style %r is invalid. Valid avatar styles: %r." % (avatar_style, ["gravatar", "manual"]))
def test_update_post_thumbnail_broken_thumbnail(tmpdir, config_injector, read_asset, post_factory): config_injector({ 'data_dir': str(tmpdir.mkdir('data')), 'thumbnails': { 'post_width': 300, 'post_height': 300, }, }) post = post_factory(id=1) db.session.add(post) db.session.flush() posts.update_post_content(post, read_asset('png.png')) posts.update_post_thumbnail(post, read_asset('png-broken.png')) assert os.path.exists(str(tmpdir) + '/data/posts/custom-thumbnails/1.dat') assert os.path.exists(str(tmpdir) + '/data/generated-thumbnails/1.jpg') with open(str(tmpdir) + '/data/posts/custom-thumbnails/1.dat', 'rb') as handle: assert handle.read() == read_asset('png-broken.png') with open(str(tmpdir) + '/data/generated-thumbnails/1.jpg', 'rb') as handle: image = images.Image(handle.read()) assert image.width == 1 assert image.height == 1
def update_post_content(post, content): if not content: raise InvalidPostContentError('Post content missing.') post.mime_type = mime.get_mime_type(content) if mime.is_flash(post.mime_type): post.type = db.Post.TYPE_FLASH elif mime.is_image(post.mime_type): if mime.is_animated_gif(content): post.type = db.Post.TYPE_ANIMATION else: post.type = db.Post.TYPE_IMAGE elif mime.is_video(post.mime_type): post.type = db.Post.TYPE_VIDEO else: raise InvalidPostContentError('Unhandled file type: %r' % post.mime_type) post.checksum = util.get_md5(content) other_post = db.session \ .query(db.Post) \ .filter(db.Post.checksum == post.checksum) \ .filter(db.Post.post_id != post.post_id) \ .one_or_none() if other_post: raise PostAlreadyUploadedError( 'Post already uploaded (%d)' % other_post.post_id) post.file_size = len(content) try: image = images.Image(content) post.canvas_width = image.width post.canvas_height = image.height except errors.ProcessingError: post.canvas_width = None post.canvas_height = None files.save(get_post_content_path(post), content) update_post_thumbnail(post, content=None, delete=False)
def update_post_content(post: model.Post, content: Optional[bytes]) -> None: assert post if not content: raise InvalidPostContentError("Post content missing.") update_signature = False post.mime_type = mime.get_mime_type(content) if mime.is_flash(post.mime_type): raise InvalidPostContentError( "Flash animations are deprecated in this build and are slowly " + "being phased out.") elif mime.is_image(post.mime_type): update_signature = True if mime.is_animated_gif(content): post.type = model.Post.TYPE_ANIMATION else: post.type = model.Post.TYPE_IMAGE elif mime.is_video(post.mime_type): post.type = model.Post.TYPE_VIDEO else: raise InvalidPostContentError(f"Unhandled file type: {post.mime_type}") post.checksum = util.get_sha1(content) post.checksum_md5 = util.get_md5(content) other_post = (db.session.query( model.Post).filter(model.Post.checksum == post.checksum).filter( model.Post.post_id != post.post_id).one_or_none()) if (other_post and other_post.post_id and other_post.post_id != post.post_id): raise PostAlreadyUploadedError(other_post) if update_signature: purge_post_signature(post) post.signature = generate_post_signature(post, content) post.file_size = len(content) post.canvas_width = None post.canvas_height = None post.date_taken = None post.camera = None try: if post.type == model.Post.TYPE_IMAGE: media = images.Image(content) elif post.type in (model.Post.TYPE_ANIMATION, model.Post.TYPE_VIDEO): media = images.Video(content) except Exception as ex: logger.exception(ex) if not config.config["allow_broken_uploads"]: raise InvalidPostContentError("Unable to process image metadata") else: if not media.width or not media.height: if not config.config["allow_broken_uploads"]: raise InvalidPostContentError( "Invalid image dimensions returned during processing") post.canvas_width = media.width post.canvas_height = media.height post.date_taken = media.date_taken post.camera = media.camera setattr(post, "__content", content)