You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
6.3 KiB
154 lines
6.3 KiB
5 years ago
|
# -*- coding: utf-8 -*-
|
||
|
from __future__ import absolute_import, unicode_literals
|
||
|
|
||
|
import json
|
||
|
import logging
|
||
|
from collections import defaultdict
|
||
|
from logging import NullHandler, getLogger
|
||
|
import enzyme
|
||
|
|
||
|
from .. import OrderedDict
|
||
|
from ..properties import (
|
||
|
AudioCodec,
|
||
|
Basic,
|
||
|
Duration,
|
||
|
Language,
|
||
|
Quantity,
|
||
|
VideoCodec,
|
||
|
YesNo,
|
||
|
)
|
||
|
from ..property import Property
|
||
|
from ..provider import (
|
||
|
MalformedFileError,
|
||
|
Provider,
|
||
|
)
|
||
|
from ..rules import (
|
||
|
AudioChannelsRule,
|
||
|
ClosedCaptionRule,
|
||
|
HearingImpairedRule,
|
||
|
LanguageRule,
|
||
|
ResolutionRule,
|
||
|
)
|
||
|
from ..serializer import get_json_encoder
|
||
|
from ..units import units
|
||
|
from ..utils import todict
|
||
|
|
||
|
logger = getLogger(__name__)
|
||
|
logger.addHandler(NullHandler())
|
||
|
|
||
|
|
||
|
class EnzymeProvider(Provider):
|
||
|
"""Enzyme Provider."""
|
||
|
|
||
|
def __init__(self, config, *args, **kwargs):
|
||
|
"""Init method."""
|
||
|
super(EnzymeProvider, self).__init__(config, {
|
||
|
'general': OrderedDict([
|
||
|
('title', Property('title', description='media title')),
|
||
|
('duration', Duration('duration', description='media duration')),
|
||
|
]),
|
||
|
'video': OrderedDict([
|
||
|
('id', Basic('number', int, description='video track number')),
|
||
|
('name', Property('name', description='video track name')),
|
||
|
('language', Language('language', description='video language')),
|
||
|
('width', Quantity('width', units.pixel)),
|
||
|
('height', Quantity('height', units.pixel)),
|
||
|
('scan_type', YesNo('interlaced', yes='Interlaced', no='Progressive', default='Progressive',
|
||
|
description='video scan type')),
|
||
|
('resolution', None), # populated with ResolutionRule
|
||
|
# ('bit_depth', Property('bit_depth', Integer('video bit depth'))),
|
||
|
('codec', VideoCodec(config, 'codec_id', description='video codec')),
|
||
|
('forced', YesNo('forced', hide_value=False, description='video track forced')),
|
||
|
('default', YesNo('default', hide_value=False, description='video track default')),
|
||
|
('enabled', YesNo('enabled', hide_value=True, description='video track enabled')),
|
||
|
]),
|
||
|
'audio': OrderedDict([
|
||
|
('id', Basic('number', int, description='audio track number')),
|
||
|
('name', Property('name', description='audio track name')),
|
||
|
('language', Language('language', description='audio language')),
|
||
|
('codec', AudioCodec(config, 'codec_id', description='audio codec')),
|
||
|
('channels_count', Basic('channels', int, description='audio channels count')),
|
||
|
('channels', None), # populated with AudioChannelsRule
|
||
|
('forced', YesNo('forced', hide_value=False, description='audio track forced')),
|
||
|
('default', YesNo('default', hide_value=False, description='audio track default')),
|
||
|
('enabled', YesNo('enabled', hide_value=True, description='audio track enabled')),
|
||
|
]),
|
||
|
'subtitle': OrderedDict([
|
||
|
('id', Basic('number', int, description='subtitle track number')),
|
||
|
('name', Property('name', description='subtitle track name')),
|
||
|
('language', Language('language', description='subtitle language')),
|
||
|
('hearing_impaired', None), # populated with HearingImpairedRule
|
||
|
('closed_caption', None), # populated with ClosedCaptionRule
|
||
|
('forced', YesNo('forced', hide_value=False, description='subtitle track forced')),
|
||
|
('default', YesNo('default', hide_value=False, description='subtitle track default')),
|
||
|
('enabled', YesNo('enabled', hide_value=True, description='subtitle track enabled')),
|
||
|
]),
|
||
|
}, {
|
||
|
'video': OrderedDict([
|
||
|
('language', LanguageRule('video language')),
|
||
|
('resolution', ResolutionRule('video resolution')),
|
||
|
]),
|
||
|
'audio': OrderedDict([
|
||
|
('language', LanguageRule('audio language')),
|
||
|
('channels', AudioChannelsRule('audio channels')),
|
||
|
]),
|
||
|
'subtitle': OrderedDict([
|
||
|
('language', LanguageRule('subtitle language')),
|
||
|
('hearing_impaired', HearingImpairedRule('subtitle hearing impaired')),
|
||
|
('closed_caption', ClosedCaptionRule('closed caption')),
|
||
|
])
|
||
|
})
|
||
|
|
||
|
def accepts(self, video_path):
|
||
|
"""Accept only MKV files."""
|
||
|
return video_path.lower().endswith('.mkv')
|
||
|
|
||
|
@classmethod
|
||
|
def extract_info(cls, video_path):
|
||
|
"""Extract info from the video."""
|
||
|
with open(video_path, 'rb') as f:
|
||
|
return todict(enzyme.MKV(f))
|
||
|
|
||
|
def describe(self, video_path, context):
|
||
|
"""Return video metadata."""
|
||
|
try:
|
||
|
data = defaultdict(dict)
|
||
|
ff = self.extract_info(video_path)
|
||
|
|
||
|
def debug_data():
|
||
|
"""Debug data."""
|
||
|
return json.dumps(ff, cls=get_json_encoder(context), indent=4, ensure_ascii=False)
|
||
|
context['debug_data'] = debug_data
|
||
|
|
||
|
if logger.isEnabledFor(logging.DEBUG):
|
||
|
logger.debug('Video %r scanned using enzyme %r has raw data:\n%s',
|
||
|
video_path, enzyme.__version__, debug_data)
|
||
|
|
||
|
data.update(ff)
|
||
|
if 'info' in data and data['info'] is None:
|
||
|
return {}
|
||
|
except enzyme.MalformedMKVError: # pragma: no cover
|
||
|
raise MalformedFileError
|
||
|
|
||
|
if logger.level == logging.DEBUG:
|
||
|
logger.debug('Video {video_path} scanned using Enzyme {version} has raw data:\n{data}',
|
||
|
video_path=video_path, version=enzyme.__version__, data=json.dumps(data))
|
||
|
|
||
|
result = self._describe_tracks(video_path, data.get('info', {}), data.get('video_tracks'),
|
||
|
data.get('audio_tracks'), data.get('subtitle_tracks'), context)
|
||
|
|
||
|
if not result:
|
||
|
raise MalformedFileError
|
||
|
|
||
|
result['provider'] = {
|
||
|
'name': 'enzyme',
|
||
|
'version': self.version
|
||
|
}
|
||
|
|
||
|
return result
|
||
|
|
||
|
@property
|
||
|
def version(self):
|
||
|
"""Return enzyme version information."""
|
||
|
return {'enzyme': enzyme.__version__}
|