9 from datetime
import datetime
10 from xml
.dom
import minidom
11 from xml
.parsers
import expat
18 from lrucache
import LRUCache
21 import plugins
.video
.transcode
25 TRIBUNE_CR
= ' Copyright Tribune Media Services, Inc.'
26 ROVI_CR
= ' Copyright Rovi, Inc.'
28 TV_RATINGS
= {'TV-Y7': 1, 'TV-Y': 2, 'TV-G': 3, 'TV-PG': 4, 'TV-14': 5,
29 'TV-MA': 6, 'TV-NR': 7, 'TVY7': 1, 'TVY': 2, 'TVG': 3,
30 'TVPG': 4, 'TV14': 5, 'TVMA': 6, 'TVNR': 7, 'Y7': 1,
31 'Y': 2, 'G': 3, 'PG': 4, '14': 5, 'MA': 6, 'NR': 7,
32 'UNRATED': 7, 'X1': 1, 'X2': 2, 'X3': 3, 'X4': 4, 'X5': 5,
35 MPAA_RATINGS
= {'G': 1, 'PG': 2, 'PG-13': 3, 'PG13': 3, 'R': 4, 'X': 5,
36 'NC-17': 6, 'NC17': 6, 'NR': 8, 'UNRATED': 8, 'G1': 1,
37 'P2': 2, 'P3': 3, 'R4': 4, 'X5': 5, 'N6': 6, 'N8': 8}
39 STAR_RATINGS
= {'1': 1, '1.5': 2, '2': 3, '2.5': 4, '3': 5, '3.5': 6,
40 '4': 7, '*': 1, '**': 3, '***': 5, '****': 7, 'X1': 1,
41 'X2': 2, 'X3': 3, 'X4': 4, 'X5': 5, 'X6': 6, 'X7': 7}
43 HUMAN
= {'mpaaRating': {1: 'G', 2: 'PG', 3: 'PG-13', 4: 'R', 5: 'X',
45 'tvRating': {1: 'Y7', 2: 'Y', 3: 'G', 4: 'PG', 5: '14',
47 'starRating': {1: '1', 2: '1.5', 3: '2', 4: '2.5', 5: '3',
49 'colorCode': {1: 'B & W', 2: 'COLOR AND B & W',
50 3: 'COLORIZED', 4: 'COLOR'}
59 tivo_cache
= LRUCache(50)
60 mp4_cache
= LRUCache(50)
61 dvrms_cache
= LRUCache(50)
62 nfo_cache
= LRUCache(50)
64 mswindows
= (sys
.platform
== "win32")
67 return HUMAN
['mpaaRating'].get(rating
, 'NR')
70 return HUMAN
['tvRating'].get(rating
, 'NR')
72 def get_stars(rating
):
73 return HUMAN
['starRating'].get(rating
, '')
76 return HUMAN
['colorCode'].get(value
, 'COLOR')
81 tsize
= '%.2f GB' % (raw
/ GB
)
83 tsize
= '%.2f MB' % (raw
/ MB
)
85 tsize
= '%.2f KB' % (raw
/ KB
)
87 tsize
= '%d Bytes' % raw
90 def tag_data(element
, tag
):
91 for name
in tag
.split('/'):
93 for new_element
in element
.childNodes
:
94 if new_element
.nodeName
== name
:
100 if not element
.firstChild
:
102 return element
.firstChild
.data
104 def _vtag_data(element
, tag
):
105 for name
in tag
.split('/'):
106 new_element
= element
.getElementsByTagName(name
)
109 element
= new_element
[0]
110 elements
= element
.getElementsByTagName('element')
111 return [x
.firstChild
.data
for x
in elements
if x
.firstChild
]
113 def _vtag_data_alternate(element
, tag
):
115 for name
in tag
.split('/'):
117 for elmt
in elements
:
118 new_elements
+= elmt
.getElementsByTagName(name
)
119 elements
= new_elements
120 return [x
.firstChild
.data
for x
in elements
if x
.firstChild
]
122 def _tag_value(element
, tag
):
123 item
= element
.getElementsByTagName(tag
)
125 value
= item
[0].attributes
['value'].value
128 def from_moov(full_path
):
129 if full_path
in mp4_cache
:
130 return mp4_cache
[full_path
]
136 mp4meta
= mutagen
.File(unicode(full_path
, 'utf-8'))
139 mp4_cache
[full_path
] = {}
142 # The following 1-to-1 correspondence of atoms to pyTivo
143 # variables is TV-biased
144 keys
= {'tvnn': 'callsign',
145 'tvsh': 'seriesTitle'}
147 if 'stik' in mp4meta
:
148 isTVShow
= (mp4meta
['stik'] == mutagen
.mp4
.MediaKind
.TV_SHOW
)
150 isTVShow
= 'tvsh' in mp4meta
151 for key
, value
in mp4meta
.items():
152 if type(value
) == list:
155 metadata
[keys
[key
]] = value
157 #could be programId (EP, SH, or MV) or "SnEn"
158 if value
.startswith('SH'):
159 metadata
['isEpisode'] = 'false'
160 elif value
.startswith('MV') or value
.startswith('EP'):
161 metadata
['isEpisode'] = 'true'
162 metadata
['programId'] = value
163 elif key
.startswith('S') and key
.count('E') == 1:
164 epstart
= key
.find('E')
165 seasonstr
= key
[1:epstart
]
166 episodestr
= key
[epstart
+1:]
167 if (seasonstr
.isdigit() and episodestr
.isdigit()):
168 if len(episodestr
) < 2:
169 episodestr
= '0' + episodestr
170 metadata
['episodeNumber'] = seasonstr
+episodestr
172 #put together tvsn and tves to make episodeNumber
175 if 'tves' in mp4meta
:
176 tvesValue
= mp4meta
['tves']
177 if type(tvesValue
) == list:
178 tvesValue
= tvesValue
[0]
179 tves
= str(tvesValue
)
182 metadata
['episodeNumber'] = tvsn
+tves
183 # These keys begin with the copyright symbol \xA9
184 elif key
== '\xa9day':
187 value
+= '-01-01T16:00:00Z'
188 metadata
['originalAirDate'] = value
191 metadata
['movieYear'] = value
[:4]
192 #metadata['time'] = value
193 elif key
in ['\xa9gen', 'gnre']:
194 for k
in ('vProgramGenre', 'vSeriesGenre'):
196 metadata
[k
].append(value
)
198 metadata
[k
] = [value
]
199 elif key
== '\xa9nam':
201 metadata
['episodeTitle'] = value
203 metadata
['title'] = value
205 # Description in desc, cmt, and/or ldes tags. Keep the longest.
206 elif key
in ['desc', '\xa9cmt', 'ldes'] and len(value
) > len_desc
:
207 metadata
['description'] = value
208 len_desc
= len(value
)
210 # A common custom "reverse DNS format" tag
211 elif (key
== '----:com.apple.iTunes:iTunEXTC' and
212 ('us-tv' in value
or 'mpaa' in value
)):
213 rating
= value
.split("|")[1].upper()
214 if rating
in TV_RATINGS
and 'us-tv' in value
:
215 metadata
['tvRating'] = TV_RATINGS
[rating
]
216 elif rating
in MPAA_RATINGS
and 'mpaa' in value
:
217 metadata
['mpaaRating'] = MPAA_RATINGS
[rating
]
219 # Actors, directors, producers, AND screenwriters may be in a long
220 # embedded XML plist.
221 elif (key
== '----:com.apple.iTunes:iTunMOVI' and
222 'plistlib' in sys
.modules
):
223 items
= {'cast': 'vActor', 'directors': 'vDirector',
224 'producers': 'vProducer', 'screenwriters': 'vWriter'}
226 data
= plistlib
.readPlistFromString(value
)
232 metadata
[items
[item
]] = [x
['name'] for x
in data
[item
]]
233 elif (key
== '----:com.pyTivo.pyTivo:tiVoINFO' and
234 'plistlib' in sys
.modules
):
236 data
= plistlib
.readPlistFromString(value
)
241 metadata
[item
] = data
[item
]
243 mp4_cache
[full_path
] = metadata
246 def from_mscore(rawmeta
):
248 keys
= {'title': ['Title'],
249 'description': ['Description', 'WM/SubTitleDescription'],
250 'episodeTitle': ['WM/SubTitle'],
251 'callsign': ['WM/MediaStationCallSign'],
252 'displayMajorNumber': ['WM/MediaOriginalChannel'],
253 'originalAirDate': ['WM/MediaOriginalBroadcastDateTime'],
254 'rating': ['WM/ParentalRating'],
255 'credits': ['WM/MediaCredits'], 'genre': ['WM/Genre']}
258 for tag
in keys
[tagname
]:
261 value
= rawmeta
[tag
][0]
262 if type(value
) not in (str, unicode):
265 metadata
[tagname
] = value
269 if 'episodeTitle' in metadata
and 'title' in metadata
:
270 metadata
['seriesTitle'] = metadata
['title']
271 if 'genre' in metadata
:
272 value
= metadata
['genre'].split(',')
273 metadata
['vProgramGenre'] = value
274 metadata
['vSeriesGenre'] = value
275 del metadata
['genre']
276 if 'credits' in metadata
:
277 value
= [x
.split('/') for x
in metadata
['credits'].split(';')]
279 metadata
['vActor'] = [x
for x
in (value
[0] + value
[3]) if x
]
280 metadata
['vDirector'] = [x
for x
in value
[1] if x
]
281 del metadata
['credits']
282 if 'rating' in metadata
:
283 rating
= metadata
['rating']
284 if rating
in TV_RATINGS
:
285 metadata
['tvRating'] = TV_RATINGS
[rating
]
286 del metadata
['rating']
290 def from_dvrms(full_path
):
291 if full_path
in dvrms_cache
:
292 return dvrms_cache
[full_path
]
295 rawmeta
= mutagen
.File(unicode(full_path
, 'utf-8'))
298 dvrms_cache
[full_path
] = {}
301 metadata
= from_mscore(rawmeta
)
302 dvrms_cache
[full_path
] = metadata
305 def from_eyetv(full_path
):
306 keys
= {'TITLE': 'title', 'SUBTITLE': 'episodeTitle',
307 'DESCRIPTION': 'description', 'YEAR': 'movieYear',
308 'EPISODENUM': 'episodeNumber'}
310 path
= os
.path
.dirname(unicode(full_path
, 'utf-8'))
311 eyetvp
= [x
for x
in os
.listdir(path
) if x
.endswith('.eyetvp')][0]
312 eyetvp
= os
.path
.join(path
, eyetvp
)
314 eyetv
= plistlib
.readPlist(eyetvp
)
317 if 'epg info' in eyetv
:
318 info
= eyetv
['epg info']
321 metadata
[keys
[key
]] = info
[key
]
323 metadata
['seriesTitle'] = info
['TITLE']
325 metadata
['vActor'] = [x
.strip() for x
in info
['ACTORS'].split(',')]
327 metadata
['vDirector'] = [info
['DIRECTOR']]
329 for ptag
, etag
, ratings
in [('tvRating', 'TV_RATING', TV_RATINGS
),
330 ('mpaaRating', 'MPAA_RATING', MPAA_RATINGS
),
331 ('starRating', 'STAR_RATING', STAR_RATINGS
)]:
332 x
= info
[etag
].upper()
333 if x
and x
in ratings
:
334 metadata
[ptag
] = ratings
[x
]
336 # movieYear must be set for the mpaa/star ratings to work
337 if (('mpaaRating' in metadata
or 'starRating' in metadata
) and
338 'movieYear' not in metadata
):
339 metadata
['movieYear'] = eyetv
['info']['start'].year
342 def from_text(full_path
):
344 full_path
= unicode(full_path
, 'utf-8')
345 path
, name
= os
.path
.split(full_path
)
346 title
, ext
= os
.path
.splitext(name
)
351 parent
= os
.path
.dirname(ptmp
)
356 search_paths
.append(os
.path
.join(ptmp
, 'default.txt'))
358 search_paths
.append(os
.path
.join(path
, title
) + '.properties')
359 search_paths
.reverse()
361 search_paths
+= [full_path
+ '.txt',
362 os
.path
.join(path
, '.meta', 'default.txt'),
363 os
.path
.join(path
, '.meta', name
) + '.txt']
365 for metafile
in search_paths
:
366 if os
.path
.exists(metafile
):
367 sep
= ':='[metafile
.endswith('.properties')]
368 for line
in file(metafile
, 'U'):
369 if line
.startswith(BOM
):
371 if line
.strip().startswith('#') or not sep
in line
:
373 key
, value
= [x
.strip() for x
in line
.split(sep
, 1)]
374 if not key
or not value
:
376 if key
.startswith('v'):
378 metadata
[key
].append(value
)
380 metadata
[key
] = [value
]
382 metadata
[key
] = value
384 for rating
, ratings
in [('tvRating', TV_RATINGS
),
385 ('mpaaRating', MPAA_RATINGS
),
386 ('starRating', STAR_RATINGS
)]:
387 x
= metadata
.get(rating
, '').upper()
389 metadata
[rating
] = ratings
[x
]
399 def basic(full_path
, mtime
=None):
400 base_path
, name
= os
.path
.split(full_path
)
401 title
, ext
= os
.path
.splitext(name
)
403 mtime
= os
.path
.getmtime(unicode(full_path
, 'utf-8'))
405 originalAirDate
= datetime
.utcfromtimestamp(mtime
)
407 originalAirDate
= datetime
.utcnow()
409 metadata
= {'title': title
,
410 'originalAirDate': originalAirDate
.isoformat()}
412 if ext
in ['.mp4', '.m4v', '.mov']:
413 metadata
.update(from_moov(full_path
))
414 elif ext
in ['.dvr-ms', '.asf', '.wmv']:
415 metadata
.update(from_dvrms(full_path
))
416 elif 'plistlib' in sys
.modules
and base_path
.endswith('.eyetv'):
417 metadata
.update(from_eyetv(full_path
))
418 metadata
.update(from_nfo(full_path
))
419 metadata
.update(from_text(full_path
))
423 def from_container(xmldoc
):
426 keys
= {'title': 'Title', 'episodeTitle': 'EpisodeTitle',
427 'description': 'Description', 'programId': 'ProgramId',
428 'seriesId': 'SeriesId', 'episodeNumber': 'EpisodeNumber',
429 'tvRating': 'TvRating', 'displayMajorNumber': 'SourceChannel',
430 'callsign': 'SourceStation', 'showingBits': 'ShowingBits',
431 'mpaaRating': 'MpaaRating'}
433 details
= xmldoc
.getElementsByTagName('Details')[0]
436 data
= tag_data(details
, keys
[key
])
438 if key
== 'description':
439 data
= data
.replace(TRIBUNE_CR
, '').replace(ROVI_CR
, '')
440 if data
.endswith(' *'):
442 elif key
== 'tvRating':
444 elif key
== 'displayMajorNumber':
446 data
, metadata
['displayMinorNumber'] = data
.split('-')
451 def from_details(xml
):
454 xmldoc
= minidom
.parseString(xml
)
455 showing
= xmldoc
.getElementsByTagName('showing')[0]
456 program
= showing
.getElementsByTagName('program')[0]
458 items
= {'description': 'program/description',
459 'title': 'program/title',
460 'episodeTitle': 'program/episodeTitle',
461 'episodeNumber': 'program/episodeNumber',
462 'programId': 'program/uniqueId',
463 'seriesId': 'program/series/uniqueId',
464 'seriesTitle': 'program/series/seriesTitle',
465 'originalAirDate': 'program/originalAirDate',
466 'isEpisode': 'program/isEpisode',
467 'movieYear': 'program/movieYear',
468 'partCount': 'partCount',
469 'partIndex': 'partIndex',
473 data
= tag_data(showing
, items
[item
])
475 if item
== 'description':
476 data
= data
.replace(TRIBUNE_CR
, '').replace(ROVI_CR
, '')
477 if data
.endswith(' *'):
479 metadata
[item
] = data
481 vItems
= ['vActor', 'vChoreographer', 'vDirector',
482 'vExecProducer', 'vProgramGenre', 'vGuestStar',
483 'vHost', 'vProducer', 'vWriter']
486 data
= _vtag_data(program
, item
)
488 metadata
[item
] = data
490 sb
= showing
.getElementsByTagName('showingBits')
492 metadata
['showingBits'] = sb
[0].attributes
['value'].value
494 #for tag in ['starRating', 'mpaaRating', 'colorCode']:
495 for tag
in ['starRating', 'mpaaRating']:
496 value
= _tag_value(program
, tag
)
498 metadata
[tag
] = value
500 rating
= _tag_value(showing
, 'tvRating')
502 metadata
['tvRating'] = rating
506 def _nfo_vitems(source
, metadata
):
508 vItems
= {'vGenre': 'genre',
509 'vWriter': 'credits',
510 'vDirector': 'director',
511 'vActor': 'actor/name'}
514 data
= _vtag_data_alternate(source
, vItems
[key
])
516 metadata
.setdefault(key
, [])
518 if not dat
in metadata
[key
]:
519 metadata
[key
].append(dat
)
521 if 'vGenre' in metadata
:
522 metadata
['vSeriesGenre'] = metadata
['vProgramGenre'] = metadata
['vGenre']
526 def _parse_nfo(nfo_path
, nfo_data
=None):
527 # nfo files can contain XML or a URL to seed the XBMC metadata scrapers
528 # It's also possible to have both (a URL after the XML metadata)
529 # pyTivo only parses the XML metadata, but we'll try to stip the URL
530 # from mixed XML/URL files. Returns `None` when XML can't be parsed.
532 nfo_data
= [line
.strip() for line
in file(nfo_path
, 'rU')]
535 xmldoc
= minidom
.parseString(os
.linesep
.join(nfo_data
))
536 except expat
.ExpatError
, err
:
537 if expat
.ErrorString(err
.code
) == expat
.errors
.XML_ERROR_INVALID_TOKEN
:
538 # might be a URL outside the xml
539 while len(nfo_data
) > err
.lineno
:
540 if len(nfo_data
[-1]) == 0:
544 if len(nfo_data
) == err
.lineno
:
545 # last non-blank line contains the error
547 return _parse_nfo(nfo_path
, nfo_data
)
550 def _from_tvshow_nfo(tvshow_nfo_path
):
551 if tvshow_nfo_path
in nfo_cache
:
552 return nfo_cache
[tvshow_nfo_path
]
554 items
= {'description': 'plot',
556 'seriesTitle': 'showtitle',
557 'starRating': 'rating',
560 nfo_cache
[tvshow_nfo_path
] = metadata
= {}
562 xmldoc
= _parse_nfo(tvshow_nfo_path
)
566 tvshow
= xmldoc
.getElementsByTagName('tvshow')
573 data
= tag_data(tvshow
, items
[item
])
575 metadata
[item
] = data
577 metadata
= _nfo_vitems(tvshow
, metadata
)
579 nfo_cache
[tvshow_nfo_path
] = metadata
582 def _from_episode_nfo(nfo_path
, xmldoc
):
585 items
= {'description': 'plot',
586 'episodeTitle': 'title',
587 'seriesTitle': 'showtitle',
588 'originalAirDate': 'aired',
589 'starRating': 'rating',
595 basepath
= os
.path
.dirname(path
)
599 tv_nfo
= os
.path
.join(path
, 'tvshow.nfo')
600 if os
.path
.exists(tv_nfo
):
601 metadata
.update(_from_tvshow_nfo(tv_nfo
))
604 episode
= xmldoc
.getElementsByTagName('episodedetails')
610 metadata
['isEpisode'] = 'true'
612 data
= tag_data(episode
, items
[item
])
614 metadata
[item
] = data
616 season
= tag_data(episode
, 'displayseason')
617 if not season
or season
== "-1":
618 season
= tag_data(episode
, 'season')
622 ep_num
= tag_data(episode
, 'displayepisode')
623 if not ep_num
or ep_num
== "-1":
624 ep_num
= tag_data(episode
, 'episode')
625 if ep_num
and ep_num
!= "-1":
626 metadata
['episodeNumber'] = "%d%02d" % (int(season
), int(ep_num
))
628 if 'originalAirDate' in metadata
:
629 metadata
['originalAirDate'] += 'T00:00:00Z'
631 metadata
= _nfo_vitems(episode
, metadata
)
635 def _from_movie_nfo(xmldoc
):
638 movie
= xmldoc
.getElementsByTagName('movie')
644 items
= {'description': 'plot',
647 'starRating': 'rating',
648 'mpaaRating': 'mpaa'}
650 metadata
['isEpisode'] = 'false'
653 data
= tag_data(movie
, items
[item
])
655 metadata
[item
] = data
657 metadata
['movieYear'] = "%04d" % int(metadata
.get('movieYear', 0))
659 metadata
= _nfo_vitems(movie
, metadata
)
662 def from_nfo(full_path
):
663 if full_path
in nfo_cache
:
664 return nfo_cache
[full_path
]
666 metadata
= nfo_cache
[full_path
] = {}
668 nfo_path
= "%s.nfo" % os
.path
.splitext(full_path
)[0]
669 if not os
.path
.exists(nfo_path
):
672 xmldoc
= _parse_nfo(nfo_path
)
676 if xmldoc
.getElementsByTagName('episodedetails'):
678 metadata
.update(_from_episode_nfo(nfo_path
, xmldoc
))
679 elif xmldoc
.getElementsByTagName('movie'):
681 metadata
.update(_from_movie_nfo(xmldoc
))
684 if 'starRating' in metadata
:
685 # .NFO 0-10 -> TiVo 1-7
686 rating
= int(float(metadata
['starRating']) * 6 / 10 + 1.5)
687 metadata
['starRating'] = rating
689 for key
, mapping
in [('mpaaRating', MPAA_RATINGS
),
690 ('tvRating', TV_RATINGS
)]:
692 rating
= mapping
.get(metadata
[key
], None)
694 metadata
[key
] = rating
698 nfo_cache
[full_path
] = metadata
701 def _tdcat_bin(tdcat_path
, full_path
, tivo_mak
):
702 fname
= unicode(full_path
, 'utf-8')
704 fname
= fname
.encode('cp1252')
705 tcmd
= [tdcat_path
, '-m', tivo_mak
, '-2', fname
]
706 tdcat
= subprocess
.Popen(tcmd
, stdout
=subprocess
.PIPE
)
707 return tdcat
.stdout
.read()
709 def _tdcat_py(full_path
, tivo_mak
):
712 tfile
= open(full_path
, 'rb')
713 header
= tfile
.read(16)
714 offset
, chunks
= struct
.unpack('>LH', header
[10:])
715 rawdata
= tfile
.read(offset
- 16)
719 for i
in xrange(chunks
):
720 chunk_size
, data_size
, id, enc
= struct
.unpack('>LLHH',
721 rawdata
[count
:count
+ 12])
723 data
= rawdata
[count
:count
+ data_size
]
724 xml_data
[id] = {'enc': enc
, 'data': data
, 'start': count
+ 16}
725 count
+= chunk_size
- 12
728 details
= chunk
['data']
730 xml_key
= xml_data
[3]['data']
732 hexmak
= hashlib
.md5('tivo:TiVo DVR:' + tivo_mak
).hexdigest()
733 key
= hashlib
.sha1(hexmak
+ xml_key
).digest()[:16] + '\0\0\0\0'
735 turkey
= hashlib
.sha1(key
[:17]).digest()
736 turiv
= hashlib
.sha1(key
).digest()
738 details
= turing
.Turing(turkey
, turiv
).crypt(details
, chunk
['start'])
742 def from_tivo(full_path
):
743 if full_path
in tivo_cache
:
744 return tivo_cache
[full_path
]
746 tdcat_path
= config
.get_bin('tdcat')
747 tivo_mak
= config
.get_server('tivo_mak')
751 details
= _tdcat_bin(tdcat_path
, full_path
, tivo_mak
)
753 details
= _tdcat_py(full_path
, tivo_mak
)
754 metadata
= from_details(details
)
755 tivo_cache
[full_path
] = metadata
761 def force_utf8(text
):
762 if type(text
) == str:
764 text
= text
.decode('utf8')
766 if sys
.platform
== 'darwin':
767 text
= text
.decode('macroman')
769 text
= text
.decode('cp1252')
770 return text
.encode('utf-8')
772 def dump(output
, metadata
):
774 value
= metadata
[key
]
775 if type(value
) == list:
777 output
.write('%s: %s\n' % (key
, item
.encode('utf-8')))
779 if key
in HUMAN
and value
in HUMAN
[key
]:
780 output
.write('%s: %s\n' % (key
, HUMAN
[key
][value
]))
782 output
.write('%s: %s\n' % (key
, value
.encode('utf-8')))
784 if __name__
== '__main__':
785 if len(sys
.argv
) > 1:
788 logging
.basicConfig()
789 fname
= force_utf8(sys
.argv
[1])
790 ext
= os
.path
.splitext(fname
)[1].lower()
792 metadata
.update(from_tivo(fname
))
793 elif ext
in ['.mp4', '.m4v', '.mov']:
794 metadata
.update(from_moov(fname
))
795 elif ext
in ['.dvr-ms', '.asf', '.wmv']:
796 metadata
.update(from_dvrms(fname
))
798 vInfo
= plugins
.video
.transcode
.video_info(fname
)
799 metadata
.update(from_mscore(vInfo
['rawmeta']))
800 dump(sys
.stdout
, metadata
)