SickGear/lib/hachoir_parser/video/mov.py
echel0n 0d9fbc1ad7 Welcome to our SickBeard-TVRage Edition ...
This version of SickBeard uses both TVDB and TVRage to search and gather it's series data from allowing you to now have access to and download shows that you couldn't before because of being locked into only what TheTVDB had to offer.

Also this edition is based off the code we used in our XEM editon so it does come with scene numbering support as well as all the other features our XEM edition has to offer.

Please before using this with your existing database (sickbeard.db) please make a backup copy of it and delete any other database files such as cache.db and failed.db if present, we HIGHLY recommend starting out with no database files at all to make this a fresh start but the choice is at your own risk!

Enjoy!
2014-03-09 22:39:12 -07:00

246 lines
8.9 KiB
Python

"""
Apple Quicktime Movie (file extension ".mov") parser.
Documents:
- Parsing and Writing QuickTime Files in Java (by Chris Adamson, 02/19/2003)
http://www.onjava.com/pub/a/onjava/2003/02/19/qt_file_format.html
- QuickTime File Format (official technical reference)
http://developer.apple.com/documentation/QuickTime/QTFF/qtff.pdf
- Apple QuickTime:
http://wiki.multimedia.cx/index.php?title=Apple_QuickTime
- File type (ftyp):
http://www.ftyps.com/
Author: Victor Stinner
Creation: 2 august 2006
"""
from lib.hachoir_parser import Parser
from lib.hachoir_core.field import (ParserError, FieldSet, MissingField,
UInt8, Int16, UInt16, UInt32, TimestampMac32,
String, PascalString8, CString,
RawBytes, PaddingBytes)
from lib.hachoir_core.endian import BIG_ENDIAN
from lib.hachoir_core.text_handler import textHandler, hexadecimal
class QTFloat32(FieldSet):
static_size = 32
def createFields(self):
yield Int16(self, "int_part")
yield UInt16(self, "float_part")
def createValue(self):
return self["int_part"].value + float(self["float_part"].value) / 65535
def createDescription(self):
return str(self.value)
class AtomList(FieldSet):
def createFields(self):
while not self.eof:
yield Atom(self, "atom[]")
class TrackHeader(FieldSet):
def createFields(self):
yield textHandler(UInt8(self, "version"), hexadecimal)
# TODO: sum of :
# TrackEnabled = 1;
# TrackInMovie = 2;
# TrackInPreview = 4;
# TrackInPoster = 8
yield RawBytes(self, "flags", 3)
yield TimestampMac32(self, "creation_date")
yield TimestampMac32(self, "lastmod_date")
yield UInt32(self, "track_id")
yield PaddingBytes(self, "reserved[]", 8)
yield UInt32(self, "duration")
yield PaddingBytes(self, "reserved[]", 8)
yield Int16(self, "video_layer", "Middle is 0, negative in front")
yield PaddingBytes(self, "other", 2)
yield QTFloat32(self, "geom_a", "Width scale")
yield QTFloat32(self, "geom_b", "Width rotate")
yield QTFloat32(self, "geom_u", "Width angle")
yield QTFloat32(self, "geom_c", "Height rotate")
yield QTFloat32(self, "geom_d", "Height scale")
yield QTFloat32(self, "geom_v", "Height angle")
yield QTFloat32(self, "geom_x", "Position X")
yield QTFloat32(self, "geom_y", "Position Y")
yield QTFloat32(self, "geom_w", "Divider scale")
yield QTFloat32(self, "frame_size_width")
yield QTFloat32(self, "frame_size_height")
class HDLR(FieldSet):
def createFields(self):
yield textHandler(UInt8(self, "version"), hexadecimal)
yield RawBytes(self, "flags", 3)
yield String(self, "subtype", 8)
yield String(self, "manufacturer", 4)
yield UInt32(self, "res_flags")
yield UInt32(self, "res_flags_mask")
if self.root.is_mpeg4:
yield CString(self, "name")
else:
yield PascalString8(self, "name")
class MediaHeader(FieldSet):
def createFields(self):
yield textHandler(UInt8(self, "version"), hexadecimal)
yield RawBytes(self, "flags", 3)
yield TimestampMac32(self, "creation_date")
yield TimestampMac32(self, "lastmod_date")
yield UInt32(self, "time_scale")
yield UInt32(self, "duration")
yield UInt16(self, "mac_lang")
yield Int16(self, "quality")
class ELST(FieldSet):
def createFields(self):
yield textHandler(UInt8(self, "version"), hexadecimal)
yield RawBytes(self, "flags", 3)
yield UInt32(self, "nb_edits")
yield UInt32(self, "length")
yield UInt32(self, "start")
yield QTFloat32(self, "playback_speed")
class Load(FieldSet):
def createFields(self):
yield UInt32(self, "start")
yield UInt32(self, "length")
yield UInt32(self, "flags") # PreloadAlways = 1 or TrackEnabledPreload = 2
yield UInt32(self, "hints") # KeepInBuffer = 0x00000004; HighQuality = 0x00000100; SingleFieldVideo = 0x00100000
class MovieHeader(FieldSet):
def createFields(self):
yield textHandler(UInt8(self, "version"), hexadecimal)
yield RawBytes(self, "flags", 3)
yield TimestampMac32(self, "creation_date")
yield TimestampMac32(self, "lastmod_date")
yield UInt32(self, "time_scale")
yield UInt32(self, "duration")
yield QTFloat32(self, "play_speed")
yield UInt16(self, "volume")
yield PaddingBytes(self, "reserved[]", 10)
yield QTFloat32(self, "geom_a", "Width scale")
yield QTFloat32(self, "geom_b", "Width rotate")
yield QTFloat32(self, "geom_u", "Width angle")
yield QTFloat32(self, "geom_c", "Height rotate")
yield QTFloat32(self, "geom_d", "Height scale")
yield QTFloat32(self, "geom_v", "Height angle")
yield QTFloat32(self, "geom_x", "Position X")
yield QTFloat32(self, "geom_y", "Position Y")
yield QTFloat32(self, "geom_w", "Divider scale")
yield UInt32(self, "preview_start")
yield UInt32(self, "preview_length")
yield UInt32(self, "still_poster")
yield UInt32(self, "sel_start")
yield UInt32(self, "sel_length")
yield UInt32(self, "current_time")
yield UInt32(self, "next_track")
class FileType(FieldSet):
def createFields(self):
yield String(self, "brand", 4, "Major brand")
yield UInt32(self, "version", "Version")
while not self.eof:
yield String(self, "compat_brand[]", 4, "Compatible brand")
class Atom(FieldSet):
tag_info = {
# TODO: Use dictionnary of dictionnary, like Matroska parser does
# "elst" is a child of "edts", but not of "moov" for example
"moov": (AtomList, "movie", "Movie"),
"trak": (AtomList, "track", "Track"),
"mdia": (AtomList, "media", "Media"),
"edts": (AtomList, "edts", ""),
"minf": (AtomList, "minf", ""),
"stbl": (AtomList, "stbl", ""),
"dinf": (AtomList, "dinf", ""),
"elst": (ELST, "edts", ""),
"tkhd": (TrackHeader, "track_hdr", "Track header"),
"hdlr": (HDLR, "hdlr", ""),
"mdhd": (MediaHeader, "media_hdr", "Media header"),
"load": (Load, "load", ""),
"mvhd": (MovieHeader, "movie_hdr", "Movie header"),
"ftyp": (FileType, "file_type", "File type"),
}
tag_handler = [ item[0] for item in tag_info ]
tag_desc = [ item[1] for item in tag_info ]
def createFields(self):
yield UInt32(self, "size")
yield String(self, "tag", 4)
size = self["size"].value
if size == 1:
raise ParserError("Extended size is not supported!")
#yield UInt64(self, "size64")
size = self["size64"].value
elif size == 0:
#size = (self.root.size - self.root.current_size - self.current_size) / 8
if self._size is None:
size = (self.parent.size - self.current_size) / 8 - 8
else:
size = (self.size - self.current_size) / 8
else:
size = size - 8
if 0 < size:
tag = self["tag"].value
if tag in self.tag_info:
handler, name, desc = self.tag_info[tag]
yield handler(self, name, desc, size=size*8)
else:
yield RawBytes(self, "data", size)
def createDescription(self):
return "Atom: %s" % self["tag"].value
class MovFile(Parser):
PARSER_TAGS = {
"id": "mov",
"category": "video",
"file_ext": ("mov", "qt", "mp4", "m4v", "m4a", "m4p", "m4b"),
"mime": (u"video/quicktime", u'video/mp4'),
"min_size": 8*8,
"magic": (("moov", 4*8),),
"description": "Apple QuickTime movie"
}
BRANDS = {
# File type brand => MIME type
'mp41': u'video/mp4',
'mp42': u'video/mp4',
}
endian = BIG_ENDIAN
def __init__(self, *args, **kw):
Parser.__init__(self, *args, **kw)
self.is_mpeg4 = False
def validate(self):
# TODO: Write better code, erk!
size = self.stream.readBits(0, 32, self.endian)
if size < 8:
return "Invalid first atom size"
tag = self.stream.readBytes(4*8, 4)
return tag in ("ftyp", "moov", "free")
def createFields(self):
while not self.eof:
yield Atom(self, "atom[]")
def createMimeType(self):
first = self[0]
try:
# Read brands in the file type
if first['tag'].value != "ftyp":
return None
file_type = first["file_type"]
brand = file_type["brand"].value
if brand in self.BRANDS:
return self.BRANDS[brand]
for field in file_type.array("compat_brand"):
brand = field.value
if brand in self.BRANDS:
return self.BRANDS[brand]
except MissingField:
pass
return None