aboutsummaryrefslogtreecommitdiffstats
path: root/mvc/video.py
diff options
context:
space:
mode:
Diffstat (limited to 'mvc/video.py')
-rw-r--r--mvc/video.py287
1 files changed, 0 insertions, 287 deletions
diff --git a/mvc/video.py b/mvc/video.py
deleted file mode 100644
index 0b89b63..0000000
--- a/mvc/video.py
+++ /dev/null
@@ -1,287 +0,0 @@
-import logging
-import os
-import re
-import tempfile
-import threading
-
-from mvc import execute
-from mvc.widgets import idle_add
-from mvc.settings import get_ffmpeg_executable_path
-from mvc.utils import hms_to_seconds, convert_path_for_subprocess
-
-logger = logging.getLogger(__name__)
-
-class VideoFile(object):
- def __init__(self, filename):
- self.filename = filename
- self.container = None
- self.video_codec = None
- self.audio_codec = None
- self.width = None
- self.height = None
- self.duration = None
- self.thumbnails = {}
- self.parse()
-
- def parse(self):
- self.__dict__.update(
- get_media_info(self.filename))
-
- @property
- def audio_only(self):
- return self.video_codec is None
-
- def get_thumbnail(self, completion, width=None, height=None, type_='.png'):
- if self.audio_only:
- # don't bother with thumbnails for audio files
- return None
- if width is None:
- width = -1
- if height is None:
- height = -1
-
- if self.duration is None:
- skip = 0
- else:
- skip = min(int(self.duration / 3), 120)
-
- key = (width, height, type_)
-
- def complete(name):
- self.thumbnails[key] = name
- completion()
-
- if key not in self.thumbnails:
- temp_path = tempfile.mktemp(suffix=type_)
- get_thumbnail(self.filename, width, height, temp_path, complete,
- skip=skip)
- return None
-
- return self.thumbnails.get(key)
-
-class Node(object):
- def __init__(self, line="", children=None):
- self.line = line
- if not children:
- self.children = []
- else:
- self.children = children
-
- if ": " in line:
- self.key, self.value = line.split(": ", 1)
- else:
- self.key = ""
- self.value = ""
-
- def add_node(self, node):
- self.children.append(node)
-
- def pformat(self, indent=0):
- s = (" " * indent) + ("Node: %s" % self.line) + "\n"
- for mem in self.children:
- s += mem.pformat(indent + 2)
- return s
-
- def get_by_key(self, key):
- if self.line.startswith(key):
- return self
- for mem in self.children:
- ret = mem.get_by_key(key)
- if ret:
- return ret
- return None
-
- def __repr__(self):
- return "<Node %s: %s>" % (self.key, self.value)
-
-
-def get_indent(line):
- length = len(line)
- line = line.lstrip()
- return (length - len(line), line)
-
-
-def parse_ffmpeg_output(output):
- """Takes a list of strings and parses it into a loose AST-ish
- thing.
-
- ffmpeg output uses indentation levels to indicate a hierarchy of
- data.
-
- If there's a : in the line, then it's probably a key/value pair.
-
- :param output: the content to parse as a list of strings.
-
- :returns: a top level node of the ffmpeg output AST
- """
- ast = Node()
- node_stack = [ast]
- indent_level = 0
-
- for mem in output:
- # skip blank lines
- if len(mem.strip()) == 0:
- continue
-
- indent, line = get_indent(mem)
- node = Node(line)
-
- if indent == indent_level:
- node_stack[-1].add_node(node)
- elif indent > indent_level:
- node_stack.append(node_stack[-1].children[-1])
- indent_level = indent
- node_stack[-1].add_node(node)
- else:
- for dedent in range(indent, indent_level, 2):
- # make sure we never pop everything off the stack.
- # the root should always be on the stack.
- if len(node_stack) <= 1:
- break
- node_stack.pop()
- indent_level = indent
- node_stack[-1].add_node(node)
-
- return ast
-
-
-# there's always a space before the size and either a space or a comma
-# afterwards.
-SIZE_RE = re.compile(" (\\d+)x(\\d+)[ ,]")
-
-
-def extract_info(ast):
- info = {}
- # logging.info("get_media_info: %s", ast.pformat())
-
- input0 = ast.get_by_key("Input #0")
- if not input0:
- raise ValueError("no input #0")
-
- foo, info['container'], bar = input0.line.split(', ', 2)
- if ',' in info['container']:
- info['container'] = info['container'].split(',')
-
- metadata = input0.get_by_key("Metadata")
- if metadata:
- for key in ('title', 'artist', 'album', 'track', 'genre'):
- node = metadata.get_by_key(key)
- if node:
- info[key] = node.line.split(':', 1)[1].strip()
- major_brand_node = metadata.get_by_key("major_brand")
- extra_container_types = []
- if major_brand_node:
- major_brand = major_brand_node.line.split(':')[1].strip()
- extra_container_types = [major_brand]
- else:
- major_brand = None
-
- compatible_brands_node = metadata.get_by_key("compatible_brands")
- if compatible_brands_node:
- line = compatible_brands_node.line.split(':')[1].strip()
- extra_container_types.extend(line[i:i+4] for i in range(0, len(line), 4)
- if line[i:i+4] != major_brand)
-
- if extra_container_types:
- if not isinstance(info['container'], list):
- info['container'] = [info['container']]
- info['container'].extend(extra_container_types)
-
- duration = input0.get_by_key("Duration:")
- if duration:
- _, rest = duration.line.split(':', 1)
- duration_string, _ = rest.split(', ', 1)
- logging.info("duration: %r", duration_string)
- try:
- hours, minutes, seconds = [
- float(i) for i in duration_string.split(':')]
- except ValueError:
- if duration_string.strip() != "N/A":
- logging.warn("Error parsing duration string: %r",
- duration_string)
- else:
- info['duration'] = hms_to_seconds(hours, minutes, seconds)
- for stream_node in duration.children:
- stream = stream_node.line
- if "Video:" in stream:
- stream_number, video, data = stream.split(': ', 2)
- video_codec = data.split(', ')[0]
- if ' ' in video_codec:
- video_codec, drmp = video_codec.split(' ', 1)
- if 'drm' in drmp:
- info.setdefault('has_drm', []).append('video')
- info['video_codec'] = video_codec
- match = SIZE_RE.search(data)
- if match:
- info["width"] = int(match.group(1))
- info["height"] = int(match.group(2))
- elif 'Audio:' in stream:
- stream_number, video, data = stream.split(': ', 2)
- audio_codec = data.split(', ')[0]
- if ' ' in audio_codec:
- audio_codec, drmp = audio_codec.split(' ', 1)
- if 'drm' in drmp:
- info.setdefault('has_drm', []).append('audio')
- info['audio_codec'] = audio_codec
- return info
-
-def get_ffmpeg_output(filepath):
-
- commandline = [get_ffmpeg_executable_path(),
- "-i", convert_path_for_subprocess(filepath)]
- logging.info("get_ffmpeg_output(): running %s", commandline)
- try:
- output = execute.check_output(commandline)
- except execute.CalledProcessError, e:
- if e.returncode != 1:
- logger.exception("error calling %r\noutput:%s", commandline,
- e.output)
- # ffmpeg -i generally returns 1, so we ignore the exception and
- # just get the output.
- output = e.output
-
- return output
-
-def get_media_info(filepath):
- """Takes a file path and returns a dict of information about
- this media file that it extracted from ffmpeg -i.
-
- :param filepath: absolute path to the media file in question
-
- :returns: dict of media info possibly containing: height, width,
- container, audio_codec, video_codec
- """
- logger.info('get_media_info: %r', filepath)
- output = get_ffmpeg_output(filepath)
- ast = parse_ffmpeg_output(output.splitlines())
- info = extract_info(ast)
- logger.info('get_media_info: %r', info)
- return info
-
-def get_thumbnail(filename, width, height, output, completion, skip=0):
- name = 'Thumbnail - %r @ %sx%s' % (filename, width, height)
- def run():
- rv = get_thumbnail_synchronous(filename, width, height, output, skip)
- idle_add(lambda: completion(rv))
- t = threading.Thread(target=run, name=name)
- t.start()
-
-def get_thumbnail_synchronous(filename, width, height, output, skip=0):
- executable = get_ffmpeg_executable_path()
- filter_ = 'scale=%i:%i' % (width, height)
- # bz19571: temporary disable: libav ffmpeg does not support this filter
- #if 'ffmpeg' in executable:
- # # supports the thumbnail filter, we hope
- # filter_ = 'thumbnail,' + filter_
- commandline = [executable,
- '-ss', str(skip),
- '-i', convert_path_for_subprocess(filename),
- '-vf', filter_, '-vframes', '1', output]
- try:
- execute.check_output(commandline)
- except execute.CalledProcessError, e:
- logger.exception('error calling %r\ncode:%s\noutput:%s',
- commandline, e.returncode, e.output)
- return None
- else:
- return output