""" Media Info MetaData Plugin ========================== Sample class which can be used as a plugin for Peter's Flexible Renaming Kit (PFrank) Just copy the class definition (no need to copy the other declarations or test code) to your PFrankUser.py file and add a reference to the class in the PFrankUser.py 'UserRenamerList' table. Or just rename this file as PFrankUser.py and move it to your PFrank install folder if you don't want any other plugins. More details can be found in the PFrankUser.py.template file that is included with the PFrank download. April 19, 2008 Initial Creation 0.1 """ ########################################## # list your imported modules here. # If you get import errors, then the modules are not # present in the environment. In this case you will have to # insert the required module in the PFrank install folder. ########################################## # start of user imported modules import re import string import os # end of user imported modules. ########################################## # The following modules must always be imported ########################################## # start of required imported modules try : from SearchRep_init import configurator from SearchRepBase import SearchRepBase from SearchRepConstants import * except : print "importing initstubs" from SearchRep_initStubs import configurator from SearchRepBaseStubs import * # end of required imported modules class UserMediaMetaDataBase (SearchRepBase) : """ This is a sample renamer class. The SearchRepLower base class is mandatory. This is an example of taking the output of a third party program from the windows command line and using that output to rename a file/folder The application in this case is imageinfo which is used to extract meta-data from image files. imageinfo is useful because unlike the core PFrank program, it can extract meta data from raw image files. A sample output of mediainfo is: General #0 Complete name : C:\Documents and Settings\peter\Desktop\Mexico c.mov Format : QuickTime Format/Info : Quicktime movie Format/Family : MPEG-4 File size : 240 MiB PlayTime : 4mn 25s Bit rate : 7585 Kbps StreamSize/String : 76.5 KiB Encoded date : UTC 2007-12-03 06:14:09 Tagged date : UTC 2007-12-03 06:14:39 Video #0 Codec : MPEG-4 Visual Codec/Family : MPEG-4V PlayTime : 4mn 25s Bit rate mode : VBR Bit rate : 6745 Kbps Width : 640 pixels Height : 480 pixels Display Aspect ratio : 4/3 Frame rate mode : CFR Frame rate : 30.000 fps Bits/(Pixel*Frame) : 0.732 StreamSize/String : 213 MiB Encoded date : UTC 2007-12-03 06:10:54 Tagged date : UTC 2007-12-03 06:14:39 Audio #0 Codec : Lossless Codec/Info : Apple Lossless Format (similar to FLAC) PlayTime : 1mn 18s Bit rate mode : VBR Bit rate : 836 Kbps Channel(s) : 2 channels Sampling rate : 44.1 KHz Resolution : 16 bits StreamSize/String : 7.81 MiB Encoded date : UTC 2007-12-03 06:14:05 Tagged date : UTC 2007-12-03 06:14:39 Audio #1 Codec : Lossless Codec/Info : Apple Lossless Format (similar to FLAC) PlayTime : 1mn 18s Bit rate mode : VBR Bit rate : 836 Kbps Channel(s) : 2 channels Sampling rate : 44.1 KHz Resolution : 16 bits StreamSize/String : 7.81 MiB Encoded date : UTC 2007-12-03 06:14:06 Tagged date : UTC 2007-12-03 06:14:39 Audio #2 Codec : Lossless Codec/Info : Apple Lossless Format (similar to FLAC) PlayTime : 1mn 18s Bit rate mode : VBR Bit rate : 836 Kbps Channel(s) : 2 channels Sampling rate : 44.1 KHz Resolution : 16 bits StreamSize/String : 7.81 MiB Encoded date : UTC 2007-12-03 06:14:07 Tagged date : UTC 2007-12-03 06:14:39 Audio #3 Codec : Lossless Codec/Info : Apple Lossless Format (similar to FLAC) PlayTime : 30s Bit rate mode : VBRUserMediaMetaData Bit rate : 849 Kbps Channel(s) : 2 channels Sampling rate : 44.1 KHz Resolution : 16 bits StreamSize/String : 3.04 MiB Encoded date : UTC 2007-12-03 06:14:09 Tagged date : UTC 2007-12-03 06:14:39 or General #0 Complete name : D:\Movies\MyMovie.avi Format : AVI Format/Info : Audio Video Interleave Format/Family : RIFF File size : 651 MiB PlayTime : 1h 7mn Bit rate : 1352 Kbps StreamSize/String : 3.21 MiB Movie name : MyMovie Subject : MyMovie Writing application : Nandub v1.0rc2 Writing library : Nandub build 1852/release Video #0 Codec : DivX 5 Codec/Family : MPEG-4V Codec settings, Packet bitst : Yes Codec settings, BVOP : Yes Codec settings, QPel : No Codec settings, GMC : 2 Codec settings, Matrix : Default PlayTime : 1h 7mn Bit rate : 897 Kbps Width : 640 pixels Height : 256 pixels Display Aspect ratio : 2.500 Frame rate : 23.976 fps Resolution : 8 bits Interlacement : Progressive Bits/(Pixel*Frame) : 0.228 StreamSize/String : 432 MiB Writing library : DivX501b484p Audio #0 Codec : AC3 PlayTime : 1h 7mn Bit rate mode : CBR Bit rate : 448 Kbps Channel(s) : 6 channels Channel positions : Front: L C R, Rear: L R, LFE Sampling rate : 48.0 KHz Video0 delay : 57ms StreamSize/String : 216 MiB This particular plugin command will insert the 'Display Aspect Ratio' before the filename. This class can be used as a template for inserting other video meta data into the filename. TO USE THIS COMMAND, YOU MUST INSTALL THE IMAGEINFO UTILITY (command line version) BEFOREHAND. THEN IF NECESSARY MODIFY THE PATH TO 'ImageInfo' IN THE 'FIXNAMES' METHOD BELOW """ def __init__ (self) : # call base class initializer. This is mandatory SearchRepBase.__init__(self) # This is the ID string that will appear in the pre-defined command # pull down list. It will also appear in the custom list when # inserted. The id will also be used in the scan summary # The name 'self.idstring' must not change. Set the assigned string # on the right-hand side of the assignment statement to whatever # name you want. self.idstring = "Media Info Base Class String" # can use these extensions to do some filtering self.mediaextensions = [ ".avi", ".mpg", ".mov", ".qt" ] def initforscan(self, VerificationTest=False) : """ This function is called whenever a new scan or rescan is made. Use it to reset anything that needs to be reinitialized before a scan. This function is mandatory. The function is also called during verification of the class. Use the VerificationTest flag to block execution of anything you don't want executed before scanning. """ pass def getMediaInfo(self, mediadatalines): """ virtual function """ return "" def fixnames(self, filename, VerificationTest=False) : """ This function converts name of file to a new name. Input: string representing the old filename or portion (either All, Prefix, or Extension) The name does not include the path to the file. Output: change indicator True if change to input string occurred, otherwise False string representing the new filename When the old filename string is passed into this routine, then depending on where this user command is placed in the custom list, the string could be in the middle of a transformation. e.g. if the original filename was ABCDEF.jpg, and the first command of the custom list inserted a counter in front of the name, and the second command was this user command, then the filename string that is passed in could be something like 0010-ABCDEF.jpg. This name of course no longer looks like the original name. If you needed to extract meta data from the file, then you would not be able to use the passed in string. Therefore the global variable 'configurator.filename' is provided which has the full path to the filename. You can then just open that name, extract the meta data, and then close the file. The function is also called during verification of the class by PFrank. Use the VerificationTest flag to block execution of anything you don't want executed before scanning. TO USE THIS COMMAND, YOU MUST INSTALL THE imageinfo UTILITY BEFOREHAND. THEN IF NECESSARY MODIFY THE PATH TO imageinfo BELOW """ # set up default return parameters in advance newname = filename change = False # just return if a verification test if VerificationTest : return change, filename # full path to original filename fullPathForFile = configurator.filename ######################################################################### # the following extension check is commented out for now. ######################################################################### # check if this is a regular image file; if not then return # Have seen mediainfo loop forever processing a txt file basepath, filen = os.path.split(fullPathForFile) name, extension = os.path.splitext(filen) #print "extension is: ", extension if extension not in self.mediaextensions : return change, filename ######################################################################### # set up path to the mediainfo tool here. This may be different # for different users! ######################################################################### mediainfopath = os.path.join("C:\\Program Files\mediainfo", "mediainfo.exe") mediainfopath = os.path.normpath(mediainfopath) #print "exivpath is: ", exivpath, fullPathForFile ######################################################################### # Insert code to modify the filename here ######################################################################### # get the imageinfo data. Note that quotes must be placed around the command and # separately around the original filename and then another pair of quotes needs to be # placed around the entire command-file ... AAARRRRGGG! proceed = True try : inhandle, outhandle, errhandle = os.popen3(' \""%s" "%s"\" '%(mediainfopath, fullPathForFile )) except : proceed = False #print "proceed is: ", proceed if proceed : error = errhandle.read() if error != "" : if error.find("exception") != -1 or error.find("image") != -1 or error.find("Exif") != -1 : # don't care about imageinfo exceptions/errors since file might not be correct image type pass else : # some other problem - maybe file doesn't exist? print "Error '%s' detected for File: '%s'"%(error, fullPathForFile) else : # read output into a line array mediadata = outhandle.readlines() #print "media data is: ", mediadata data = self.getMediaInfo(mediadata) if data != "" : # valid data newname = data + '_' + filename else : # invalid time/date line; therefore no change newname= filename try : inhandle.close() outhandle.close() errhandle.close() except : pass ######################################################################### # these statements are mandatory ######################################################################### change = False if filename != newname : change = True return change, newname ######################################################################### # end of the class definition ######################################################################### class UserMediaMetaDataAspectRatio (UserMediaMetaDataBase) : """ This is a class used for extracting Aspect Ratio media info. This particular plugin command will insert the 'Display Aspect Ratio' before the filename. This class can be used as a template for inserting other video meta data into the filename. TO USE THIS COMMAND, YOU MUST INSTALL THE IMAGEINFO UTILITY (command line version) BEFOREHAND. THEN IF NECESSARY MODIFY THE PATH TO 'ImageInfo' IN THE 'FIXNAMES' METHOD IN THE UserMediaMetaDataBase CLASS """ def __init__ (self) : # call base class initializer. This is mandatory UserMediaMetaDataBase.__init__(self) # This is the ID string that will appear in the pre-defined command # pull down list. It will also appear in the custom list when # inserted. The id will also be used in the scan summary # The name 'self.idstring' must not change. Set the assigned string # on the right-hand side of the assignment statement to whatever # name you want. self.idstring = "(User) - Insert Aspect Ratio before " self.fraction = re.compile(r""" ([\-0-9L]+)/([0-9L]+) # get numerator and denominator for fraction """, re.VERBOSE) def getMediaInfo(self, mediadatalines): """ used to get the aspect ratio """ for line in mediadatalines : if line.find("Aspect") != -1 : break #print "line is: '%s'"%line words = line.split() if len(words) == 5 : # valid data aspect = words[4] if aspect.find('/') != -1 : num = str(self.fraction.sub(r'\1', aspect)) num = float(eval(num)) den = str(self.fraction.sub(r'\2', aspect)) den = float(eval(den)) aspect = "%.2f"%(float(num)/float(den)) return aspect else : return "" ######################################################################### # end of the class definition ######################################################################### class UserMediaMetaDataEncodedDate (UserMediaMetaDataBase) : """ This is a class used for extracting Aspect Ratio media info. This particular plugin command will insert the 'Display Aspect Ratio' before the filename. This class can be used as a template for inserting other video meta data into the filename. TO USE THIS COMMAND, YOU MUST INSTALL THE IMAGEINFO UTILITY (command line version) BEFOREHAND. THEN IF NECESSARY MODIFY THE PATH TO 'ImageInfo' IN THE 'FIXNAMES' METHOD IN THE UserMediaMetaDataBase CLASS """ def __init__ (self) : # call base class initializer. This is mandatory UserMediaMetaDataBase.__init__(self) # This is the ID string that will appear in the pre-defined command # pull down list. It will also appear in the custom list when # inserted. The id will also be used in the scan summary # The name 'self.idstring' must not change. Set the assigned string # on the right-hand side of the assignment statement to whatever # name you want. self.idstring = "(User) - Insert Encoded Date before " def getMediaInfo(self, mediadatalines): """ used to get the encoded date """ for line in mediadatalines : if line.find("Encoded date") != -1 : break # test code #line = "Encoded date : UTC 2007-12-03 06:14:09" #print "line is: '%s'"%line words = line.split() if len(words) == 6 : # valid data date = words[4] time = words[5] # remove ':' character since it is an invalid char in a file name time = time.replace(":", "") datetime = date + " " + time return datetime else : return "" ######################################################################### # end of the class definition ######################################################################### ################################################################### # Create the Renamer Objects in a list. The list is mandatory. # The name of the list must not change. # You can list as many names in the list as you like. PFrank will try to # load all of them. Each name in the list must correspond to the # name of a user command renaming class. A name can only appear # once in the list. # # User Command Objects are created with the following syntax: # ClassName() # i.e. the name of the class followed by () ################################################################### UserRenamerList = [ UserMediaMetaDataAspectRatio(), UserMediaMetaDataEncodedDate(), ] ###################################### # Code for testing the renamer objects ###################################### if __name__ == '__main__' : """ This is some test code to test the renaming objects. Test the code using the python IDLE tool to verify that things work. Then try importing the file to PFrank """ userpath = os.getcwd() userpath = os.path.normpath(userpath) print "\ ***NOTICE: Before first editing a user command file, you might need to \n\ associate an editor with .py files to prevent the unintentional execution \n\ of the user command file when trying to edit it.\n\ If the association is not made with an editor, the command window you\n\ are looking at now will shortly disappear!\n\ The user command file is called PFRankUser.py and is located at:\n\ %s\n\n\ After this association is made, you can delete this notice and the timed \n\ delay which follows.\n\ "%userpath import time time.sleep(1) print "Initializing Local Stubs" ################################################################### # Stubs for Local Testing. These are in addition to the # stubs imported earlier. # only perform this extra initialization for the create folder command # initialize current folder config option for testing Username3 #configurator.optionlist[SEARCHREP_COMMANDFILEDIRECTORY_ID] = os.getcwd() #print "current folder is: '%s'\n"%configurator.optionlist[SEARCHREP_COMMANDFILEDIRECTORY_ID] print "Testing User Defined Renaming Classes" # this is a table of sample filenames for testing # Assume that the files are in the current folder. nameTable = [ "D:\\Movies\\Fight Club (1999) CD1.avi", #"C:\\Program Files\\imageinfo\\test.jpg", "TestFile.txt", #"TestFile1.jpg", #"Test32File3.mp3", #"Test1.mp3", #"33T.ogg" ] # run all the initializers for renamer in UserRenamerList : renamer.initforscan() # run all name fixers for renamer in UserRenamerList : print "*************Testing Renamer: ", renamer.idstring for name in nameTable : fullpath = os.path.join(os.getcwd(), name) configurator.filename = os.path.normpath(fullpath) change, newname = renamer.fixnames(name) print "Oldname is: ", name print "Newname is: ", newname assert (change == (not name == newname)) print ""