diff --git a/docs/index.rst b/docs/index.rst index dae706f..b1ae35c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,8 +4,8 @@ FFmpegWrapper .. module:: ffmpegwrapper FFmpegWrapper is a small wrapper for the ffmpeg encoder. You can append -Codec, Filters and other OptionStores to the FFmpeg class and then run the -resulting command. +Codec, Filters and other ParameterContainer to the FFmpeg class and then +run the resulting command. >>> from ffmpegwrapper import FFmpeg, Input, Output, VideoCodec, VideoFilter >>> videofilter = VideoFilter().crop(300, 200) @@ -17,16 +17,13 @@ resulting command. -Stores and Options ------------------- +Parameter and Containers +--------------------- -There are two kinds of classes in FFmpegwrapper. The Stores and Options. -A Store is a object where you can append an Option or even Stores which -then have their own Options. A Option represent one or more arguments for -FFmpeg. A Store is more likea bigger block of Options for a specific need. -For example: Everything that describe the Output can be appended to the -Output class. +An Parameter is a namedtuple which represents parameters of the FFmpeg command. +Containers combines them. :class:`VideoFilter` is for example a subclassed +:class:`ParameterContainer` which can contain some filters. API --- @@ -68,14 +65,47 @@ Filters :members: :undoc-members: -Stores and Options -~~~~~~~~~~~~~~~~~~ +Container and Parameter +~~~~~~~~~~~~~~~~~~~~ -.. autoclass:: ffmpegwrapper.options.Option +.. autoclass:: ffmpegwrapper.parameters.Parameter :members: -.. autoclass:: ffmpegwrapper.options.OptionStore +.. autoclass:: ffmpegwrapper.parameters.ParameterContainer :members: License ------- + +Copyright (c) 2012 by Mathias Koehler. + +Some rights reserved. + +Redistribution and use in source and binary forms of the software as well +as documentation, with or without modification, are permitted provided +that the following conditions are met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND +CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT +NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. \ No newline at end of file diff --git a/ffmpegwrapper/codec.py b/ffmpegwrapper/codec.py index 3423f2a..691cac7 100644 --- a/ffmpegwrapper/codec.py +++ b/ffmpegwrapper/codec.py @@ -11,78 +11,79 @@ """ from itertools import chain - -from .options import OptionStore +from .parameters import ParameterContainer -NO_AUDIO = ['-an'] -NO_VIDEO = ['-vn'] +NO_AUDIO = ('-an',) +NO_VIDEO = ('-vn',) -class Codec(OptionStore): +class Codec(ParameterContainer): + """ Container for Codecs-Settings""" def __init__(self, name, *args): self.name = name - OptionStore.__init__(self, *args) + ParameterContainer.__init__(self, *args) class VideoCodec(Codec): """This represent an video codec. - + You can append this class to an :class:`Output` object to tell - which FFmpeg which codec you want.""" + which FFmpeg which codec you want. + """ def bitrate(self, bitrate): - self.add_option('-b', str(bitrate)) + self.add_formatparam('-b', str(bitrate)) return self def frames(self, number): - self.add_option('-vframes', str(number)) + self.add_formatparam('-vframes', str(number)) return self def fps(self, fps): - self.add_option('-r', str(fps)) + self.add_formatparam('-r', str(fps)) return self def size(self, x, y): filter = "{x}x{y}".format(x, y) - self.add_option('-s', filter) + self.add_formatparam('-s', filter) return self def aspect(self, x, y): - self.add_parameter('-aspect', x, y) + self.add_formatparam('-aspect', x, y) return self def bitrate_tolerance(self, tolerance): - self.add_option('-bt', str(tolerance)) + self.add_formatparam('-bt', str(tolerance)) return self def max_bitrate(self, rate): - self.add_option('-maxrate', str(rate)) + self.add_formatparam('-maxrate', str(rate)) return self def min_bitrate(self, rate): - self.add_option('-minrate', str(rate)) + self.add_formatparam('-minrate', str(rate)) return self def buffer_size(self, size): - self.add_option('-bufsize', str(size)) + self.add_formatparam('-bufsize', str(size)) return self def pass_number(self, number): - self.add_option('-pass', str(number)) + self.add_formatparam('-pass', str(number)) return self def language(self, lang): - self.add_option('-vlang', str(lang)) + self.add_formatparam('-vlang', str(lang)) return self def same_quality(self): - self.add_option('-sameq', None) + self.add_formatparam('-sameq', None) return self def preset(self, preset): - self.add_option('-vpre', str(preset)) + self.add_formatparam('-vpre', str(preset)) return self def __iter__(self): @@ -91,37 +92,38 @@ class VideoCodec(Codec): class AudioCodec(Codec): """This represent an audio codec. - + You can append this class to an :class:`Output` object to tell - which FFmpeg which codec you want.""" + which FFmpeg which codec you want. + """ def frames(self, number): - self.add_option('-aframes', str(number)) + self.add_formatparam('-aframes', str(number)) return self def frequence(self, freq): - self.add_option('-ar', str(freq)) + self.add_formatparam('-ar', str(freq)) return self def bitrate(self, rate): - self.add_option('-ab', str(rate)) + self.add_formatparam('-ab', str(rate)) return self def quality(self, number): - self.add_option('-aq', str(number)) + self.add_formatparam('-aq', str(number)) return self def channels(self, number): - self.add_option('-ac', str(number)) + self.add_formatparam('-ac', str(number)) return self def language(self, lang): - self.add_option('-alang', str(lang)) + self.add_formatparam('-alang', str(lang)) return self def preset(self, preset): """Load default presets from a preset file""" - self.add_option('-apre', str(preset)) + self.add_formatparam('-apre', str(preset)) return self def __iter__(self): diff --git a/ffmpegwrapper/ffmpeg.py b/ffmpegwrapper/ffmpeg.py index f6cd7f0..af0d299 100644 --- a/ffmpegwrapper/ffmpeg.py +++ b/ffmpegwrapper/ffmpeg.py @@ -19,42 +19,43 @@ try: except ImportError: from queue import Queue, Empty -from .options import OptionStore, Option +from .parameters import ParameterContainer, Parameter -class Input(OptionStore): - """Store for a input file. +class Input(ParameterContainer): + """Container for a input file. - :param file: Path to the input file - :param args: A list of Stores that should be appended + :param file_path: Path to the input file + :param args: A list of Containers that should be appended """ - def __init__(self, file, *args): - self.file = file - OptionStore.__init__(self, *args) + def __init__(self, file_path, *args): + self.file_path = file_path + ParameterContainer.__init__(self, *args) def __iter__(self): - return chain(OptionStore.__iter__(self), ['-i', self.file]) + return chain(ParameterContainer.__iter__(self), + ['-i', self.file_path]) -class Output(OptionStore): - """Store for a output file. +class Output(ParameterContainer): + """Container for a output file. - :param file: Path in which the file should be saved - :param args: A list of Stores that should be appended + :param file_path: Path in which the file should be saved + :param args: A list of Containers that should be appended """ - def __init__(self, file, *args): - self.file = file - OptionStore.__init__(self, *args) + def __init__(self, file_path, *args): + self.file_path = file_path + ParameterContainer.__init__(self, *args) def overwrite(self): """Overwrite the file if it already exist""" - self.add_option('-y', None) + self.add_parameter('-y', None) return self def __iter__(self): - return chain(OptionStore.__iter__(self), [self.file]) + return chain(ParameterContainer.__iter__(self), [self.file_path]) class FFmpegProcess(object): @@ -65,7 +66,7 @@ class FFmpegProcess(object): def __init__(self, command): self.command = list(command) - self.queue = Queue() + self.queue = Queue(maxsize=2000) self.process = None def _queue_output(self, out, queue): @@ -127,26 +128,25 @@ class FFmpegProcess(object): return self.readlines() - -class FFmpeg(OptionStore): +class FFmpeg(ParameterContainer): """This class represent the FFmpeg command. It behaves like a list. If you iterate over the object it will yield small parts from the ffmpeg command with it arguments. The arguments - for the command are in the Option classes. They can be appended directly - or through one or more Stores. + for the command are in the Parameter classes. They can be appended + directly or through one or more Containers. :param binary: The binary subprocess should execute at the :meth:`run` - :param args: A list of Stores that should be appended + :param args: A list of Containers that should be appended """ def __init__(self, binary="ffmpeg", *args): self.binary = binary self.process = None - OptionStore.__init__(self, *args) + ParameterContainer.__init__(self, *args) - def add_option(self, key, value): - self.container_list.insert(0, Option(key, value)) + def add_parameter(self, key, value): + self.container_list.insert(0, Parameter(key, value)) def run(self): """Executes the command of this object. Return a @@ -167,4 +167,4 @@ class FFmpeg(OptionStore): self.process = None def __iter__(self): - return chain([self.binary], OptionStore.__iter__(self)) + return chain([self.binary], ParameterContainer.__iter__(self)) diff --git a/ffmpegwrapper/filter.py b/ffmpegwrapper/filter.py index 1f9f66f..d5b6246 100644 --- a/ffmpegwrapper/filter.py +++ b/ffmpegwrapper/filter.py @@ -10,25 +10,25 @@ """ from itertools import chain - -from .options import OptionStore +from .parameters import ParameterContainer -class FilterStore(OptionStore): +class FilterContainer(ParameterContainer): + """Contains filters which you can append to an output file.""" def __str__(self): - return ",".join(FilterStore.__iter__(self)) + return ",".join(FilterContainer.__iter__(self)) def __iter__(self): - for key, value in OptionStore.iteritems(self): + for key, value in ParameterContainer.iteritems(self): if value is not None: yield "=".join([key, str(value)]) else: yield key -class VideoFilter(FilterStore): - """FilterStore for Videofilters. +class VideoFilter(FilterContainer): + """FilterContainer for Videofilters. .. seealso:: @@ -40,142 +40,142 @@ class VideoFilter(FilterStore): """ def blackframe(self, amount, threshold): - self.add_parameter('blackframe', amount, threshold) + self.add_formatparam('blackframe', amount, threshold) return self def copy(self): - self.add_option('copy', None) + self.add_parameter('copy', None) return self def crop(self, out_w, out_h=None, x=None, y=None): - self.add_parameter('crop', out_w, out_h, x, y) + self.add_formatparam('crop', out_w, out_h, x, y) return self def cropdetect(self, limit=None, round=None, reset=None): - self.add_parameter('cropdetect', limit, round, reset) + self.add_formatparam('cropdetect', limit, round, reset) return self def drawbox(self, x, y, width, height, color): - self.add_parameter('drawbox', x, y, width, height, color) + self.add_formatparam('drawbox', x, y, width, height, color) return self def drawtext(self, **kwargs): - self.add_parameter('drawtext', **kwargs) + self.add_formatparam('drawtext', **kwargs) return self def fade(self, type, start, number): - self.add_parameter('fade', type, start, number) + self.add_formatparam('fade', type, start, number) return self def fieldorder(self, type): if str(type) not in ['0', '1', 'bff', 'tff']: - raise ValueError('Invalid Option for fieldorder. ' + raise ValueError('Invalid Parameter for fieldorder. ' 'Read FFmpeg manual!') - self.add_parameter('fieldorder', type) + self.add_formatparam('fieldorder', type) return self def fifo(self): - self.add_option('fifo', None) + self.add_parameter('fifo', None) return self def format(self, *args): - self.add_parameter('format', *args) + self.add_formatparam('format', *args) return self def freior(self, name, *args): - self.add_parameter('frei0r', name, *args) + self.add_formatparam('frei0r', name, *args) return self def gradfun(self, strength='', radius=''): - self.add_parameter('gradfun', strength, radius) + self.add_formatparam('gradfun', strength, radius) return self def hflip(self): - self.add_option('hflip', None) + self.add_parameter('hflip', None) return self def hqdn3d(self, luma_sp=None, chroma_sp=None, luma_tmp=None, chroma_tmp=None): - self.add_parameter('hqdn3d', + self.add_formatparam('hqdn3d', luma_sp, chroma_sp, luma_tmp, chroma_tmp) return self def mp(self, **kwargs): - self.add_parameter('mp', **kwargs) + self.add_formatparam('mp', **kwargs) return self def negate(self): - self.add_option('negate', 1) + self.add_parameter('negate', 1) return self def noformat(self, *args): - self.add_parameter('noformat', *args) + self.add_formatparam('noformat', *args) return self def null(self): - self.add_option('null', None) + self.add_parameter('null', None) return self def overlay(self, x, y): - self.add_parameter('overlay', x, y) + self.add_formatparam('overlay', x, y) return self def pad(self, width, height, x, y, color): - self.add_parameter('pad', width, height, x, y, color) + self.add_formatparam('pad', width, height, x, y, color) return self def scale(self, width=-1, height=-1): - self.add_parameter('scale', width, height) + self.add_formatparam('scale', width, height) return self def select(self, expression): - self.add_parameter('select', expression) + self.add_formatparam('select', expression) return self def setdar(self, x, y): - self.add_parameter('setdar', x, y) + self.add_formatparam('setdar', x, y) return self def setpts(self, expression): - self.add_parameter('setpts', expression) + self.add_formatparam('setpts', expression) return self def setsar(self, x, y): - self.add_parameter('setsar', x, y) + self.add_formatparam('setsar', x, y) return self def slicify(self, height=16): - self.add_parameter('slicify', height) + self.add_formatparam('slicify', height) return self def transpose(self, type): - if str(type) not in ['0', '1','2', '3']: - raise ValueError('Invalid Option for transpose. ' + if str(type) not in ['0', '1', '2', '3']: + raise ValueError('Invalid Parameter for transpose. ' 'Read FFmpeg manual') - self.add_parameter('transpose', type) + self.add_formatparam('transpose', type) return self def unsharp(self, *args): if len(args) > 6: message = 'unsharp() takes exactly 6 positional arguments' raise TypeError(message) - self.add_parameter('unsharp', *args) + self.add_formatparam('unsharp', *args) return self def vflip(self): - self.add_option('vflip', None) + self.add_parameter('vflip', None) return self def yadif(self, mode=0, parity=-1): - self.add_parameter('yadif', mode, parity) + self.add_formatparam('yadif', mode, parity) return self def __iter__(self): - return chain(['-vf', FilterStore.__str__(self)]) + return chain(['-vf', FilterContainer.__str__(self)]) -class AudioFilter(FilterStore): - """FilterStore for Audifilters. +class AudioFilter(FilterContainer): + """FilterContainer for Audifilters. .. seealso:: @@ -188,8 +188,8 @@ class AudioFilter(FilterStore): def null(self): """does nothing""" - self.add_option('null', None) + self.add_parameter('null', None) return self def __iter__(self): - return chain(['-af', FilterStore.__str__(self)]) + return chain(['-af', FilterContainer.__str__(self)]) diff --git a/ffmpegwrapper/options.py b/ffmpegwrapper/options.py deleted file mode 100644 index 04261ee..0000000 --- a/ffmpegwrapper/options.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- - -from itertools import chain -from collections import MutableSequence, namedtuple - -try: - from itertools import ifilter -except ImportError: - ifilter = filter - - -def format_parameter(*args, **kwargs): - parameter_list = [] - for key, value in kwargs.items(): - try: - if not value: - parameter_list.append(key) - else: - parameter_list.append("=".join([key, value])) - except TypeError: - values = ':'.join(kwargs[key]) - parameter_list.append("=".join([key, values])) - for value in args: - if value is not None: - parameter_list.append(str(value)) - result = ':'.join(parameter_list) - if kwargs: - return '"%s"' % result - return result - - -Option = namedtuple('Option', ['name', 'value']) - - -class OptionStore(MutableSequence): - - def __init__(self, *containers): - self.container_list = list(containers) - - def add_option(self, key, value): - self.container_list.append(Option(key, value)) - - def add_parameter(self, name, *args, **kwargs): - parameter = format_parameter(*args, **kwargs) - self.add_option(name, parameter) - - def insert(self, index, value): - self.container_list.insert(index, value) - - def iteritems(self): - return iter(self.container_list) - - def __iter__(self): - return ifilter(None, chain.from_iterable(self.container_list)) - - def __len__(self): - return self.container_list.__len__() - - def __setitem__(self, index, value): - self.container_list.__setitem__(index, value) - - def __getitem__(self, index): - return self.container_list.__getitem__(index) - - def __delitem__(self, index): - self.container_list.__delitem__(index) - - def __contains__(self, other): - return self.container_list.__contains__(other) - - def __repr__(self): - return "<{cls} {opts}>".format( - opts=list(self), cls=self.__class__.__name__) diff --git a/setup.py b/setup.py index 5024430..8ae13bb 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ FFmpegwrapper ~~~~~~~~~~~~~ FFmpegWrapper is a small wrapper for the ffmpeg encoder. You can append -Codec, Filters and other OptionStores to the FFmpeg class and then run the +Codec, Filters and other ParameterStores to the FFmpeg class and then run the resulting command. >>> from ffmpegwrapper import FFmpeg, Input, Output, VideoCodec, VideoFilter @@ -18,10 +18,8 @@ resulting command. """ -import sys from setuptools import setup - setup( name="ffmpegwrapper", version="0.1-dev", diff --git a/test.py b/test.py index 047dbd0..8f71cee 100644 --- a/test.py +++ b/test.py @@ -6,7 +6,7 @@ from mock import patch from ffmpegwrapper import FFmpeg, Input, Output, \ VideoCodec, AudioCodec, VideoFilter -from ffmpegwrapper.options import Option +from ffmpegwrapper.parameters import Parameter class FFmpegTestCase(unittest.TestCase): @@ -31,28 +31,28 @@ class FFmpegTestCase(unittest.TestCase): def test_input_interface(self): input = Input('/old') self.assertEqual(list(input), ['-i', '/old']) - self.assertEqual(input.file, '/old') + self.assertEqual(input.file_path, '/old') - option = Option('-vf', 'x11grab') - input.append(option) + parameter = Parameter('-vf', 'x11grab') + input.append(parameter) self.assertEqual(list(input), ['-vf', 'x11grab', '-i', '/old']) - self.assertEqual(input.pop(), option) + self.assertEqual(input.pop(), parameter) - input.add_option('-vf', 'x11grab') - self.assertEqual(input.container_list, [option]) + input.add_formatparam('-vf', 'x11grab') + self.assertEqual(input.container_list, [parameter]) def test_output_interface(self): output = Output('/new') self.assertEqual(list(output), ['/new']) - self.assertEqual(output.file, '/new') + self.assertEqual(output.file_path, '/new') - option = Option('-vcodec', 'libx264') - output.append(option) + parameter = Parameter('-vcodec', 'libx264') + output.append(parameter) self.assertEqual(list(output), ['-vcodec', 'libx264', '/new']) - self.assertEqual(output.pop(), option) + self.assertEqual(output.pop(), parameter) - output.add_option('-vcodec', 'libx264') - self.assertEqual(output.container_list, [option]) + output.add_formatparam('-vcodec', 'libx264') + self.assertEqual(output.container_list, [parameter]) def test_codec_interface(self): codec = VideoCodec('libx264')