Merge pull request #18 from chmduquesne/master

Adding support for videos
This commit is contained in:
Simon Conseil
2013-07-21 06:07:51 -07:00
12 changed files with 373 additions and 91 deletions

View File

@@ -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

View File

@@ -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):

View File

@@ -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)

View File

@@ -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

View File

@@ -12,7 +12,7 @@
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans">
<link rel="stylesheet" href="{{ theme.url }}/css/style.min.css">
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
@@ -67,17 +67,38 @@
</div>
{% endif %}
{% if images %}
{% if medias %}
<div id="gallery" class="row">
{% for image in images %}
<div class="four columns thumbnail
{% if loop.index % 3 == 1 %}alpha{% endif%}
{% if loop.index % 3 == 0 %}omega{% endif%}">
<a href="{{ image.file }}" class="gallery" title="{{ image.file }}"
{% if image.big %} data-big="{{ image.big }}"{% endif %}>
<img src="{{ image.thumb }}" alt="{{ image.file }}"
title="{{ image.file }}" /></a>
</div>
{% for media in medias %}
{% if media.type == "img" %}
<div class="four columns thumbnail
{% if loop.index % 3 == 1 %}alpha{% endif%}
{% if loop.index % 3 == 0 %}omega{% endif%}">
<a href="{{ media.file }}" class="gallery" title="{{ media.file }}"
{% if media.big %} data-big="{{ media.big }}"{% endif %}>
<img src="{{ media.thumb }}" alt="{{ media.file }}"
title="{{ media.file }}" /></a>
</div>
{% endif %}
{% if media.type == "vid" %}
<div class="four columns thumbnail
{% if loop.index % 3 == 1 %}alpha{% endif%}
{% if loop.index % 3 == 0 %}omega{% endif%}">
<a href="#{{ media.file|replace('.', '') }}" class="gallery"
inline='yes' title="{{ media.file }}"
{% if media.big %} data-big="{{ media.big }}"{% endif %}>
<img src="{{ media.thumb }}" alt="{{ media.file }}"
title="{{ media.file }}" /></a>
</div>
<!-- This contains the hidden content for the video -->
<div style='display:none'>
<div id="{{ media.file|replace('.', '') }}">
<video controls>
<source src='{{ media.file }}' type='video/webm' />
</video>
</div>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
@@ -90,7 +111,7 @@
</div>
</div>
{% if images %}
{% if medias %}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="{{ theme.url }}/js/jquery-1.10.2.min.js"%3E%3C/script%3E'))</script>
<script src="{{ theme.url }}/js/jquery.colorbox.min.js"></script>
@@ -109,6 +130,9 @@
title += " (full size)".link(this.getAttribute("data-big"));
}
return title;
},
inline: function() {
return this.hasAttribute("inline");
}
});
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

View File

@@ -13,7 +13,7 @@
<link rel="stylesheet" href="{{ theme.url }}/css/style.min.css">
<link rel="stylesheet" href="{{ theme.url }}/css/galleria.classic.css">
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
@@ -57,14 +57,28 @@
</div>
{% endif %}
{% if images %}
{% if medias %}
<div id="gallery">
{% for image in images %}
<a href="{{ image.file }}">
<img src="{{ image.thumb }}" alt="{{ image.file }}"
data-title="{{ image.file }}"
{%- if image.big %} data-description="<a href='{{ image.big }}'>Full size</a>"{% endif %}/>
</a>
{% for media in medias %}
{% if media.type == "img" %}
<a href="{{ media.file }}">
<img src="{{ media.thumb }}" alt="{{ media.file }}"
data-title="{{ media.file }}"
{%- if media.big %} data-description="<a href='{{ media.big }}'>Full size</a>"{% endif %}/>
</a>
{% endif %}
{% if media.type == "vid" %}
<a href="{{ theme.url }}/img/empty.png">
<img src="{{ media.thumb }}" alt="{{ media.file }}"
{# TODO: stop using inline css, put this in the main css file #}
data-layer="<video style='position:absolute;
top:10%;
width:100%;
margin=0 auto;' controls>
<source src={{ media.file }} type='video/webm' />
</video>" />
</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
@@ -82,7 +96,7 @@
</footer>
</div>
{% if images %}
{% if medias %}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script>!window.jQuery && document.write(unescape('%3Cscript src="{{ theme.url }}/js/jquery-1.8.2.min.js"%3E%3C/script%3E'))</script>
<script src="{{ theme.url }}/js/galleria-1.2.9.min.js"></script>

97
sigal/video.py Normal file
View File

@@ -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)

View File

@@ -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,

View File

@@ -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():

56
tests/test_video.py Normal file
View File

@@ -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