diff --git a/.travis.yml b/.travis.yml index f642eee..3795238 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ python: before_install: # Dependencies to build PIL - sudo apt-get update -qq - - sudo apt-get install -qq libfreetype6-dev libjpeg8-dev zlib1g-dev + - sudo apt-get install -qq libfreetype6-dev libjpeg8-dev zlib1g-dev ffmpeg install: - pip install pytest --use-mirrors - pip install . --use-mirrors diff --git a/sigal/gallery.py b/sigal/gallery.py index ce749f6..4e7f429 100644 --- a/sigal/gallery.py +++ b/sigal/gallery.py @@ -1,6 +1,7 @@ # -*- coding:utf-8 -*- # Copyright (c) 2009-2013 - Simon Conseil +# Copyright (c) 2013 - Christophe-Marie Duquesne # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to @@ -34,12 +35,16 @@ from clint.textui import progress, colored from os.path import join from PIL import Image as PILImage -from .image import generate_image, generate_thumbnail +import sigal.image +import sigal.video from .settings import get_thumb from .writer import Writer DESCRIPTION_FILE = "index.md" +class FileExtensionError(Exception): + """Raised if we made an error when handling file extensions""" + pass class PathsDb(object): """Container for all the information on the directory structure. @@ -49,8 +54,9 @@ class PathsDb(object): """ - def __init__(self, path, ext_list): - self.ext_list = ext_list + def __init__(self, path, img_ext_list, vid_ext_list): + self.img_ext_list = img_ext_list + self.vid_ext_list = vid_ext_list self.logger = logging.getLogger(__name__) # basepath must to be a unicode string so that os.walk will return @@ -92,23 +98,23 @@ class PathsDb(object): self.db['paths_list'].append(relpath) self.db[relpath] = { - 'img': [f for f in filenames - if os.path.splitext(f)[1] in self.ext_list], + 'medias': [f for f in filenames if os.path.splitext(f)[1] + in (self.img_ext_list + self.vid_ext_list)], 'subdir': dirnames } self.db[relpath].update(get_metadata(path)) - path_im = [path for path in self.db['paths_list'] - if self.db[path]['img'] and path != '.'] - path_noim = [path for path in self.db['paths_list'] - if not self.db[path]['img'] and path != '.'] + path_media = [path for path in self.db['paths_list'] + if self.db[path]['medias'] and path != '.'] + path_nomedia = [path for path in self.db['paths_list'] + if not self.db[path]['medias'] and path !='.'] # dir with images: check the thumbnail, and find it if necessary - for path in path_im: + for path in path_media: self.check_thumbnail(path) # dir without images, start with the deepest ones - for path in reversed(sorted(path_noim, key=lambda x: x.count('/'))): + for path in reversed(sorted(path_nomedia, key=lambda x: x.count('/'))): for subdir in self.get_subdirs(path): # use the thumbnail of their sub-directories if self.db[subdir].get('thumbnail', ''): @@ -136,17 +142,20 @@ class PathsDb(object): return # find and return the first landscape image - for f in self.db[path]['img']: - im = PILImage.open(join(self.basepath, path, f)) - if im.size[0] > im.size[1]: - self.db[path]['thumbnail'] = f - return + for f in self.db[path]['medias']: + base, ext = os.path.splitext(f) + if ext in self.img_ext_list: + im = PILImage.open(join(self.basepath, path, f)) + if im.size[0] > im.size[1]: + self.db[path]['thumbnail'] = f + return - # else simply return the 1st image - if self.db[path]['img']: - self.db[path]['thumbnail'] = self.db[path]['img'][0] - else: - self.db[path]['thumbnail'] = '' + # else simply return the 1st media file + if self.db[path]['medias']: + self.db[path]['thumbnail'] = self.db[path]['medias'][0] + return + + self.db[path]['thumbnail'] = '' class Gallery(object): @@ -162,7 +171,8 @@ class Gallery(object): theme=theme) self.paths = PathsDb(self.settings['source'], - self.settings['ext_list']) + self.settings['img_ext_list'], + self.settings['vid_ext_list']) self.paths.build() self.db = self.paths.db @@ -179,22 +189,22 @@ class Gallery(object): # loop on directories in reversed order, to process subdirectories # before their parent for path in reversed(self.db['paths_list']): - imglist = [os.path.normpath(join(self.settings['source'], path, f)) - for f in self.db[path]['img']] + media_files = [os.path.normpath(join(self.settings['source'], path, f)) + for f in self.db[path]['medias']] # output dir for the current path - img_out = os.path.normpath(join(self.settings['destination'], + outpath = os.path.normpath(join(self.settings['destination'], path)) - check_or_create_dir(img_out) + check_or_create_dir(outpath) - if len(imglist) != 0: - self.process_dir(imglist, img_out, path, - label_width=label_width) + if len(media_files) != 0: + self.process_dir(media_files, outpath, path, + label_width=label_width) if self.settings['write_html']: self.writer.write(self.db, path) - def process_dir(self, imglist, outpath, dirname, label_width=20): + def process_dir(self, media_files, outpath, dirname, label_width=20): """Process a list of images in a directory.""" # Create thumbnails directory and optionally the one for original img @@ -206,25 +216,36 @@ class Gallery(object): # use progressbar if level is > INFO if self.logger.getEffectiveLevel() > 20: label = colored.green(dirname.ljust(label_width)) - img_iterator = progress.bar(imglist, label=label) + media_iterator = progress.bar(media_files, label=label) else: - img_iterator = iter(imglist) + media_iterator = iter(media_files) self.logger.info("") - self.logger.info(":: Processing '%s' [%i images]", - colored.green(dirname), len(imglist)) + self.logger.info(":: Processing '%s' [%i img/vid]", + colored.green(dirname), len(media_files)) self.logger.info("") try: # loop on images - for f in img_iterator: + for f in media_iterator: filename = os.path.split(f)[1] - outname = join(outpath, filename) + base, ext = os.path.splitext(filename) + if ext in self.settings['img_ext_list']: + outname = join(outpath, filename) + elif ext in self.settings['vid_ext_list']: + outname = join(outpath, base + '.webm') + else: + raise FileExtensionError if os.path.isfile(outname) and not self.force: self.logger.info("%s exists - skipping", filename) else: self.logger.info(filename) - process_image(f, outpath, self.settings) + if ext in self.settings['img_ext_list']: + process_image(f, outpath, self.settings) + elif ext in self.settings['vid_ext_list']: + process_video(f, outpath, self.settings) + else: + raise FileExtensionError except KeyboardInterrupt: sys.exit('Interrupted') @@ -247,14 +268,35 @@ def process_image(filepath, outpath, settings): if settings['keep_orig']: shutil.copy(filepath, join(outpath, settings['orig_dir'], filename)) - generate_image(filepath, outname, settings['img_size'], None, - options=options, copyright_text=settings['copyright'], - method=settings['img_processor']) + sigal.image.generate_image(filepath, outname, settings['img_size'], + None, options=options, copyright_text=settings['copyright'], + method=settings['img_processor']) if settings['make_thumbs']: thumb_name = join(outpath, get_thumb(settings, filename)) - generate_thumbnail(outname, thumb_name, settings['thumb_size'], None, - fit=settings['thumb_fit'], options=options) + sigal.image.generate_thumbnail(outname, thumb_name, + settings['thumb_size'], None, fit=settings['thumb_fit'], + options=options) + +def process_video(filepath, outpath, settings): + """Process one image: resize, create thumbnail.""" + + filename = os.path.split(filepath)[1] + base, ext = os.path.splitext(filename) + outname = join(outpath, base + '.webm') + + if settings['keep_orig']: + shutil.copy(filepath, join(outpath, settings['orig_dir'], filename)) + + # TODO: Add specific video size settings + sigal.video.generate_video(filepath, outname, settings['img_size'], + settings['webm_options']) + + if settings['make_thumbs']: + thumb_name = join(outpath, get_thumb(settings, filename)) + sigal.video.generate_thumbnail(outname, thumb_name, + settings['thumb_size'], None, fit=settings['thumb_fit'], + options=settings['jpg_options']) def get_metadata(path): diff --git a/sigal/settings.py b/sigal/settings.py index 583f15c..715f011 100644 --- a/sigal/settings.py +++ b/sigal/settings.py @@ -1,6 +1,7 @@ # -*- coding:utf-8 -*- # Copyright (c) 2009-2013 - Simon Conseil +# Copyright (c) 2013 - Christophe-Marie Duquesne # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to @@ -37,8 +38,10 @@ _DEFAULT_CONFIG = { 'keep_orig': False, 'orig_dir': 'original', 'jpg_options': {'quality': 85, 'optimize': True, 'progressive': True}, + 'webm_options': {'crf': '10', 'bitrate': '1.6M', 'qmin': '4', 'qmax': '63'}, 'copyright': '', - 'ext_list': ['.jpg', '.jpeg', '.JPG', '.JPEG', '.png'], + 'img_ext_list': ['.jpg', '.jpeg', '.JPG', '.JPEG', '.png'], + 'vid_ext_list': ['.MOV', '.mov', '.avi', '.mp4', '.webm', '.ogv'], 'theme': 'colorbox', 'write_html': True, 'index_in_url': False, @@ -48,10 +51,23 @@ _DEFAULT_CONFIG = { def get_thumb(settings, filename): - """Return the path to the thumb.""" + """Return the path to the thumb. + + examples: + >>> get_thumb(default_settings, "bar/foo.jpg") + "bar/thumbnails/foo.jpg" + >>> get_thumb(default_settings, "bar/foo.png") + "bar/thumbnails/foo.png" + + for videos, it returns a jpg file: + >>> get_thumb(default_settings, "bar/foo.webm") + "bar/thumbnails/foo.jpg" + """ path, filen = os.path.split(filename) name, ext = os.path.splitext(filen) + if ext in settings['vid_ext_list']: + ext = '.jpg' return os.path.join(path, settings['thumb_dir'], settings['thumb_prefix'] + name + settings['thumb_suffix'] + ext) diff --git a/sigal/templates/sigal.conf.py b/sigal/templates/sigal.conf.py index 1c6b50d..841bd2c 100644 --- a/sigal/templates/sigal.conf.py +++ b/sigal/templates/sigal.conf.py @@ -16,7 +16,7 @@ source = 'pictures' # - colorbox (default), galleria, or the path to a custom theme directory theme = 'galleria' -# Size of resized image +# Size of resized image (default: (640, 480)) img_size = (800, 600) # Pilkit processor used to resize the image @@ -55,6 +55,16 @@ thumb_size = (280, 210) # 'optimize': True, # 'progressive': True} +# Webm options +# Options used in ffmpeg to encode the webm video. You may want to read +# http://ffmpeg.org/trac/ffmpeg/wiki/vpxEncodingGuide +# Be aware of the fact these options need to be passed as strings. +# webm_options = {'crf': '10', +# 'bitrate': '1.6M', +# 'qmin': '4', +# 'qmax': '63'} + + # Write HTML files. If False, sigal will only process the images. # write_html = True diff --git a/sigal/themes/colorbox/templates/index.html b/sigal/themes/colorbox/templates/index.html index 28bd825..5a58ea6 100644 --- a/sigal/themes/colorbox/templates/index.html +++ b/sigal/themes/colorbox/templates/index.html @@ -12,7 +12,7 @@
@@ -67,17 +67,38 @@ {% endif %} - {% if images %} + {% if medias %} {% endif %} @@ -90,7 +111,7 @@ - {% if images %} + {% if medias %} @@ -109,6 +130,9 @@ title += " (full size)".link(this.getAttribute("data-big")); } return title; + }, + inline: function() { + return this.hasAttribute("inline"); } }); diff --git a/sigal/themes/galleria/static/img/empty.png b/sigal/themes/galleria/static/img/empty.png new file mode 100644 index 0000000..9fc626c Binary files /dev/null and b/sigal/themes/galleria/static/img/empty.png differ diff --git a/sigal/themes/galleria/templates/index.html b/sigal/themes/galleria/templates/index.html index 532a646..80ba88c 100644 --- a/sigal/themes/galleria/templates/index.html +++ b/sigal/themes/galleria/templates/index.html @@ -13,7 +13,7 @@ @@ -57,14 +57,28 @@ {% endif %} - {% if images %} + {% if medias %} {% endif %} @@ -82,7 +96,7 @@ - {% if images %} + {% if medias %} diff --git a/sigal/video.py b/sigal/video.py new file mode 100644 index 0000000..a806d11 --- /dev/null +++ b/sigal/video.py @@ -0,0 +1,97 @@ +# -*- coding:utf-8 -*- + +# Copyright (c) 2013 - Christophe-Marie Duquesne + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +from __future__ import with_statement +import subprocess +import os +import re +import shutil +import sigal.image + +def vid_size(source): + """Returns the dimensions of the video""" + pattern = re.compile(r'Stream.*Video.* ([0-9]+)x([0-9]+)') + p = subprocess.Popen(['ffmpeg', '-i', source], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + match = pattern.search(stderr) + if match: + x, y = int(match.groups()[0]), int(match.groups()[1]) + else: + x = y = 0 + return (x, y) + + +def generate_video(source, outname, size, options={}): + """Video processor + + :param source: path to an image + :param outname: name of the generated video + :param options: array of options passed to ffmpeg + + """ + # Don't transcode if source is in the required format and + # has fitting datedimensions, copy instead. + w_src, h_src = vid_size(source) + w_dst, h_dst = size + base, src_ext = os.path.splitext(source) + base, dst_ext = os.path.splitext(outname) + if dst_ext == src_ext and w_src <= w_dst and h_src <= w_dst: + shutil.copy(source, outname) + return + + # http://stackoverflow.com/questions/8218363/maintaining-ffmpeg-aspect-ratio + # + I made a drawing on paper to figure this out + if h_dst * w_src < h_src * w_dst: + # biggest fitting dimension is height + resize_opt = ['-vf', "scale=trunc(oh*a/2)*2:%i" % h_dst] + else: + # biggest fitting dimension is width + resize_opt = ['-vf', "scale=%i:trunc(ow/a/2)*2" % w_dst] + + # do not resize if input dimensions are smaller than output dimensions + if w_src <= w_dst and h_src <= h_dst: + resize_opt = [] + + # Encoding options improved, thanks to + # http://ffmpeg.org/trac/ffmpeg/wiki/vpxEncodingGuide + with open("/dev/null") as devnull: + subprocess.call(['ffmpeg', '-i', source, '-y', + '-crf', options.get('crf', '10'), + '-b:v', options.get('bitrate', '1.6M'), + '-qmin', options.get('qmin', '4'), + '-qmax', options.get('qmax', '63')] + + resize_opt + [outname], + stderr=devnull) + +def generate_thumbnail(source, outname, box, format, fit=True, options=None): + "Create a thumbnail image" + # 1) dump an image of the video + tmpfile = outname + ".tmp.jpg" + with open("/dev/null") as devnull: + subprocess.call(['ffmpeg', '-i', source, '-an', '-r', '1', + '-vframes', '1', '-y', tmpfile], stderr=devnull) + # 2) use the generate_thumbnail function from sigal.image + sigal.image.generate_thumbnail(tmpfile, outname, box, format, fit, options) + # 3) remove the image + os.unlink(tmpfile) diff --git a/sigal/writer.py b/sigal/writer.py index 60d09ed..ec6d580 100644 --- a/sigal/writer.py +++ b/sigal/writer.py @@ -1,6 +1,7 @@ # -*- coding:utf-8 -*- # Copyright (c) 2009-2013 - Simon Conseil +# Copyright (c) 2013 - Christophe-Marie Duquesne # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to @@ -33,7 +34,8 @@ from distutils.dir_util import copy_tree from jinja2 import Environment, FileSystemLoader, ChoiceLoader, PrefixLoader from jinja2.exceptions import TemplateNotFound -from .image import generate_thumbnail +import sigal.image +import sigal.video from .settings import get_thumb, get_orig from .pkgmeta import __url__ as sigal_link @@ -89,7 +91,7 @@ class Writer(object): self.ctx = { 'sigal_link': sigal_link, 'theme': {'name': os.path.basename(self.theme)}, - 'images': [], + 'medias': [], 'albums': [], 'breadcumb': '' } @@ -134,12 +136,19 @@ class Writer(object): if relpath != '.': ctx['breadcumb'] = self.get_breadcumb(paths, relpath) - for i in paths[relpath]['img']: - img_ctx = {'file': i, - 'thumb': get_thumb(self.settings, i)} + for i in paths[relpath]['medias']: + media_ctx = {} + base, ext = os.path.splitext(i) + if ext in self.settings['img_ext_list']: + media_ctx['type'] = 'img' + media_ctx['file'] = i + else: + media_ctx['type'] = 'vid' + media_ctx['file'] = base + '.webm' + media_ctx['thumb'] = get_thumb(self.settings, i) if self.settings['keep_orig']: - img_ctx['big'] = get_orig(self.settings, i) - ctx['images'].append(img_ctx) + media_ctx['big'] = get_orig(self.settings, i) + ctx['medias'].append(media_ctx) for d in paths[relpath]['subdir']: dpath = os.path.normpath(os.path.join(relpath, d)) @@ -152,9 +161,15 @@ class Writer(object): # settings['make_thumbs'] is False) if not os.path.exists(thumb_path): source = os.path.join(self.output_dir, dpath, alb_thumb) - generate_thumbnail( - source, thumb_path, self.settings['thumb_size'], None, - fit=self.settings['thumb_fit']) + base, ext = os.path.splitext(source) + if ext in self.settings['img_ext_list']: + sigal.image.generate_thumbnail( + source, thumb_path, self.settings['thumb_size'], + None, fit=self.settings['thumb_fit']) + else: + sigal.video.generate_thumbnail( + source, thumb_path, self.settings['thumb_size'], + None, fit=self.settings['thumb_fit']) ctx['albums'].append({ 'url': d + '/' + self.url_ext, diff --git a/tests/sample/pictures/video/stallman-software-freedom-day-low.ogv b/tests/sample/pictures/video/stallman-software-freedom-day-low.ogv new file mode 100644 index 0000000..0a88cb4 Binary files /dev/null and b/tests/sample/pictures/video/stallman-software-freedom-day-low.ogv differ diff --git a/tests/test_gallery.py b/tests/test_gallery.py index 039c5d6..7cd0bbe 100644 --- a/tests/test_gallery.py +++ b/tests/test_gallery.py @@ -13,30 +13,35 @@ REF = { 'dir1': { 'title': 'An example gallery', 'thumbnail': 'test1/11.jpg', - 'img': '', + 'medias': [], }, 'dir1/test1': { 'title': 'An example sub-category', 'thumbnail': '11.jpg', - 'img': ['11.jpg', 'archlinux-kiss-1024x640.png'], + 'medias': ['11.jpg', 'archlinux-kiss-1024x640.png'], }, 'dir1/test2': { 'title': 'Test2', 'thumbnail': '21.jpg', - 'img': ['21.jpg', '22.jpg'], + 'medias': ['21.jpg', '22.jpg'], }, 'dir2': { 'title': 'Another example gallery', 'thumbnail': 'm57_the_ring_nebula-587px.jpg', - 'img': ['exo20101028-b-full.jpg', + 'medias': ['exo20101028-b-full.jpg', 'm57_the_ring_nebula-587px.jpg', 'Hubble ultra deep field.jpg', - 'Hubble Interacting Galaxy NGC 5257.jpg'] + 'Hubble Interacting Galaxy NGC 5257.jpg'], }, u'accentué': { 'title': u'Accentué', 'thumbnail': u'hélicoïde.jpg', - 'img': [u'hélicoïde.jpg', 'superdupont_source_wikipedia_en.jpg'] + 'medias': [u'hélicoïde.jpg', 'superdupont_source_wikipedia_en.jpg'], + }, + 'video': { + 'title': 'Video', + 'thumbnail': 'stallman-software-freedom-day-low.ogv', + 'medias': ['stallman-software-freedom-day-low.ogv'] } } @@ -47,7 +52,8 @@ def paths(): default_conf = os.path.join(SAMPLE_DIR, 'sigal.conf.py') settings = read_settings(default_conf) - return PathsDb(os.path.join(SAMPLE_DIR, 'pictures'), settings['ext_list']) + return PathsDb(os.path.join(SAMPLE_DIR, 'pictures'), + settings['img_ext_list'], settings['vid_ext_list']) @pytest.fixture(scope='module') @@ -58,14 +64,15 @@ def db(paths): def test_filelist(db): assert set(db.keys()) == set(['paths_list', 'skipped_dir', '.', - 'dir1', 'dir2', 'dir1/test1', 'dir1/test2', u'accentué']) + 'dir1', 'dir2', 'dir1/test1', 'dir1/test2', u'accentué', 'video']) assert set(db['paths_list']) == set(['.', 'dir1', 'dir1/test1', - 'dir1/test2', 'dir2', u'accentué']) + 'dir1/test2', 'dir2', u'accentué', 'video']) assert set(db['skipped_dir']) == set(['empty', 'dir1/empty']) - assert db['.']['img'] == [] - assert set(db['.']['subdir']) == set([u'accentué', 'dir1', 'dir2']) + assert db['.']['medias'] == [] + assert set(db['.']['subdir']) == set([u'accentué', 'dir1', 'dir2', + 'video']) def test_title(db): @@ -78,15 +85,16 @@ def test_thumbnail(db): assert db[p]['thumbnail'] == REF[p]['thumbnail'] -def test_imglist(db): +def test_medialist(db): for p in REF.keys(): - assert set(db[p]['img']) == set(REF[p]['img']) + assert set(db[p]['medias']) == set(REF[p]['medias']) def test_get_subdir(paths): assert set(paths.get_subdirs('dir1')) == set(['dir1/test1', 'dir1/test2']) assert set(paths.get_subdirs('.')) == set(['dir1', 'dir2', 'dir1/test1', - 'dir1/test2', u'accentué']) + 'dir1/test2', u'accentué', + 'video']) def test_get_metadata(): diff --git a/tests/test_video.py b/tests/test_video.py new file mode 100644 index 0000000..065059c --- /dev/null +++ b/tests/test_video.py @@ -0,0 +1,56 @@ +# -*- coding:utf-8 -*- + +from __future__ import division + +import os +import filecmp +import pytest + +from sigal import init_logging +from sigal.video import vid_size, generate_video + +CURRENT_DIR = os.path.dirname(__file__) +TEST_VIDEO = 'stallman-software-freedom-day-low.ogv' +SRCFILE = os.path.join(CURRENT_DIR, 'sample', 'pictures', 'video', TEST_VIDEO) + + + +def test_generate_video_fit_height(tmpdir): + """largest fitting dimension is height""" + + base, ext = os.path.splitext(TEST_VIDEO) + dstfile = str(tmpdir.join(base + '.webm')) + generate_video(SRCFILE, dstfile, (50, 100)) + + size_src = vid_size(SRCFILE) + size_dst = vid_size(dstfile) + + assert size_dst[0] == 50 + # less than 2% error on ratio + assert abs(size_dst[0]/size_dst[1] - size_src[0]/size_src[1]) < 2e-2 + +def test_generate_video_fit_width(tmpdir): + """largest fitting dimension is width""" + + base, ext = os.path.splitext(TEST_VIDEO) + dstfile = str(tmpdir.join(base + '.webm')) + generate_video(SRCFILE, dstfile, (100, 50)) + + size_src = vid_size(SRCFILE) + size_dst = vid_size(dstfile) + + assert size_dst[1] == 50 + # less than 2% error on ratio + assert abs(size_dst[0]/size_dst[1] - size_src[0]/size_src[1]) < 2e-2 + +def test_generate_video_dont_enlarge(tmpdir): + """video dimensions should not be enlarged""" + + base, ext = os.path.splitext(TEST_VIDEO) + dstfile = str(tmpdir.join(base + '.webm')) + generate_video(SRCFILE, dstfile, (1000, 1000)) + + size_src = vid_size(SRCFILE) + size_dst = vid_size(dstfile) + + assert size_src == size_dst