[4] | 1 | # ----------------------------------------------------------------------- |
---|
| 2 | # lsdvdinfo.py - parse dvd title structure |
---|
| 3 | # ----------------------------------------------------------------------- |
---|
| 4 | # $Id: lsdvd.py,v 1.13.2.1 2005/06/30 17:28:24 dischi Exp $ |
---|
| 5 | # |
---|
| 6 | # Use lsdvd to get dvd informations. |
---|
| 7 | # |
---|
| 8 | # ----------------------------------------------------------------------- |
---|
| 9 | # $Log: lsdvd.py,v $ |
---|
| 10 | # Revision 1.13.2.1 2005/06/30 17:28:24 dischi |
---|
| 11 | # fix to new lsdvd |
---|
| 12 | # |
---|
| 13 | # Revision 1.13 2005/01/13 20:19:34 dischi |
---|
| 14 | # detect video_ts in lower case |
---|
| 15 | # |
---|
| 16 | # Revision 1.12 2004/08/27 17:27:44 dischi |
---|
| 17 | # do not import ifoparser |
---|
| 18 | # |
---|
| 19 | # |
---|
| 20 | # ----------------------------------------------------------------------- |
---|
| 21 | # Copyright (C) 2003 Thomas Schueppel, Dirk Meyer |
---|
| 22 | # |
---|
| 23 | # This program is free software; you can redistribute it and/or modify |
---|
| 24 | # it under the terms of the GNU General Public License as published by |
---|
| 25 | # the Free Software Foundation; either version 2 of the License, or |
---|
| 26 | # (at your option) any later version. |
---|
| 27 | # |
---|
| 28 | # This program is distributed in the hope that it will be useful, but |
---|
| 29 | # WITHOUT ANY WARRANTY; without even the implied warranty of MER- |
---|
| 30 | # CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
---|
| 31 | # Public License for more details. |
---|
| 32 | # |
---|
| 33 | # You should have received a copy of the GNU General Public License along |
---|
| 34 | # with this program; if not, write to the Free Software Foundation, Inc., |
---|
| 35 | # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
| 36 | # |
---|
| 37 | # ----------------------------------------------------------------------- */ |
---|
| 38 | |
---|
| 39 | |
---|
| 40 | import os |
---|
| 41 | import re |
---|
| 42 | from mmpython import mediainfo |
---|
| 43 | import mmpython |
---|
| 44 | from discinfo import DiscInfo |
---|
| 45 | |
---|
| 46 | LSDVD_EXE='lsdvd' |
---|
| 47 | |
---|
| 48 | class DVDAudio(mediainfo.AudioInfo): |
---|
| 49 | def __init__(self, data): |
---|
| 50 | mediainfo.AudioInfo.__init__(self) |
---|
| 51 | self.number = int(data[1]) |
---|
| 52 | if data[3] != 'xx': |
---|
| 53 | self.language = data[3] |
---|
| 54 | try: |
---|
| 55 | # some DVDs have a very bad language setting |
---|
| 56 | self.language.encode() |
---|
| 57 | except UnicodeError: |
---|
| 58 | self.language = '' |
---|
| 59 | |
---|
| 60 | try: |
---|
| 61 | self.codec = data[7] |
---|
| 62 | try: |
---|
| 63 | self.samplerate = int(data[9]) |
---|
| 64 | except ValueError, e: |
---|
| 65 | if data[9].lower().find('khz') > 0: |
---|
| 66 | pos = data[9].lower().find('khz') |
---|
| 67 | self.samplerate = int(data[9][:pos]) * 1000 |
---|
| 68 | else: |
---|
| 69 | raise e |
---|
| 70 | self.channels = data[13] |
---|
| 71 | except Exception, e: |
---|
| 72 | # WTF, strange DVD, now try to find the bug (may not work) |
---|
| 73 | self.codec = data[data.index('Format:') + 1] |
---|
| 74 | try: |
---|
| 75 | freq = data[data.index('Frequency:') + 1] |
---|
| 76 | self.samplerate = int(freq) |
---|
| 77 | except ValueError: |
---|
| 78 | if freq.lower().find('khz') > 0: |
---|
| 79 | self.samplerate = int(freq[:freq.lower().find('khz')])*1000 |
---|
| 80 | self.channels = int(data[data.index('Channels:') + 1]) |
---|
| 81 | |
---|
| 82 | |
---|
| 83 | class DVDVideo(mediainfo.VideoInfo): |
---|
| 84 | def __init__(self, data): |
---|
| 85 | mediainfo.VideoInfo.__init__(self) |
---|
| 86 | self.width = int(data[12]) |
---|
| 87 | self.height = int(data[14]) |
---|
| 88 | self.fps = float(data[5]) |
---|
| 89 | self.aspect = data[10] |
---|
| 90 | |
---|
| 91 | |
---|
| 92 | class DVDTitle(mediainfo.AVInfo): |
---|
| 93 | def __init__(self, data): |
---|
| 94 | mediainfo.AVInfo.__init__(self) |
---|
| 95 | self.number = int(data[1]) |
---|
| 96 | |
---|
| 97 | self.keys.append('subtitles') |
---|
| 98 | self.keys.append('chapters') |
---|
| 99 | |
---|
| 100 | self.mime = 'video/mpeg' |
---|
| 101 | |
---|
| 102 | l = re.split('[:.]', data[3]) |
---|
| 103 | self.length = (int(l[0])*60+int(l[1]))*60+int(l[2]) |
---|
| 104 | self.trackno = int(data[1]) |
---|
| 105 | self.chapters = int(data[5]) |
---|
| 106 | |
---|
| 107 | |
---|
| 108 | class DVDInfo(DiscInfo): |
---|
| 109 | def __init__(self, device): |
---|
| 110 | DiscInfo.__init__(self) |
---|
| 111 | self.context = 'video' |
---|
| 112 | self.offset = 0 |
---|
| 113 | if mediainfo.DEBUG > 1: |
---|
| 114 | print 'trying lsdvd for scanning the disc' |
---|
| 115 | |
---|
| 116 | if os.path.isdir(device): |
---|
| 117 | self.valid = self.isDVDdir(device) |
---|
| 118 | else: |
---|
| 119 | self.valid = self.isDisc(device) |
---|
| 120 | |
---|
| 121 | if self.valid and self.tracks: |
---|
| 122 | self.keys.append('length') |
---|
| 123 | self.length = 0 |
---|
| 124 | first = 0 |
---|
| 125 | |
---|
| 126 | for t in self.tracks: |
---|
| 127 | self.length += t.length |
---|
| 128 | if not first: |
---|
| 129 | first = t.length |
---|
| 130 | |
---|
| 131 | if self.length/len(self.tracks) == first: |
---|
| 132 | # badly mastered dvd |
---|
| 133 | self.length = first |
---|
| 134 | |
---|
| 135 | if mediainfo.DEBUG > 1: |
---|
| 136 | print 'lsdvd detection ok' |
---|
| 137 | |
---|
| 138 | self.mime = 'video/dvd' |
---|
| 139 | self.type = 'DVD' |
---|
| 140 | self.subtype = 'video' |
---|
| 141 | |
---|
| 142 | |
---|
| 143 | def lsdvd(self, path): |
---|
| 144 | """ |
---|
| 145 | use lsdvd to get informations about this disc |
---|
| 146 | """ |
---|
| 147 | import popen2 |
---|
| 148 | child = popen2.Popen3('%s -v -n -a -s "%s"' % \ |
---|
| 149 | (LSDVD_EXE, path), 1, 100) |
---|
| 150 | for line in child.fromchild.readlines(): |
---|
| 151 | data = line.replace(',', '').replace('\t', '').\ |
---|
| 152 | replace('\n', '').lstrip(' ').split(' ') |
---|
| 153 | if len(data) > 2: |
---|
| 154 | if data[0] == 'Title:': |
---|
| 155 | ti = DVDTitle(data) |
---|
| 156 | self.appendtrack(ti) |
---|
| 157 | elif data[0] == 'Audio:': |
---|
| 158 | self.tracks[-1].audio.append(DVDAudio(data)) |
---|
| 159 | elif data[0] == 'Subtitle:': |
---|
| 160 | self.tracks[-1].subtitles.append(data[3]) |
---|
| 161 | elif data[0] == 'VTS:': |
---|
| 162 | self.tracks[-1].video.append(DVDVideo(data)) |
---|
| 163 | self.tracks[-1].video[-1].length = self.tracks[-1].length |
---|
| 164 | elif data[:3] == ['Number', 'of', 'Angles:']: |
---|
| 165 | self.tracks[-1].angles = int(data[3]) |
---|
| 166 | self.tracks[-1].keys.append('angles') |
---|
| 167 | |
---|
| 168 | child.wait() |
---|
| 169 | child.fromchild.close() |
---|
| 170 | child.childerr.close() |
---|
| 171 | child.tochild.close() |
---|
| 172 | |
---|
| 173 | if len(self.tracks) > 0: |
---|
| 174 | for ti in self.tracks: |
---|
| 175 | ti.trackof = len(self.tracks) |
---|
| 176 | return 1 |
---|
| 177 | |
---|
| 178 | return 0 |
---|
| 179 | |
---|
| 180 | |
---|
| 181 | def isDVDdir(self, dirname): |
---|
| 182 | if os.path.isdir(dirname+'/VIDEO_TS') or \ |
---|
| 183 | os.path.isdir(dirname+'/video_ts') or \ |
---|
| 184 | os.path.isdir(dirname+'/Video_ts'): |
---|
| 185 | return self.lsdvd(dirname) |
---|
| 186 | return 0 |
---|
| 187 | |
---|
| 188 | |
---|
| 189 | def isDisc(self, device): |
---|
| 190 | if DiscInfo.isDisc(self, device) != 2: |
---|
| 191 | return 0 |
---|
| 192 | |
---|
| 193 | # brute force reading of the device to find out if it is a DVD |
---|
| 194 | f = open(device,'rb') |
---|
| 195 | f.seek(32768, 0) |
---|
| 196 | buffer = f.read(60000) |
---|
| 197 | |
---|
| 198 | if buffer.find('UDF') == -1: |
---|
| 199 | f.close() |
---|
| 200 | return 0 |
---|
| 201 | |
---|
| 202 | # seems to be a DVD, read a little bit more |
---|
| 203 | buffer += f.read(550000) |
---|
| 204 | f.close() |
---|
| 205 | |
---|
| 206 | if buffer.find('VIDEO_TS') == -1 and \ |
---|
| 207 | buffer.find('VIDEO_TS.IFO') == -1 and \ |
---|
| 208 | buffer.find('OSTA UDF Compliant') == -1: |
---|
| 209 | return 0 |
---|
| 210 | |
---|
| 211 | ret = self.lsdvd(device) |
---|
| 212 | if not ret: |
---|
| 213 | # we are very sure this is a DVD, maybe the drive was not |
---|
| 214 | # ready, let's try again |
---|
| 215 | return self.lsdvd(device) |
---|
| 216 | return 1 |
---|
| 217 | |
---|
| 218 | |
---|
| 219 | if os.environ.has_key('LSDVD') and os.environ['LSDVD']: |
---|
| 220 | LSDVD_EXE = os.environ['LSDVD'] |
---|
| 221 | else: |
---|
| 222 | for path in os.environ['PATH'].split(':'): |
---|
| 223 | if os.path.isfile(os.path.join(path, 'lsdvd')): |
---|
| 224 | LSDVD_EXE = os.path.join(path, 'lsdvd') |
---|
| 225 | break |
---|
| 226 | else: |
---|
| 227 | if mediainfo.DEBUG: |
---|
| 228 | print 'ImportError: lsdvd not found' |
---|
| 229 | raise ImportError |
---|
| 230 | |
---|
| 231 | mmpython.registertype( 'video/dvd', mediainfo.EXTENSION_DEVICE, |
---|
| 232 | mediainfo.TYPE_AV, DVDInfo ) |
---|
| 233 | mmpython.registertype( 'video/dvd', mediainfo.EXTENSION_DIRECTORY, |
---|
| 234 | mediainfo.TYPE_AV, DVDInfo ) |
---|