1
2
3
4
5
6
7
8
9
10 "Returns information about files"
11
12 import os
13 import struct
14
15 import common
16
17 import midifile
18 import wma_file
19 import mp4_file
20
29
31 """Wraps a file object letting you get various parts without exceptions"""
32
33 READAHEAD=1024
34
44
54
59
64
69
74
76 v=self.GetBytes(offset,1)
77 if v is None: return v
78 return ord(v)
79
81 """
82 Wraps a string object letting you get various parts w/o exceptions.
83 Mainly used by the com_* modules as part of writing media to the phone.
84 """
86 if isinstance(string, str):
87 self.file=None
88 self.data=string
89 self.size=len(string)
90 else:
91 self.size=-1
92 self.__class__=FailedFile
93
97
99 "Wraps information about an image file"
100
101
102 attrnames=("width", "height", "format", "bpp", "size", "MAXSIZE")
103
105 for a in self.attrnames:
106 setattr(self, a, None)
107 self.mimetypes=[]
108 self.size=f.size
109 self.__dict__.update(kwds)
110
112 v=getattr(self, "_shortdescription", None)
113 if v is not None:
114 return v(self)
115 res=[]
116 if self.width is not None and self.height is not None:
117 res.append( "%d x %d" % (self.width, self.height) )
118 if self.format is not None:
119 res.append( self.format)
120 if self.bpp is not None:
121 res.append( "%d bpp" % (self.bpp,))
122
123 if len(res):
124 return " ".join(res)
125 return "Unknown format"
126
128 v=getattr(self, "_longdescription", None)
129 if v is not None:
130 return v(self)
131 return self.shortdescription()
132
151
153 "Long description for BMP"
154 res=[ifi.shortdescription()]
155 if ifi.compression==0:
156 res.append("No compression")
157 elif ifi.compression==1:
158 res.append("8 bit run length encoding")
159 elif ifi.compression==2:
160 res.append("4 bit run length encoding")
161 elif ifi.compression==3:
162 res.append("RGB bitmap with mask")
163 else:
164 res.append("Unknown compression "+`ifi.compression`)
165 if ifi.ncolours:
166 res.append("%d colours" % (ifi.ncolours,))
167 if ifi.nimportantcolours:
168 res[-1]=res[-1]+(" (%d important)" % (ifi.nimportantcolours,))
169 return "\n".join(res)
170
172 "Identify a PNG"
173 if f.GetBytes(0,8)=="\x89PNG\r\n\x1a\n" and f.GetBytes(12,4)=="IHDR":
174 d={'format': "PNG"}
175 d['width']=f.GetMSBUint32(16)
176 d['height']=f.GetMSBUint32(20)
177 d['bitdepth']=f.GetByte(24)
178 d['colourtype']=f.GetByte(25)
179 d['compression']=f.GetByte(26)
180 d['filter']=f.GetByte(27)
181 d['interlace']=f.GetByte(28)
182 d['_shortdescription']=fmts_PNG
183 d['_longdescription']=fmt_PNG
184 d['mimetypes']=['image/png', 'image/x-png']
185 for i in d.itervalues():
186 if i is None: return None
187 ifi=ImgFileInfo(f,**d)
188 return ifi
189 return None
190
192 res=[]
193 res.append( "%d x %d" % (ifi.width, ifi.height) )
194 res.append( ifi.format)
195 if ifi.colourtype in (0,4):
196 res.append("%d bit grayscale" % (ifi.bitdepth,))
197 elif ifi.colourtype in (2,6):
198 res.append("truecolour (%d bit)" % (ifi.bitdepth*3,))
199 elif ifi.colourtype==3:
200 res.append("%d colours" % (2**ifi.bitdepth,))
201 if not short and ifi.colourtype in (4,6):
202 res.append("with transparency")
203 return " ".join(res)
204
206 "Long description for PNG"
207 res=[fmts_PNG(ifi, False)]
208
209 if ifi.compression==0:
210 res.append("Deflate compressed")
211 else:
212 res.append("Unknown compression "+`ifi.compression`)
213
214 if ifi.filter==0:
215 res.append("Adaptive filtering")
216 else:
217 res.append("Unknown filtering "+`ifi.filter`)
218
219 if ifi.interlace==0:
220 res.append("No interlacing")
221 elif ifi.interlace==1:
222 res.append("Adam7 interlacing")
223 else:
224 res.append("Unknown interlacing "+`ifi.interlace`)
225 return "\n".join(res)
226
228 "Identify a Brew Compressed Image"
229 if f.GetBytes(0,4)=="BCI\x00":
230 d={'format': "BCI"}
231 d['width']=f.GetLSBUint16(0x0e)
232 d['height']=f.GetLSBUint16(0x10)
233 d['bpp']=8
234 d['ncolours']=f.GetLSBUint16(0x1a)
235 d['_longdescription']=fmt_BCI
236 d['mimetypes']=['image/x-brewcompressedimage']
237 for i in d.itervalues():
238 if i is None: return None
239 ifi=ImgFileInfo(f,**d)
240 return ifi
241 return None
242
244 "Long description for BCI"
245 res=[ifi.shortdescription()]
246 res.append("%d colour palette" % (ifi.ncolours,))
247 return "\n".join(res)
248
281
283 res=[]
284 res.append( "%d x %d" % (ifi.width, ifi.height) )
285 res.append( ifi.format)
286 if ifi.components==1:
287 res.append("(greyscale)")
288 elif ifi.components==3:
289 res.append("(RGB)")
290 elif ifi.components==4:
291 res.append("(CMYK)")
292 else:
293 res.append("Unknown components "+`ifi.components`)
294 return " ".join(res)
295
297 "Identify a GIF image"
298 if f.GetBytes(0, 3)!='GIF':
299
300 return None
301 d={ 'format': 'GIF' }
302 d['version']=f.GetBytes(3, 3)
303 d['width']=f.GetLSBUint16(6)
304 d['height']=f.GetLSBUint16(8)
305 d['_shortdescription']=fmts_GIF
306 d['mimetypes']=['image/gif', 'image/x-gif']
307 ofs=13
308 i=f.GetByte(10)
309 if (i&0x80):
310
311 bpp=(i&0x7)+1
312 d['bpp']=bpp
313 ofs+=3*(2**bpp)
314
315 i=f.GetByte(ofs)
316 if i!=0x2c:
317
318 if d['version']=='89a' and i==0x21:
319
320 return ImgFileInfo(f, **d)
321 else:
322
323 return None
324
325 d['width']=f.GetLSBUint16(ofs+5)
326 d['height']=f.GetLSBUint16(ofs+7)
327 i=f.GetByte(ofs+9)
328 if (i&0x80):
329 d['bpp']=(i&0xf)+1
330 return ImgFileInfo(f, **d)
331
333 res=[]
334 res.append( "%d x %d" % (ifi.width, ifi.height) )
335 res.append( '%s%s'%(ifi.format, ifi.version))
336 if ifi.bpp is not None:
337 res.append( '%d BPP'%ifi.bpp)
338 return ' '.join(res)
339
354
356 res=['%d x %d' % (ifi.width, ifi.height)]
357 res.append('%.1fs video'%ifi.duration)
358 return ' '.join(res)
359
361 "Identify a LGBIT image (LG phone proprietary image format)"
362
363
364
365 width=f.GetLSBUint16(0x00)
366 height=f.GetLSBUint16(0x02)
367 if width is None or height is None:
368 return None
369 if f.size==(width*height*2+4):
370 d={'format': "LGBIT"}
371 d['width']=width
372 d['height']=height
373 d['bpp']=16
374 d['_shortdescription']=fmts_LGBIT
375 for i in d.itervalues():
376 if i is None: return None
377 ifi=ImgFileInfo(f,**d)
378 return ifi
379 return None
380
382 res=[]
383 res.append( "%d x %d" % (ifi.width, ifi.height) )
384 res.append( '%s'%(ifi.format))
385 if ifi.bpp is not None:
386 res.append( '%d BPP'%ifi.bpp)
387 return ' '.join(res)
388
390 "Identify a 3GPP2(3g2) file"
391 if f.GetBytes(4, 8)!='ftyp3g2a':
392
393 return None
394 d={ 'format': '3GPP2',
395 'mimetypes': ['video/3gpp2'] }
396 return ImgFileInfo(f, **d)
397
398 imageids=[globals()[f] for f in dir() if f.startswith("idimg_")]
408
416
418 "Wraps information about an audio file"
419
420
421 attrnames=("format", "size", "duration", "MAXSIZE")
422
424 for a in self.attrnames:
425 setattr(self, a, None)
426 self.mimetypes=[]
427 self.size=f.size
428 self.__dict__.update(kwds)
429
431 v=getattr(self, "_shortdescription", None)
432 if v is not None:
433 return v(self)
434 res=[]
435 if self.format is not None:
436 res.append( self.format)
437 if self.duration is not None:
438 res.append( "%d seconds" % (self.duration,))
439
440 if len(res):
441 return " ".join(res)
442 return "Unknown format"
443
445 v=getattr(self, "_longdescription", None)
446 if v is not None:
447 return v(self)
448 return self.shortdescription()
449
451 "Identify a midi file"
452
453
454
455
456
457 m=midifile.MIDIFile(f)
458 if not m.valid:
459 return None
460 d={'format': "MIDI"}
461 d['type']=m.type
462 d['numtracks']=m.num_tracks
463 d['duration']=m.duration
464 d['_shortdescription']=fmts_MIDI
465 d['mimetypes']=['audio/x-midi', 'audio/midi']
466 for i in d.itervalues():
467 if i is None: return None
468 afi=AudioFileInfo(f,**d)
469 return afi
470
480
482 assert length>0
483 return (value>>(start-length+1)) & ((2**length)-1)
484
488
492
493
494 twooheightzeros="\x00"*208
495
497
498 try:
499 idv3present=False
500 id3v1present=False
501
502 header=f.GetMSBUint32(0)
503
504
505 if header==0 and f.data.startswith(twooheightzeros):
506 offset=208
507
508 elif header==0x49443303:
509 sz=[f.GetByte(x) for x in range(6,10)]
510 if len([zz for zz in sz if zz<0 or zz>=0x80]):
511 return None
512 sz=(sz[0]<<21)+(sz[1]<<14)+(sz[2]<<7)+sz[3]
513 offset=10+sz
514 idv3present=True
515 header=f.GetMSBUint32(offset)
516 elif header is None:
517 return None
518 else:
519 offset=0
520
521
522 while True:
523 v=f.GetMSBUint16(offset)
524 if v is None: return None
525 if v&0xffe0==0xffe0:
526 break
527 offset=f.data.find("\xff", offset+1)
528 if offset<0:
529
530 return None
531
532 frames=[]
533 while offset<f.size:
534 if offset==f.size-128 and f.GetBytes(offset,3)=="TAG":
535 offset+=128
536 id3v1present=True
537 continue
538 frame=MP3Frame(f, offset)
539 if not frame.OK or frame.nextoffset>f.size: break
540 offset=frame.nextoffset
541 frames.append(frame)
542
543 if len(frames)==0: return
544
545 if offset!=f.size:
546 print "MP3 offset is",offset,"size is",f.size
547
548
549 f0=frames[0]
550 d={'format': 'MP3',
551 'id3v1present': id3v1present,
552 'idv3present': idv3present,
553 'unrecognisedframes': offset!=f.size,
554 'version': f0.version,
555 'layer': f0.layer,
556 'bitrate': f0.bitrate,
557 'samplerate': f0.samplerate,
558 'channels': f0.channels,
559 'copyright': f0.copyright,
560 'original': f0.original,
561 'mimetypes': ['audio/x-mp3', 'audio/mpeg', 'audio/x-mpeg']}
562
563 duration=f0.duration
564 vbrmin=vbrmax=f0.bitrate
565
566 for frame in frames[1:]:
567 duration+=frame.duration
568 if frame.bitrate!=f0.bitrate:
569 d['bitrate']=0
570 if frame.samplerate!=f0.samplerate:
571 d['samplerate']=0
572 vbrmin=min(frame.bitrate,vbrmin)
573 vbrmax=max(frame.bitrate,vbrmax)
574 if frame.channels!=f0.channels:
575 d['channels']=0
576
577 d['duration']=duration
578 d['vbrmin']=vbrmin
579 d['vbrmax']=vbrmax
580 d['_longdescription']=fmt_MP3
581 d['_shortdescription']=fmts_MP3
582
583 if returnframes:
584 d['frames']=frames
585
586 return AudioFileInfo(f, **d)
587 except:
588 return None
589
591 res=[]
592 res.append("MP3 (Mpeg Version %d Layer %d)" % (afi.version, afi.layer))
593 res.append("%s %.1f Khz %0.1f seconds" % (["Variable!!", "Mono", "Stereo"][afi.channels], afi.samplerate/1000.0, afi.duration,))
594 if afi.bitrate:
595 res.append(`afi.bitrate`+" kbps")
596 else:
597 res.append("VBR (min %d kbps, max %d kbps)" % (afi.vbrmin, afi.vbrmax))
598 if afi.unrecognisedframes:
599 res.append("There are unrecognised frames in this file")
600 if afi.idv3present:
601 res.append("IDV3 tag present at begining of file")
602 if afi.id3v1present:
603 res.append("IDV3.1 tag present at end of file")
604 if afi.copyright:
605 res.append("Marked as copyrighted")
606 if afi.original:
607 res.append("Marked as the original")
608
609 return "\n".join(res)
610
612 return "MP3 %s %dKhz %d sec" % (["Variable!!", "Mono", "Stereo"][afi.channels], afi.samplerate/1000.0, afi.duration,)
613
614
616
617 bitrates={
618
619 (1, 1): [None, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, None],
620 (1, 2): [None, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, None],
621 (1, 3): [None, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, None],
622 (2, 1): [None, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, None],
623 (2, 2): [None, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, None],
624 (2, 3): [None, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, None],
625 }
626
627 samplerates={
628 1: [44100, 48000, 32000, None],
629 2: [22050, 24000, 16000, None]
630 }
631
633 self.OK=False
634 header=f.GetMSBUint32(offset)
635 if header is None: return
636
637 if _getbits(31,11, header)!=2047:
638 return
639 self.header=header
640
641 version=_getbits(20,2,header)
642 if version not in (2,3):
643 return
644 if version==3:
645 version=1
646 self.version=version
647
648 layer=_getbits(18,2,header)
649 if layer==0: return
650 if layer==1:
651 self.layer=3
652 elif layer==2:
653 self.layer=2
654 elif layer==3:
655 self.layer=1
656 self.crc=_getbits(16,1,header)
657 self.bitrate=self.bitrates[(self.version, self.layer)][_getbits(15,4,header)]
658 self.samplerate=self.samplerates[self.version][_getbits(11,2,header)]
659 self.padding=_getbits(9,1,header)
660 if self.layer==1:
661 self.framelength=(12000*self.bitrate/self.samplerate+self.padding)*4
662 else:
663 if self.version==1:
664 self.framelength=144000*self.bitrate/self.samplerate+self.padding
665 else:
666 self.framelength=72000*self.bitrate/self.samplerate+self.padding
667 self.duration=self.framelength*8*1.0/(self.bitrate*1000)
668 self.private=_getbits(8,1,header)
669 self.channelmode=_getbits(7,2,header)
670 if self.channelmode in (0,1,2):
671 self.channels=2
672 else:
673 self.channels=1
674
675 self.modeextenstion=_getbits(5,2,header)
676 self.copyright=_getbits(3,1,header)
677 self.original=_getbits(2,1, header)
678 self.emphasis=_getbits(1,2, header)
679
680 self.offset=offset
681 self.nextoffset=offset+self.framelength
682 self.OK=True
683
685 "Identify a Qualcomm Purevoice file"
686
687
688
689 if f.GetBytes(0,4)=="RIFF" and f.GetBytes(8,4)=="QLCM":
690 d={'format': "QCP"}
691
692
693 if f.GetBytes(12,4)!="fmt ":
694 return None
695
696 d['qcpmajor']=f.GetByte(20)
697 d['qcpminor']=f.GetByte(21)
698
699 d['codecguid']=(f.GetLSBUint32(22), f.GetLSBUint16(26), f.GetLSBUint16(28), f.GetMSBUint16(30), (long(f.GetMSBUint16(32))<<32)+f.GetMSBUint32(34))
700 d['codecversion']=f.GetLSBUint16(38)
701 name=f.GetBytes(40,80)
702 zero=name.find('\x00')
703 if zero>=0:
704 name=name[:zero]
705 d['codecname']=name
706 d['averagebps']=f.GetLSBUint16(120)
707
708
709 d['samplingrate']=f.GetLSBUint16(126)
710 d['samplesize']=f.GetLSBUint16(128)
711 d['_longdescription']=fmt_QCP
712
713 if d['codecguid']==( 0x5e7f6d41, 0xb115, 0x11d0, 0xba91, 0x00805fb4b97eL ) or \
714 d['codecguid']==( 0x5e7f6d42, 0xb115, 0x11d0, 0xba91, 0x00805fb4b97eL ):
715 d['mimetypes']=['audio/qcelp']
716 elif d['codecguid']==( 0xe689d48dL, 0x9076, 0x46b5, 0x91ef, 0x736a5100ceb4L ):
717 d['mimetypes']=['audio/evrc-qcp']
718 elif d['codecguid']==( 0x8d7c2b75L, 0xa797, 0xed49, 0x985e, 0xd53c8cc75f84L ):
719 d['mimetypes']=['audio/smv-qcp']
720
721 for i in d.itervalues():
722 if i is None: return None
723 afi=AudioFileInfo(f,**d)
724 return afi
725 return None
726
728 res=["QCP %s" % (afi.codecname,)]
729 res.append("%d bps %d Hz %d bits/sample" % (afi.averagebps, afi.samplingrate, afi.samplesize))
730 codecguid=afi.codecguid
731 if codecguid==( 0x5e7f6d41, 0xb115, 0x11d0, 0xba91, 0x00805fb4b97eL ):
732 res.append("QCELP-13K V"+`afi.codecversion` + " (guid 1)")
733 elif codecguid==( 0x5e7f6d42, 0xb115, 0x11d0, 0xba91, 0x00805fb4b97eL ):
734 res.append("QCELP-13K V"+`afi.codecversion` + " (guid 2)")
735 elif codecguid==( 0xe689d48dL, 0x9076, 0x46b5, 0x91ef, 0x736a5100ceb4L ):
736 res.append("EVRC V"+`afi.codecversion`)
737 elif codecguid==( 0x8d7c2b75L, 0xa797, 0xed49, 0x985e, 0xd53c8cc75f84L ):
738 res.append("SMV V"+`afi.codecversion`)
739 else:
740 res.append("Codec Guid {%08X-%04X-%04X-%04X-%012X} V%d" % (afi.codecguid+(afi.codecversion,)))
741 res.append("QCP File Version %d.%d" % (afi.qcpmajor, afi.qcpminor))
742
743 return "\n".join(res)
744
746 "Identify a PMD/CMX file"
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761 try:
762 if f.GetBytes(0, 4)!='cmid':
763 return None
764 d={ 'format': 'PMD' }
765 hdr_len=f.GetMSBUint16(8)
766 i=f.GetByte(10)
767 d['content']=['Unknown', 'Ringer', 'Pictures&Audio'][i]
768 d['numtracks']=f.GetByte(12)
769 ofs=13
770 ofs_end=hdr_len+10
771 while ofs<ofs_end:
772 s=f.GetBytes(ofs, 4)
773 i=f.GetMSBUint16(ofs+4)
774 ofs+=6
775 if i==0:
776 continue
777 if s=='vers':
778 d['version']=f.GetBytes(ofs, i)
779 elif s=='titl':
780 d['title']=f.GetBytes(ofs, i)
781 elif s=='cnts':
782 d['media']=f.GetBytes(ofs, i)
783 ofs+=i
784 d['_longdescription']=fmt_PMD
785 d['_shortdescription']=fmts_PMD
786 return AudioFileInfo(f, **d)
787 except:
788 return None
789
791 return 'PMD/CMF %s'% afi.content
792
794 res=['PMD/CMF']
795 if hasattr(afi, 'version'):
796 res.append('Version: '+afi.version)
797 res.append('Content: '+afi.content)
798 res.append('%d Tracks'%afi.numtracks)
799 if hasattr(afi, 'title'):
800 res.append('Title: '+afi.title)
801 if hasattr(afi, 'media'):
802 res.append('Media: '+afi.media)
803 return '\n'.join(res)
804
828
831
833 res=['PCM/WAV']
834 res.append('%d KHz %d bits %s'%\
835 (afi.samplerate/1000, afi.bitspersample,\
836 ['None', 'Mono', 'Stereo'][afi.numchannels]))
837 return '\n'.join(res)
838
842
843
845 "Identify a WMA file"
846 try:
847 _wma=wma_file.WMA_File(f)
848 if not _wma.valid:
849 return None
850 d={ 'format': 'WMA',
851 'mimetypes': ['audio/x-ms-wma'],
852 'duration': _wma.play_duration,
853 'title': _wma.title,
854 'artist': _wma.author,
855 'album': _wma.album,
856 'genre': _wma.genre,
857 '_longdescription': fmt_WMA,
858 }
859 return AudioFileInfo(f, **d)
860 except:
861 if __debug__:
862 raise
863
865 res=['WMA', 'Duration: %.2f'%afi.duration ]
866 if afi.title:
867 res.append('Title: %s'%afi.title)
868 if afi.artist:
869 res.append('Artist: %s'%afi.artist)
870 if afi.album:
871 res.append('Album: %s'%afi.album)
872 if afi.genre:
873 res.append('Genre: %s'%afi.genre)
874 return '\n'.join(res)
875
876
878 "Identify a MP4 file"
879 try:
880 _mp4=mp4_file.MP4_File(f)
881 if not _mp4.valid:
882 return None
883 d={ 'format': 'MP4',
884 'mimetypes': ['audio/mpeg4'],
885 'duration': _mp4.duration,
886 'title': _mp4.title,
887 'artist': _mp4.artist,
888 'album': _mp4.album,
889 'bitrate': _mp4.bitrate,
890 'samplerate': _mp4.samplerate,
891 '_longdescription': fmt_MP4,
892 }
893 return AudioFileInfo(f, **d)
894 except:
895 if __debug__:
896 raise
897
898
900 res=[]
901 res.append("MPEG4")
902 res.append("%.1f Khz %0.1f seconds" % (afi.samplerate/1000.0, afi.duration,))
903 if afi.bitrate:
904 res.append(`afi.bitrate`+" kbps")
905 if afi.title:
906 res.append('Title: %s'%afi.title)
907 if afi.artist:
908 res.append('Artist: %s'%afi.artist)
909 if afi.album:
910 res.append('Album: %s'%afi.album)
911
912 return "\n".join(res)
913
914 audioids=[globals()[f] for f in dir() if f.startswith("idaudio_")]
924
932
933
934
935
936 thefileinfocache=common.FileCache(lowwater=100,hiwater=140)
937