2019-09-19 09:50:20 +08:00
|
|
|
from __future__ import absolute_import
|
2019-08-26 10:08:13 +08:00
|
|
|
from os import path
|
|
|
|
|
2019-09-19 09:50:20 +08:00
|
|
|
from .baseparser import BaseParser
|
2019-08-26 10:08:13 +08:00
|
|
|
|
|
|
|
|
|
|
|
class StreamParser(BaseParser):
|
|
|
|
@staticmethod
|
|
|
|
def value_codec(data):
|
|
|
|
"""Returns a string"""
|
|
|
|
info = data.get("codec_name", None)
|
|
|
|
return info, (info or "null")
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_format(data):
|
|
|
|
"""Returns a string"""
|
|
|
|
info = data.get("format_name", None)
|
|
|
|
return info, (info or "null")
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_bit_rate(data):
|
|
|
|
"""Returns an int"""
|
|
|
|
info = data.get("bit_rate", None)
|
|
|
|
try:
|
|
|
|
return info, int(float(info))
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return info, 0
|
|
|
|
|
|
|
|
|
|
|
|
class VideoStreamParser(BaseParser):
|
|
|
|
@staticmethod
|
|
|
|
def value_codec(data):
|
|
|
|
return StreamParser.value_codec(data)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_format(data):
|
|
|
|
return StreamParser.value_format(data)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_bit_rate(data):
|
|
|
|
return StreamParser.value_bit_rate(data)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_resolution(data):
|
|
|
|
"""Returns a tuple (width, height)"""
|
|
|
|
width = data.get("width", None)
|
|
|
|
height = data.get("height", None)
|
2019-09-19 09:50:20 +08:00
|
|
|
if width is None and height is None:
|
2019-08-26 10:08:13 +08:00
|
|
|
return None, (0, 0)
|
|
|
|
try:
|
|
|
|
return (width, height), (int(float(width)), int(float(height)))
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return (width, height), (0, 0)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def average_framerate(data):
|
|
|
|
"""Returns an int"""
|
|
|
|
frames = data.get("nb_frames", None)
|
|
|
|
duration = data.get("duration", None)
|
|
|
|
try:
|
|
|
|
return float(frames) / float(duration)
|
|
|
|
except (ValueError, TypeError, ZeroDivisionError):
|
|
|
|
return 0.0
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def value_framerate(cls, data):
|
|
|
|
"""Returns a float"""
|
|
|
|
input_str = data.get("avg_frame_rate", None)
|
|
|
|
try:
|
|
|
|
num, den = input_str.split("/")
|
2019-09-19 09:50:20 +08:00
|
|
|
return input_str, round(float(num) / float(den), 3)
|
2019-08-26 10:08:13 +08:00
|
|
|
except (ValueError, ZeroDivisionError, AttributeError):
|
|
|
|
info = cls.average_framerate(data)
|
|
|
|
return input_str, info
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_aspect_ratio(data):
|
|
|
|
"""Returns a string"""
|
|
|
|
info = data.get("display_aspect_ratio", None)
|
|
|
|
return info, (info or "null")
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_pixel_format(data):
|
|
|
|
"""Returns a string"""
|
|
|
|
info = data.get("pix_fmt", None)
|
|
|
|
return info, (info or "null")
|
|
|
|
|
|
|
|
|
|
|
|
class AudioStreamParser(StreamParser):
|
|
|
|
@staticmethod
|
|
|
|
def value_sample_rate(data):
|
|
|
|
"""Returns an int - audio sample rate in Hz"""
|
|
|
|
info = data.get("sample_rate", None)
|
|
|
|
try:
|
|
|
|
return info, int(float(info))
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return info, 0
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_channel_count(data):
|
|
|
|
"""Returns an int"""
|
|
|
|
info = data.get("channels", None)
|
|
|
|
try:
|
|
|
|
return info, int(float(info))
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return info, 0
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_channel_layout(data):
|
|
|
|
"""Returns a string"""
|
|
|
|
info = data.get("channel_layout", None)
|
|
|
|
return info, (info or "null")
|
|
|
|
|
|
|
|
|
|
|
|
class SubtitleStreamParser(BaseParser):
|
|
|
|
@staticmethod
|
|
|
|
def value_codec(data):
|
|
|
|
return StreamParser.value_codec(data)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_language(data):
|
|
|
|
"""Returns a string """
|
|
|
|
tags = data.get("tags", None)
|
|
|
|
if tags:
|
2019-10-14 10:41:34 +08:00
|
|
|
info = tags.get("language", None) or tags.get("LANGUAGE", None)
|
2019-08-26 10:08:13 +08:00
|
|
|
return info, (info or "null")
|
|
|
|
return None, "null"
|
|
|
|
|
2019-09-19 09:50:20 +08:00
|
|
|
@staticmethod
|
|
|
|
def value_forced(data):
|
|
|
|
"""Returns a bool """
|
|
|
|
disposition = data.get("disposition", None)
|
|
|
|
if disposition:
|
|
|
|
info = disposition.get("forced", None)
|
|
|
|
return bool(info), (bool(info) or False)
|
|
|
|
return None, "null"
|
|
|
|
|
2019-08-26 10:08:13 +08:00
|
|
|
|
|
|
|
class ChapterParser(BaseParser):
|
|
|
|
@staticmethod
|
|
|
|
def value_start(data):
|
|
|
|
"""Returns an int"""
|
|
|
|
info = data.get("start_time", None)
|
|
|
|
try:
|
|
|
|
return info, float(data.get("start_time"))
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return info, 0
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def value_end(cls, data):
|
|
|
|
"""Returns a float"""
|
|
|
|
info = data.get("end_time", None)
|
|
|
|
try:
|
|
|
|
return info, float(info)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return info, 0
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_title(data):
|
|
|
|
"""Returns a string"""
|
|
|
|
info = data.get("tags", {}).get("title", None)
|
|
|
|
return info, (info or "null")
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def fillEmptyTitles(chapters):
|
|
|
|
"""Add text in place of empty titles
|
|
|
|
If a chapter doesn't have a title, this will add a basic
|
|
|
|
string in the form "Chapter `index+1`"
|
|
|
|
|
|
|
|
Args:
|
|
|
|
chapters(list<dict>): The list of parsed chapters
|
|
|
|
|
|
|
|
"""
|
|
|
|
index = 0
|
|
|
|
for chapter in chapters:
|
|
|
|
if not chapter["title"]:
|
|
|
|
chapter["title"] = "Chapter " + str(index)
|
|
|
|
index += 1
|
|
|
|
|
|
|
|
|
|
|
|
class RootParser(BaseParser):
|
|
|
|
@staticmethod
|
|
|
|
def value_duration(data):
|
|
|
|
"""Returns an int"""
|
|
|
|
info = data.get("duration", None)
|
|
|
|
try:
|
|
|
|
return info, float(info)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return info, 0.0
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def value_size(data):
|
|
|
|
"""Returns an int"""
|
|
|
|
info = data.get("size", None)
|
2019-09-19 09:50:20 +08:00
|
|
|
if info is None:
|
2019-08-26 10:08:13 +08:00
|
|
|
file_path = data.get("filename", "")
|
|
|
|
if path.isfile(file_path):
|
|
|
|
info = str(path.getsize(file_path))
|
|
|
|
try:
|
|
|
|
return info, int(float(info))
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return info, 0
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def value_bit_rate(cls, data):
|
|
|
|
"""Returns an int"""
|
|
|
|
info = data.get("bit_rate", None)
|
2019-09-19 09:50:20 +08:00
|
|
|
if info is None:
|
2019-08-26 10:08:13 +08:00
|
|
|
_, size = cls.value_size(data)
|
|
|
|
_, duration = cls.value_duration(data)
|
|
|
|
if size and duration:
|
|
|
|
info = size / (duration / 60 * 0.0075) / 1000
|
|
|
|
try:
|
|
|
|
return info, int(float(info))
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
return info, 0
|