Ticket #2734: metadata_script_improvements_against_trunk_07-03-03.patch

File metadata_script_improvements_against_trunk_07-03-03.patch, 31.4 KB (added by visit0r, 17 years ago)

Changes against trunk (note: scripts are NOT tested with MythVideo? on trunk, only with 0.20).

  • mythvideo/mythvideo/scripts/find_meta.py

    old new  
    55directory with multiple video files. Perform movie data lookups using the
    66imdbpy.py script.
    77
    8 Written by Pekka JÀÀskelÀinen (gmail: pekka.jaaskelainen) 2006.
     8Written by Pekka JÀÀskelÀinen (gmail: pekka.jaaskelainen) 2006-2007.
    99
    1010The metadata is searched with following steps:
    1111       
     
    2626In case it's a video directory (a directory with 2 or more video files for the same title,
    2727or a DVD directory) the file name is video.metadata.
    2828
    29 Includes an interactive mode which makes the script ask user for input in case there are
    30 titles for which IMDb queries cannot be done unambiguously.
     29A crontab entry for the script to insert automatically data of new video files to
     30MythDB every hour:
    3131
     320 * * * * find_meta.py -r /videos
     33
     34In which /videos is the root of your MythVideo files.
    3235""" 
    3336
    3437import sys
     
    3841import glob
    3942import fileinput
    4043import imdbpy
     44import shlex
     45import socket
     46import urllib
     47
    4148try:
    42         # If loaded, get neater input prompt for interactive questions.
    43         import readline
     49        # If found, we can insert data directly to MythDB
     50        import MySQLdb 
     51        db_support = True
    4452except:
    45         pass
     53        print "MySQLdb (python-mysqldb) not installed, MythDB importing disabled."
     54        db_support = False
    4655
    4756from stat import *
    4857
    4958verbose=False
    50 overwrite=False
     59
    5160interactive=False
    5261recursive=False
     62dbimport=False
     63
     64# Import metadata from .metadata files if found.
     65import_from_files=False
     66
     67# Overwrite the old metadata if found.
     68overwrite=False
     69
     70# Create the *.metadata files.
     71metafiles=False
    5372
    54 videoExtensions = ["avi", "mpg", "wmv"]
     73videoExtensions = ["avi", "mpg", "wmv", "mkv"]
    5574
    5675# The file name for storing metadata for videos that occupy the whole
    5776# directory and might consist of multiple video files for the same
     
    6079
    6180# Directories to skip in the recursive scan (matched to the rightmost
    6281# part of the directory).
    63 skipDirs = ["/Sample", "/Sub"]
     82skipDirs = ["/Sample", "/Sub", "/VIDEO_TS"]
     83
     84# The directory to store the poster files in (this will be fetched from the
     85# MythTV settings table).
     86poster_dir = "./"
    6487
    6588def print_verbose(string):
    6689        global verbose
     
    6891                print string
    6992        return
    7093
     94db = None
     95def init_db():
     96        global db
     97        try:
     98                config = shlex.shlex(open(os.path.expanduser('~/.mythtv/mysql.txt')))
     99        except:
     100                print "Error opening ~/.mythtv/mysql.txt"
     101                return False
     102               
     103       
     104        token = config.get_token()
     105        db_host = db_user = db_password = None
     106        while  token != config.eof and (db_host == None or db_user == None or db_password == None):
     107                if token == "DBHostName":
     108                        if config.get_token() == "=":
     109                                db_host = config.get_token()
     110                elif token == "DBUserName":
     111                        if config.get_token() == "=":
     112                                db_user = config.get_token()
     113                elif token == "DBPassword":
     114                        if config.get_token() == "=":
     115                                db_password = config.get_token()
     116                                                               
     117                token = config.get_token()
     118        db = MySQLdb.connect(user=db_user, host=db_host, passwd=db_password,
     119                db="mythconverg")
     120        print_verbose("Database connection successful.")
     121        return True
     122       
    71123def find_imdb_id_from_text_file(textFile):
    72124       
    73125        if os.access(textFile, os.R_OK):
     
    98150                return path[0: suffix_pos]
    99151        else:
    100152                return path
    101        
    102        
     153               
    103154def cleanup_title(title):
    104155        title = title.replace("_", " ").replace(".", " ")
    105         cut_point_strings = ["hdtv", "xvid", "dvd", "proper"]
     156        cut_point_strings = ["hdtv", "xvid", "dvd", "proper", "720p"]
    106157        lowest_cutpoint = len(title)
    107158        for string in cut_point_strings:
    108159                pos = title.lower().rfind(string)
     
    112163        title = title[0:lowest_cutpoint]
    113164        return title.strip()
    114165
    115 def save_metadata(fileName, metadata):
     166def get_genre_id(genre_name):
     167        """
     168        Find the id of the given genre from MythDB.
     169       
     170        If the genre does not exist, insert it and return its id.
     171        """
     172        global db
     173        c = db.cursor()
     174        c.execute("SELECT intid FROM videocategory WHERE lower(category) = %s", (genre_name,))
     175        row = c.fetchone()
     176        c.close()
     177       
     178        if row is not None:
     179                return row[0]
     180       
     181        # Insert a new genre.
     182        c = db.cursor()
     183        c.execute("INSERT INTO videocategory(category) VALUES (%s)", (genre_name.capitalize(),))
     184        newid = c.lastrowid
     185        c.close()
     186       
     187        return newid
     188       
     189def parse_meta(variable, oldvalue, emptyvalue="", meta=""):
     190        """
     191        Parses a single metadata from a metadata string (returned by the imdbpy.py, etc.).
     192       
     193        variable is the metadata to find
     194        oldvalue if we are replacing an old value for the metadata
     195        emptyvalue what is considered an empty value for the metadata (e.g. "None", "")
     196        meta the metadata string
     197        """
     198        global overwrite
     199        if not overwrite and oldvalue is not None and oldvalue != emptyvalue:
     200                return oldvalue
     201        for line in meta.split("\n"):
     202                beginning = variable + ":"
     203                if line.startswith(beginning):
     204                        return line[len(beginning):].strip()
     205        return None
     206
     207def detect_disc_number(allfiles, file):
     208        """
     209        Detect the number of disc of the video file considering all the discs
     210        that are part of the title.
     211       
     212        Returns None if cannot detect the disc number.
     213        """
     214       
     215        if len(allfiles) < 2 or file not in allfiles:
     216                return None
     217       
     218        # Ensure all filenames are of same length (assume there are no
     219        # more than 9 discs per title). And they are not all the same.
     220       
     221        size = len(allfiles[0])
     222        differing = False       
     223        for f in allfiles:
     224                if len(f) != size:
     225                        return None
     226                if f != file:
     227                        differing = True
     228       
     229        if not differing:
     230                return None
     231       
     232        # Find the range of chars of the string which are not equal
     233        # for all the files.
     234        # For example:
     235        # "Rambo III disc 1.avi"
     236        # "Rambo III disc 2.avi"
     237        #  ===============!====
     238
     239        startpos = 0
     240        for startpos in range(len(file)):
     241                c = allfiles[0][startpos]
     242                allequal = True
     243                for f in allfiles:
     244                        if f[startpos] != c:
     245                                allequal = False
     246                                break
     247                if not allequal:
     248                        # Found the starting index.
     249                        break
     250               
     251        endpos = len(file) - 1
     252        for endpos in reversed(range(len(file))):
     253                c = allfiles[0][endpos]
     254                allequal = True
     255                for f in allfiles:
     256                        if f[endpos] != c:
     257                                allequal = False
     258                                break
     259                if not allequal:
     260                        # Found the ending index.
     261                        break
     262        endpos = endpos + 1
     263       
     264        # Now the disc number can be found.
     265        disc_str = file[startpos:endpos].lower()
     266               
     267        disc = -1
     268        try:
     269                disc = int(disc_str)
     270        except:
     271                # It was not an integer. Maybe an alphabetic?
     272                alpha = ["a", "b", "c", "d", "e"]
     273                for i in range(len(alpha)):
     274                        if alpha[i] == disc_str:
     275                                return i + 1 # a == disc 1, b == disc 2, ...
     276       
     277        if disc == -1:
     278                return None
     279        else:
     280                return disc
     281       
     282
     283def save_metadata_to_mythdb(videopath, metadata):
     284        """
     285        Updates the given metadata for the given video path.
     286       
     287        Detects if the given title is a dvd-rip dir with multiple videos and
     288        adds metadata for all the videos separately, and chains the videos
     289        together.
     290        """
     291        files_str = parse_meta("Files", "", "", metadata)
     292        if files_str is not None:
     293                files = files_str.split(",")
     294               
     295                if len(files) > 1:
     296                        # It's a dvd-rip dir.
     297                       
     298                        # Set the first video's childid to -1, which denotes no childs.
     299                        child = -1
     300                        # Add the last file in chain first so we can set it as a child
     301                        # to the next one, etc.
     302                        for file in reversed(files):
     303                                child = save_video_metadata_to_mythdb(
     304                                        videopath + "/" + file, metadata,
     305                                        child, disc = detect_disc_number(files, file))
     306                        return
     307                       
     308        return save_video_metadata_to_mythdb(videopath, metadata)
     309       
     310       
     311def mythvideo_metadata_id(videopath):
     312        """
     313        Finds the MythVideo metadata id for the given video path from the MythDB, if any.
     314       
     315        Returns None if no metadata was found.
     316        """
     317        global db
     318        c = db.cursor()
     319        c.execute("""
     320                SELECT intid
     321                FROM videometadata
     322                WHERE filename = %s""", (videopath,))
     323        row = c.fetchone()
     324        c.close()
     325       
     326        if row is not None:
     327                return row[0]
     328        else:
     329                return None
     330       
     331def mythtv_setting(value, hostname = '%'):
     332        """
     333        Returns the value for the given MythTV setting.
     334       
     335        Returns None if the settings was not found. If multiple rows are
     336        found (multiple hostnames), returns the value of the first one.
     337        """
     338        global db
     339        c = db.cursor()
     340        c.execute("""
     341                SELECT data
     342                FROM settings
     343                WHERE value LIKE(%s) AND hostname LIKE(%s) LIMIT 1""",
     344                (value, hostname))
     345        row = c.fetchone()
     346        c.close()
     347       
     348        if row is not None:
     349                return row[0]
     350        else:
     351                return None
     352       
     353       
     354def save_video_metadata_to_mythdb(videopath, metadata, child=-1, disc=None):
     355        """
     356        Updates the given metadata for the given video file.
     357       
     358        child can be set to the id of the child video (video to be played after this one).
     359        disc can be set to the disc number in case of multifile title (the disc number
     360             is appended to the title string to enable differentiating the titles in
     361             file browse mode.
     362       
     363        Returns the id of the inserted metadata.
     364        """
     365        global overwrite, db, poster_dir
     366       
     367        # Drop the trailing '/' from the path
     368        if videopath.endswith('/'):
     369                videopath = videopath[0:-1]
     370        videopath = os.path.abspath(videopath)
     371       
     372        print_verbose("Inserting metadata to MythDB for %s." % videopath)
     373               
     374        (intid, title, category, director, plot, rating, inetref, year,
     375         userrating, length, filename, coverfile, childid, playcommand) = \
     376                 (None, None, 0, None, None, None, None,
     377                  0.0, None, 0, None, None, child, "")
     378                 
     379        intid = mythvideo_metadata_id(videopath)
     380        if intid is not None:
     381                if not overwrite:               
     382                        print_verbose("Metadata already exist in MythDB, not overwriting it.")
     383                        return None                     
     384        else:
     385                print_verbose("No metadata in MythDB, creating a new one.")
     386                # Create a new empty entry at this point so we can use the common UPDATE code
     387                # to actually insert the data.
     388                c = db.cursor()
     389                c.execute("""INSERT INTO videometadata(filename) VALUES(%s)""", (videopath,))
     390                intid = c.lastrowid
     391                c.close()
     392       
     393                       
     394        def parse_metadata(variable, oldvalue, emptyvalue="", meta=metadata):
     395                return parse_meta(variable, oldvalue, emptyvalue, meta)
     396                       
     397        title = parse_metadata('Title', title)
     398       
     399        if disc is not None:
     400                title += " (disc " + str(disc) + ")"
     401               
     402        year = parse_metadata('Year', year, 0)
     403       
     404        if title is None or year is None:
     405                return
     406       
     407       
     408        director = parse_metadata('Director', director, 'Unknown')
     409       
     410        if director == None:
     411                director = "Unknown"
     412       
     413        plot = parse_metadata('Plot', plot, "None")
     414        userrating = parse_metadata('UserRating', userrating, 0.0)
     415                       
     416        try:
     417                float(userrating)
     418        except:
     419                userrating = 0.0
     420                       
     421        rating = parse_metadata('MovieRating', rating, "Unknown")
     422       
     423        if rating is None:
     424                rating = "Unknown"
     425               
     426        length = parse_metadata('Runtime', length, 0)   
     427        try:
     428                length = length.split(",")[0]
     429                length = int(length)
     430        except:
     431                length = 0
     432               
     433        inetref = parse_metadata('IMDb', inetref, '00000000')
     434       
     435        if inetref == None:
     436                inetref = '00000000'
     437               
     438        filename = videopath
     439                               
     440        genrestring = parse_metadata('Genres', "", "")
     441        genres = []
     442        if genrestring is not None and len(genrestring) > 0:
     443                genres = genrestring.split(",")
     444       
     445        if len(genres) < 1:
     446                print_verbose("No genres.")
     447                return
     448        else:
     449                # Only one genre supported?
     450                category = get_genre_id(genres[0])
     451               
     452        coverfile = find_poster_image(inetref)
     453       
     454        if coverfile == None:
     455                coverfile = "No cover"
     456        else:
     457                # TODO: should enter only the filename to allow reusing
     458                # the same cover file from multiple hosts where the
     459                # poster image directory is mounted to different directories.
     460                # This needs to be fixed in MythVideo first.
     461                coverfile = poster_dir + "/" + coverfile               
     462               
     463        c = db.cursor()
     464        c.execute("""
     465                UPDATE videometadata
     466                SET showlevel = 1, browse = 1, childid = %s, playcommand = %s, title = %s,
     467                    director = %s, plot = %s, rating = %s, inetref = %s, category = %s,
     468                    year = %s, userrating = %s, length = %s, filename = %s, coverfile = %s
     469                WHERE intid = %s""",
     470                (childid, playcommand, title, director, plot, rating, inetref, category,
     471                 year, userrating, length, filename, coverfile, intid))
     472        c.close()
     473        return intid
     474                               
     475def find_poster_image(imdb_id):
     476        """
     477        Tries to find a poster image for the given IMDb id.
     478       
     479        First looks if the image already exist, if not, tries to fetch it using
     480        the imdbpy.py. Returns None in case a poster image couldn't be found,
     481        otherwise returns the base name of the poster image file.
     482        """
     483        global poster_dir
     484        image_extensions = ["png", "jpg", "bmp"]
     485       
     486        poster_files = []
     487        for ext in image_extensions:
     488                poster_files += glob.glob("%s/%s.%s" % (poster_dir, imdb_id, ext))
     489       
     490        if len(poster_files) == 0:
     491                # Try to fetch the poster image from the web.
     492                poster_url = imdbpy.find_poster_url(imdb_id)
     493                if poster_url is None:
     494                        return None             
     495                print_verbose("Found poster at '%s', downloading it..." % poster_url)
     496                filename = poster_url.split("/")[-1]
     497                (name, extension) = os.path.splitext(filename)
     498                local_filename = poster_dir + "/" + imdb_id + extension
     499                urllib.urlretrieve(poster_url, local_filename)
     500                poster_files.append(local_filename)
     501        else:
     502                print_verbose("Found existing cover image.")
     503       
     504        coverfile = None
     505        if len(poster_files) > 0:
     506                # TODO: if multiple poster images available, pick the one with largest
     507                # dimensions.
     508                # Now just pick the first found.               
     509                coverfile = os.path.basename(poster_files[0])
     510               
     511        return coverfile
     512               
     513               
     514def save_metadata_to_file(fileName, metadata):
     515        global overwrite
    116516               
    117517        if os.path.exists(fileName) and not overwrite:
    118518                print_verbose("Metadata already exists, not overwriting.")
     519                return
    119520       
    120521        if metadata is not None:
    121522                print_verbose("Writing metadata to '%s'" % fileName)
    122523                f = open(fileName, 'w')
    123524                f.write(metadata.encode("utf8"))
    124525                f.close()
    125                 print_verbose("Metadata:")
    126                 print_verbose(metadata)
    127 
     526               
     527def save_metadata(videopath, metadata_filename, metadata):
     528        """
     529        Saves metadata for the given video path to the given metadata_filename.
     530       
     531        Metadata should be a single string.
     532        """
     533        global dbimport, metafiles
     534       
     535        print_verbose("Metadata:")
     536        print_verbose(metadata)
     537       
     538        if metafiles:
     539                save_metadata_to_file(metadata_filename, metadata)
     540        if dbimport:
     541                save_metadata_to_mythdb(videopath, metadata)
    128542
    129543def find_metadata_for_video_path(pathName):
    130        
    131544        global interactive
    132545       
    133546        fileName = os.path.basename(pathName)
     
    146559                        dirName + "/imdb.url",
    147560                        dirName + "/" + file_body + ".nfo",
    148561                        dirName + "/" + file_body + ".imdb"]
    149                        
    150562                               
    151        
    152         # Add rest of the .nfos to the list of the scanned ones.
     563        # Add rest of the .nfos to the end of the list.
    153564        for nfo in glob.glob(dirName + "/*.nfo"):
    154565                if nfo not in nfos:
    155566                        nfos.append(nfo)               
     
    164575                       
    165576        if imdb_id is None:
    166577                # A title search
    167                 title = cleanup_title(title)
     578                title = unicode(cleanup_title(title), "utf8", "ignore")
    168579               
    169580                print_verbose("Title search '%s'" % title)             
    170581               
    171                 candidates = imdbpy.title_search(title.decode("utf8"))
     582                candidates = imdbpy.title_search(title)
    172583                if candidates is None or len(candidates) == 0:                 
    173                         # Try with the dirname
     584                        # TODO: Try with the dirname
    174585                        pass
    175586               
    176587                if candidates is not None and len(candidates) > 0:
    177588                       
    178589                        index = 0
    179590                        if len(candidates) > 1:
     591
     592                                print "Got multiple candidates for title search '%s'. " % title
     593                                print "Use the '-a' switch to choose the correct one."
     594                                for candidate in candidates:
     595                                        print "%s) %s (%d)" % (candidate[0], candidate[1], candidate[2])
     596                                               
    180597                                if interactive:
    181                                         print "Got multiple candidates for the title search '%s'. " % title
    182                                         print "Which one is the correct title?"
    183                                         for i in range(len(candidates)):
    184                                                 print "%d) %s (%d)" % (i, candidates[i][1], candidates[i][2])
    185                                         print "N) None of the above"
    186598                                        answer = raw_input("?)")
    187599                                        if answer is None or len(answer) == 0:
    188                                                 return [None, None]
     600                                                return None
    189601                                       
    190                                         if answer.lower() == "n":
    191                                                 return [None, None]
    192                                         else:
    193                                                 try:
    194                                                         ans = int(answer)
    195                                                         if 0 <= ans < len(candidates):
    196                                                                 index = ans
    197                                                         else:
    198                                                                 return [None, None]
    199                                                 except:
    200                                                         print_verbose("Illegal index.")
    201                                                         return [None, None]
    202                                         print_verbose("Chose %d" % index)
     602                                        print_verbose("Chose %s" % answer)
     603                                        imdb_id = answer
    203604                                else:
    204                                         print "Multiple candidates found for the movie and not in interactive mode."
    205                                         return [None, None]
    206                        
    207                         imdb_id = candidates[index][0]
     605                                        return None
     606                        else:
     607                                imdb_id = candidates[0][0]             
    208608                else:
    209609                        print "Couldn't find IMDb ID for '%s'" % pathName
    210                         return [None, None]
     610                        return None
    211611                       
    212612        print_verbose("Querying IMDb for meta data for ID %s..." % imdb_id)
    213613        metadata = imdbpy.metadata_search(imdb_id)
    214614        if metadata is not None:
    215615                metadata += "IMDb:%s" % imdb_id + "\n"
    216         return [source_nfo, metadata]
    217        
    218         # Save the metadata
     616        return metadata
    219617
    220618def video_file_list_metadata(videoPaths):       
    221619        videoPaths = [os.path.basename(v) for v in videoPaths]         
     
    223621                               
    224622        return "Files:%s" % (",".join(videoPaths)) + "\n"
    225623
     624def load_metadata_file(metadata_filename):
     625        """
     626        Loads a metadata file if found, returns None otherwise.
     627        """
     628        metadata = None
     629        try:
     630                f = open(metadata_filename)
     631                metadata = "".join(f.readlines())
     632                f.close()
     633        except:
     634                pass
     635        return metadata
     636
    226637def detect_dvd_backup(dirName):
    227638        """
    228639        If the given directory is detected as a directory with a dvd backup, meta data is
    229640        searched for the directory title name (in addition to the directory-wide imdb files).
    230641        """
     642        global import_from_files
    231643        videoTs = dirName + "/VIDEO_TS"
    232644        if not (os.path.exists(videoTs) and os.path.isdir(videoTs)):
    233645                return False
    234646       
    235         print_verbose('A DVD backup directory (with DVD directory structure) detected.')
     647        print_verbose('A DVD backup directory with DVD directory structure detected.')
    236648       
    237649        metadata_target = dirName + "/" + dirMetadataFileName
    238650       
    239         if os.path.exists(metadata_target) and not overwrite:
    240                 print_verbose("Metadata already exists, not overwriting.")
    241                 return True             
    242        
    243         [src_nfo, metadata] = find_metadata_for_video_path(dirName + "/VIDEO_TS")
     651        metadata = None
     652        if import_from_files:
     653                metadata = load_metadata_file(metadata_target)
     654               
     655        if metadata is None:
     656                if should_be_skipped(dirName, metadata_target):
     657                        return True
     658                metadata = find_metadata_for_video_path(dirName + "/VIDEO_TS")
     659               
    244660        if metadata is not None:
    245                 save_metadata(metadata_target, metadata)               
     661                save_metadata(dirName, metadata_target, metadata)               
    246662        return True
    247663       
    248        
    249 
    250664def detect_compressed_dvd_backup_dir(dirName):
    251665        """
    252666        If the given directory is detected as a directory with one or more files of
    253         a single title, fetched the meta data and returns true.
     667        a single title, fetches the meta data and returns true.
    254668        """
    255         global videoExtensions
     669        global videoExtensions, import_from_files
    256670        maxFilesPerTitle = 3
    257671       
    258672        foundVideos = None
     
    261675        # a collection of videos in a directory (e.g., a backup of a tv-series).
    262676        for ext in videoExtensions:
    263677                videos = glob.glob(dirName + "/*." + ext)
    264                 # TODO: ensure that the filenames are almost exactly the same (CD1, CD2, etc.)
     678               
    265679                if 1 < len(videos) <= maxFilesPerTitle:
    266680                        if foundVideos is not None:
    267681                                # Already found a set of videos, this cannot be a dvdrip dir
    268682                                return False
    269                        
    270                         # Ensure that file names are of the same length:
    271                         # moviename.cd1.avi moviename.cd2.avi, etc.
    272                         # in addition, detect that the file names don't look like series episodes
     683                                                               
     684                        # Detect that the file names don't look like series episodes
    273685                        filename_length = len(videos[0])
    274686                        for video in videos:
    275687                                if imdbpy.detect_series_query(cleanup_title(video)) != (None, None, None):
    276688                                        print_verbose("'%s' looks like a TV-series episode." % video)
    277689                                        return False
    278                                 if len(video) != filename_length:
     690                       
     691                        # Detect the disc numbers from file names.
     692                        # If cannot detect, assume it's not a dvd rip dir.
     693                        for video in videos:
     694                                disc = detect_disc_number(videos, video)
     695                                if disc is None:
     696                                        print_verbose("Did not detect disc number for %s." % video)
    279697                                        return False
     698                                else:
     699                                        print_verbose("Found disc %d." % disc)
     700                                       
    280701                        foundVideos = videos
    281702                elif len(videos) > maxFilesPerTitle:
    282703                        return False                    # Too many videos for a dvdrip dir.
    283704       
    284705        metadata_target = dirName + "/" + dirMetadataFileName
    285706        if foundVideos is not None:
    286                 print_verbose('Compressed DVD backup directory (a.k.a. DVD rip) detected.')
    287                                
    288                 if os.path.exists(metadata_target) and not overwrite:
    289                         print_verbose("Metadata already exists, not overwriting.")
    290                         return True             
     707                print_verbose('DVD rip directory detected.')
     708                                               
     709                # Check if the video search should be skipped (because of existing data).
     710                first_file = foundVideos[0]
     711                if should_be_skipped(first_file, metadata_target):
     712                        print_verbose("Skipping '%s'." % dirName)
     713                        return True # DVD rip was detected, but no data should be inserted
    291714               
    292715                # Scan for the IMDb ID as usual, but store the
    293716                # metadata to video.metadata file instead, i.e., do not create .metadata for
    294717                # all videos in the directory.
    295                 [src_nfo, metadata] = find_metadata_for_video_path(foundVideos[0])
     718                metadata = None
     719                if import_from_files:
     720                        metadata = load_metadata_file(metadata_target)
     721
     722                if metadata is None:
     723                        metadata = find_metadata_for_video_path(foundVideos[0])                 
     724               
    296725                if metadata is not None:
    297726                        # Add the Files: metadata which lists the videos in correct playing order
    298727                        metadata += video_file_list_metadata(foundVideos)
    299                         save_metadata(metadata_target, metadata)               
     728                        save_metadata(dirName, metadata_target, metadata)               
    300729                return True
    301730       
    302731        return False
    303732
    304 def scan_file(pathName):
    305         metadata_target = strip_extension(pathName) + ".metadata";
    306         if os.path.exists(metadata_target) and not overwrite:
    307                 print_verbose("Metadata already exists, not overwriting.")
    308         else:
    309                 [src_nfo, metadata] = find_metadata_for_video_path(pathName)
    310                 save_metadata(metadata_target, metadata)
     733def scan_file(pathName, imdb_id = None):
     734        global import_from_files
     735               
     736        metadata_target = strip_extension(pathName) + ".metadata";     
     737       
     738        if should_be_skipped(pathName, metadata_target):
     739                print_verbose("Skipping '%s'." % pathName)
     740                return
     741       
     742        metadata = None
     743        if import_from_files:
     744                metadata = load_metadata_file(metadata_target)
     745       
     746        if imdb_id is not None:
     747                metadata = imdbpy.metadata_search(imdb_id)
     748                metadata += "IMDb:%s" % imdb_id + "\n"
     749               
     750        if metadata is None:
     751                metadata = find_metadata_for_video_path(pathName)
     752               
     753        if metadata is not None:
     754                save_metadata(pathName, metadata_target, metadata)
    311755
    312756
    313 def scan_directory(dirName):
     757def scan_directory(dirName, imdb_id = None):
    314758        global videoExtensions
    315759        print_verbose("Scanning directory %s..." % dirName)
     760       
    316761        if detect_compressed_dvd_backup_dir(dirName):
    317762                return
    318763       
     764        if imdb_id is not None:
     765                metadata = imdbpy.metadata_search(imdb_id)
     766                if metadata is not None:
     767                        metadata += "IMDb:%s" % imdb_id + "\n"
     768                        save_metadata(dirName, dirName + "/video.metadata", metadata)
     769                        return
     770       
     771       
    319772        if detect_dvd_backup(dirName):
    320773                return 
    321774       
     
    334787        for video in foundVideos:
    335788                scan_file(video)
    336789       
    337 def scan(pathName):
     790def should_be_skipped(path, meta_file = None):
     791        """
     792        Returns true in case the given path should be skipped in the scan.
     793        """
     794        global skipDirs, overwrite, dbimport, metafiles
     795       
     796        if path.endswith("/"):
     797                path = path[0:-1]
     798       
     799        # Check the ignored filename patterns.
     800        for skip in skipDirs:
     801                if path.endswith(skip):
     802                        return True
     803               
     804       
     805        # Check if we are not in overwrite mode and there is existing data
     806        # for the wanted targets (metadata files and/or MythDB).
     807        if not overwrite:               
     808                need_mythdb_data = dbimport and mythvideo_metadata_id(path) is None
     809                need_metadata_file = metafiles
     810                if metafiles and meta_file is not None:
     811                        need_metadata_file = not os.path.exists(meta_file)
     812                if not need_mythdb_data and not need_metadata_file:
     813                        return True # No need for data, skip this path.
     814                       
     815        return False
     816       
     817def scan(pathName, imdb_id = None):
    338818        global recursive
    339819        metadata = None
    340820        metadata_target = None
    341821        if os.path.isdir(pathName):
    342822                if recursive:
    343823                        for root, dirs, files in os.walk(pathName):
    344                                 if should_be_skipped(root): continue
     824                                if should_be_skipped(root):
     825                                        print_verbose("Skipping '%s'." % root)         
     826                                        continue
    345827                                scan_directory(root)
    346828                else:
    347                         scan_directory(pathName)
     829                        if should_be_skipped(pathName):
     830                                print_verbose("Skipping '%s'." % pathName)                               
     831                                return
     832                        scan_directory(pathName, imdb_id)
    348833        elif os.path.isfile(pathName):
    349                 scan_file(pathName)
     834                if should_be_skipped(pathName):
     835                        print_verbose("Skipping '%s'." % pathName)
     836                        return 
     837                scan_file(pathName, imdb_id)
    350838        else:
    351839                raise IOError("File not found")
    352840        return
    353841
    354842def main():
    355         global verbose,overwrite,interactive,recursive
     843        global verbose,overwrite,interactive,recursive,dbimport
     844        global import_from_files,metafiles,poster_dir
    356845       
    357         p = optparse.OptionParser()
     846        usage = "usage: %prog [options] videopath1 [videopath2 videopath3...]"
     847       
     848        p = optparse.OptionParser(usage=usage)
    358849        p.add_option('--version', '-v', action="store_true", default=False,
    359                 help="display 1-line describing name, version, author etc")
     850                help="Display 1-line describing name, version, author etc.")
    360851        p.add_option('--overwrite', '-o', action="store_true", default=False,
    361                 help="overwrite existing metadata file(s)")            
     852                help="Overwrite existing metadata.")   
    362853        p.add_option('--wordy', '-w', action="store_true", default=False,
    363                 help="verbose mode, be wordy while scanning for the info")
     854                help="Verbose mode, be wordy while scanning for the info.")
    364855        p.add_option('--interactive', '-i', action="store_true", default=False,
    365                 help="allow the script to ask questions from the user to find the meta data")           
     856                help="Allow the script to ask questions from the user to find the meta data.")
    366857        p.add_option('--recursive', '-r', action="store_true", default=False,
    367                 help="traverse sub directories of the given directory recursively")             
     858                help="Traverse sub directories of the given directory recursively.")
     859       
     860        p.add_option('--no_dbimport', '-n', action="store_true", default=False,
     861                help="Do not import metadata directly to MythDB.")     
     862        p.add_option('--fromfiles', '-f', action="store_true", default=False,
     863                help="Import data to MythDB from .metadata files if found. Requires -d.")
     864        p.add_option('--metafiles', '-m', action="store_true", default=False,
     865                help="Write metadata to *.metadata ascii files.")
     866               
     867        p.add_option('--answer', '-a', action="store", type="string", dest="imdb_id",
     868                help="Fetch metadata with the given IMDb ID for the path (must be a single path).")
    368869               
    369870               
    370871        options, arguments = p.parse_args()
    371872       
    372873        if options.version:
    373                 print "MythVideo Metadata Finder (c) Pekka Jääskeläinen 2006-2007"
     874                print "MythVideo Metadata Finder (c) Pekka JÀÀskelÀinen 2006-2007"
    374875                sys.exit(0)
    375876                       
    376877        verbose = options.wordy
    377878        overwrite = options.overwrite
    378879        interactive = options.interactive
    379880        recursive = options.recursive
     881        dbimport = not options.no_dbimport
     882        import_from_files = options.fromfiles and dbimport
     883        metafiles = options.metafiles
    380884       
     885        if not (metafiles or dbimport):
     886                print "You must define writing to either MythDB import (-d) or metadata files (-m)."
     887                sys.exit(1)
     888                       
    381889        if len(arguments) < 1:
    382                 print "Please give the file/directory to be scanned as argument."
     890                print "Please give the paths to be scanned as argument."
    383891                sys.exit(1)
     892        paths = arguments       
    384893       
    385         pathName = " ".join(arguments)
    386        
    387         if not os.path.exists(pathName):
    388                 print "Given file does not exist."
    389                 sys.exit(1)
     894        if options.imdb_id is not None:
     895                if recursive:
     896                        print "Manual IMDb ID must be given in recursive mode."
     897                        sys.exit(1)
     898                if len(arguments) > 1:
     899                        print "Manual IMDb ID must be given for a single path only (%d given)." % len(arguments)
     900                        sys.exit(1)                     
     901                print_verbose("IMDb ID %s given manually." % options.imdb_id)           
     902                       
     903        if dbimport:
     904                if not db_support:
     905                        print "You must install MySQLdb module to make direct DB importing to work"
     906                        sys.exit(1)
     907                if not init_db():
     908                        print "Database connection failed."
     909                        sys.exit(1)     
     910                poster_dir = mythtv_setting("VideoArtworkDir", socket.gethostname())
    390911               
    391         scan(pathName)         
     912       
     913        for path in paths:
     914       
     915                if not os.path.exists(path):
     916                        print "'%s' does not exist." % path
     917                        sys.exit(1)
     918                               
     919                scan(path, options.imdb_id)             
    392920                       
    393921        sys.exit(0)
    394922       
  • mythvideo/mythvideo/scripts/imdbpy.py

    old new  
    88it to make this script work.
    99
    1010This wrapper script is written by
    11 Pekka JÀÀskelÀinen (pekka jaaskelainen gmail).
    12 
    13 Changes:
    14 2007-02-21:[AW] Inserted plot outline as potential plot pick, now prints
    15                 genre and country.
    16 2006-11-26:[PJ] Modified some of the functions to be suitable for using as a
    17                 Python API (from find_meta.py).
    18 2006-10-05:[PJ] Improved poster searching and Runtime metadata finding for
    19                 TV-series episodes.
    20                 Better detection for a episode search.
    21 2006-10-04:[PJ] The first version.
     11Pekka JÀÀskelÀinen (gmail: pekka.jaaskelainen).
    2212""" 
    2313
    2414import sys
     
    2818try:
    2919        import imdb
    3020except ImportError:
    31         print "You need to install the IMDbPy library from "\
    32                         "http://imdbpy.sourceforge.net/"
     21        print "You need to install the IMDbPy library "\
     22                "from (http://imdbpy.sourceforge.net/?page=download)"
    3323        sys.exit(1)
    3424       
    3525def detect_series_query(search_string):
     
    6656                                        # Probably indexing exception in case the episode/season
    6757                                        # is not found.
    6858                                        continue
     59                                # Found an exact episode match, return that match only.
     60                                matches = []
    6961                                matches.append([imdb_access.get_imdbID(ep),
    7062                                                title.title().strip() + ", S" + season + " E" +
    7163                                                episode, int(serie['year'])])
     64                                return matches
    7265                        else:
    7366                                matches.append([imdb_access.get_imdbID(serie),
    7467                                        serie['title'], int(serie['year'])])
     
    8982
    9083        imdb_access = imdb.IMDb()
    9184        #print "Search:",search_string
    92         movies = imdb_access.search_movie(search_string.encode("ascii", 'replace'))     
     85        movies = imdb_access.search_movie(search_string.encode("ascii", 'ignore'))     
    9386
    9487        if movies is None or len(movies) == 0:
    9588                return None
     
    133126                movies.append([imdb_access.get_imdbID(m), m['title'], int(m['year'])])
    134127        return movies
    135128
    136 def poster_search(imdb_id):
     129def find_poster_url(imdb_id):
     130       
    137131        imdb_access = imdb.IMDb()
    138132        movie = imdb_access.get_movie(imdb_id)
    139133        imdb_access.update(movie)
    140134        url = None
    141135        if 'cover url' in movie.keys():
    142136                url = movie['cover url']
    143         if url is not None:
    144                 print url
    145         elif movie['kind'] == 'episode':
     137       
     138        if url is None and movie['kind'] == 'episode':
    146139                series = movie['episode of']
    147140                imdb_access.update(series)
    148141                if 'cover url' in series.keys():
    149142                        url = series['cover url']
    150                 if url is not None:
    151                         print url
     143        return url
     144               
     145def poster_search(imdb_id):
     146        url = find_poster_url(imdb_id)
     147        if url is not None:
     148                print url
    152149
    153150def metadata_search(imdb_id):
    154151        metadata = unicode()
     
    202199
    203200        if 'plot' in movie.keys():
    204201                plots = movie['plot']
    205                 if movie['plot outline'] is not None and len(movie['plot outline']):
     202                if 'plot outline' in movie and len(movie['plot outline']):
    206203                        plots.append("Outline::" + movie['plot outline'])
    207204                if plots is not None:
    208205                        # Find the shortest plot.
     
    258255        elif options.poster_search is not None:
    259256                poster_search(options.poster_search)
    260257        elif options.metadata_search is not None:
    261                 print metadata_search(options.metadata_search)
     258                print metadata_search(options.metadata_search).encode("utf8")
    262259        else:
    263260                p.print_help()
    264261        sys.exit(0)