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 , 17 years ago) |
---|
-
mythvideo/mythvideo/scripts/find_meta.py
old new 5 5 directory with multiple video files. Perform movie data lookups using the 6 6 imdbpy.py script. 7 7 8 Written by Pekka JÀÀskelÀinen (gmail: pekka.jaaskelainen) 2006 .8 Written by Pekka JÀÀskelÀinen (gmail: pekka.jaaskelainen) 2006-2007. 9 9 10 10 The metadata is searched with following steps: 11 11 … … 26 26 In case it's a video directory (a directory with 2 or more video files for the same title, 27 27 or a DVD directory) the file name is video.metadata. 28 28 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. 29 A crontab entry for the script to insert automatically data of new video files to 30 MythDB every hour: 31 31 32 0 * * * * find_meta.py -r /videos 33 34 In which /videos is the root of your MythVideo files. 32 35 """ 33 36 34 37 import sys … … 38 41 import glob 39 42 import fileinput 40 43 import imdbpy 44 import shlex 45 import socket 46 import urllib 47 41 48 try: 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 44 52 except: 45 pass 53 print "MySQLdb (python-mysqldb) not installed, MythDB importing disabled." 54 db_support = False 46 55 47 56 from stat import * 48 57 49 58 verbose=False 50 overwrite=False 59 51 60 interactive=False 52 61 recursive=False 62 dbimport=False 63 64 # Import metadata from .metadata files if found. 65 import_from_files=False 66 67 # Overwrite the old metadata if found. 68 overwrite=False 69 70 # Create the *.metadata files. 71 metafiles=False 53 72 54 videoExtensions = ["avi", "mpg", "wmv" ]73 videoExtensions = ["avi", "mpg", "wmv", "mkv"] 55 74 56 75 # The file name for storing metadata for videos that occupy the whole 57 76 # directory and might consist of multiple video files for the same … … 60 79 61 80 # Directories to skip in the recursive scan (matched to the rightmost 62 81 # part of the directory). 63 skipDirs = ["/Sample", "/Sub"] 82 skipDirs = ["/Sample", "/Sub", "/VIDEO_TS"] 83 84 # The directory to store the poster files in (this will be fetched from the 85 # MythTV settings table). 86 poster_dir = "./" 64 87 65 88 def print_verbose(string): 66 89 global verbose … … 68 91 print string 69 92 return 70 93 94 db = None 95 def 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 71 123 def find_imdb_id_from_text_file(textFile): 72 124 73 125 if os.access(textFile, os.R_OK): … … 98 150 return path[0: suffix_pos] 99 151 else: 100 152 return path 101 102 153 103 154 def cleanup_title(title): 104 155 title = title.replace("_", " ").replace(".", " ") 105 cut_point_strings = ["hdtv", "xvid", "dvd", "proper" ]156 cut_point_strings = ["hdtv", "xvid", "dvd", "proper", "720p"] 106 157 lowest_cutpoint = len(title) 107 158 for string in cut_point_strings: 108 159 pos = title.lower().rfind(string) … … 112 163 title = title[0:lowest_cutpoint] 113 164 return title.strip() 114 165 115 def save_metadata(fileName, metadata): 166 def 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 189 def 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 207 def 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 283 def 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 311 def 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 331 def 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 354 def 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 475 def 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 514 def save_metadata_to_file(fileName, metadata): 515 global overwrite 116 516 117 517 if os.path.exists(fileName) and not overwrite: 118 518 print_verbose("Metadata already exists, not overwriting.") 519 return 119 520 120 521 if metadata is not None: 121 522 print_verbose("Writing metadata to '%s'" % fileName) 122 523 f = open(fileName, 'w') 123 524 f.write(metadata.encode("utf8")) 124 525 f.close() 125 print_verbose("Metadata:") 126 print_verbose(metadata) 127 526 527 def 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) 128 542 129 543 def find_metadata_for_video_path(pathName): 130 131 544 global interactive 132 545 133 546 fileName = os.path.basename(pathName) … … 146 559 dirName + "/imdb.url", 147 560 dirName + "/" + file_body + ".nfo", 148 561 dirName + "/" + file_body + ".imdb"] 149 150 562 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. 153 564 for nfo in glob.glob(dirName + "/*.nfo"): 154 565 if nfo not in nfos: 155 566 nfos.append(nfo) … … 164 575 165 576 if imdb_id is None: 166 577 # A title search 167 title = cleanup_title(title)578 title = unicode(cleanup_title(title), "utf8", "ignore") 168 579 169 580 print_verbose("Title search '%s'" % title) 170 581 171 candidates = imdbpy.title_search(title .decode("utf8"))582 candidates = imdbpy.title_search(title) 172 583 if candidates is None or len(candidates) == 0: 173 # T ry with the dirname584 # TODO: Try with the dirname 174 585 pass 175 586 176 587 if candidates is not None and len(candidates) > 0: 177 588 178 589 index = 0 179 590 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 180 597 if interactive: 181 print "Got multiple candidates for the title search '%s'. " % title182 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"186 598 answer = raw_input("?)") 187 599 if answer is None or len(answer) == 0: 188 return [None, None]600 return None 189 601 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 203 604 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] 208 608 else: 209 609 print "Couldn't find IMDb ID for '%s'" % pathName 210 return [None, None]610 return None 211 611 212 612 print_verbose("Querying IMDb for meta data for ID %s..." % imdb_id) 213 613 metadata = imdbpy.metadata_search(imdb_id) 214 614 if metadata is not None: 215 615 metadata += "IMDb:%s" % imdb_id + "\n" 216 return [source_nfo, metadata] 217 218 # Save the metadata 616 return metadata 219 617 220 618 def video_file_list_metadata(videoPaths): 221 619 videoPaths = [os.path.basename(v) for v in videoPaths] … … 223 621 224 622 return "Files:%s" % (",".join(videoPaths)) + "\n" 225 623 624 def 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 226 637 def detect_dvd_backup(dirName): 227 638 """ 228 639 If the given directory is detected as a directory with a dvd backup, meta data is 229 640 searched for the directory title name (in addition to the directory-wide imdb files). 230 641 """ 642 global import_from_files 231 643 videoTs = dirName + "/VIDEO_TS" 232 644 if not (os.path.exists(videoTs) and os.path.isdir(videoTs)): 233 645 return False 234 646 235 print_verbose('A DVD backup directory (with DVD directory structure)detected.')647 print_verbose('A DVD backup directory with DVD directory structure detected.') 236 648 237 649 metadata_target = dirName + "/" + dirMetadataFileName 238 650 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 244 660 if metadata is not None: 245 save_metadata( metadata_target, metadata)661 save_metadata(dirName, metadata_target, metadata) 246 662 return True 247 663 248 249 250 664 def detect_compressed_dvd_backup_dir(dirName): 251 665 """ 252 666 If the given directory is detected as a directory with one or more files of 253 a single title, fetche dthe meta data and returns true.667 a single title, fetches the meta data and returns true. 254 668 """ 255 global videoExtensions 669 global videoExtensions, import_from_files 256 670 maxFilesPerTitle = 3 257 671 258 672 foundVideos = None … … 261 675 # a collection of videos in a directory (e.g., a backup of a tv-series). 262 676 for ext in videoExtensions: 263 677 videos = glob.glob(dirName + "/*." + ext) 264 # TODO: ensure that the filenames are almost exactly the same (CD1, CD2, etc.)678 265 679 if 1 < len(videos) <= maxFilesPerTitle: 266 680 if foundVideos is not None: 267 681 # Already found a set of videos, this cannot be a dvdrip dir 268 682 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 273 685 filename_length = len(videos[0]) 274 686 for video in videos: 275 687 if imdbpy.detect_series_query(cleanup_title(video)) != (None, None, None): 276 688 print_verbose("'%s' looks like a TV-series episode." % video) 277 689 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) 279 697 return False 698 else: 699 print_verbose("Found disc %d." % disc) 700 280 701 foundVideos = videos 281 702 elif len(videos) > maxFilesPerTitle: 282 703 return False # Too many videos for a dvdrip dir. 283 704 284 705 metadata_target = dirName + "/" + dirMetadataFileName 285 706 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 291 714 292 715 # Scan for the IMDb ID as usual, but store the 293 716 # metadata to video.metadata file instead, i.e., do not create .metadata for 294 717 # 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 296 725 if metadata is not None: 297 726 # Add the Files: metadata which lists the videos in correct playing order 298 727 metadata += video_file_list_metadata(foundVideos) 299 save_metadata( metadata_target, metadata)728 save_metadata(dirName, metadata_target, metadata) 300 729 return True 301 730 302 731 return False 303 732 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) 733 def 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) 311 755 312 756 313 def scan_directory(dirName ):757 def scan_directory(dirName, imdb_id = None): 314 758 global videoExtensions 315 759 print_verbose("Scanning directory %s..." % dirName) 760 316 761 if detect_compressed_dvd_backup_dir(dirName): 317 762 return 318 763 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 319 772 if detect_dvd_backup(dirName): 320 773 return 321 774 … … 334 787 for video in foundVideos: 335 788 scan_file(video) 336 789 337 def scan(pathName): 790 def 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 817 def scan(pathName, imdb_id = None): 338 818 global recursive 339 819 metadata = None 340 820 metadata_target = None 341 821 if os.path.isdir(pathName): 342 822 if recursive: 343 823 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 345 827 scan_directory(root) 346 828 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) 348 833 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) 350 838 else: 351 839 raise IOError("File not found") 352 840 return 353 841 354 842 def main(): 355 global verbose,overwrite,interactive,recursive 843 global verbose,overwrite,interactive,recursive,dbimport 844 global import_from_files,metafiles,poster_dir 356 845 357 p = optparse.OptionParser() 846 usage = "usage: %prog [options] videopath1 [videopath2 videopath3...]" 847 848 p = optparse.OptionParser(usage=usage) 358 849 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.") 360 851 p.add_option('--overwrite', '-o', action="store_true", default=False, 361 help=" overwrite existing metadata file(s)")852 help="Overwrite existing metadata.") 362 853 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.") 364 855 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.") 366 857 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).") 368 869 369 870 370 871 options, arguments = p.parse_args() 371 872 372 873 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" 374 875 sys.exit(0) 375 876 376 877 verbose = options.wordy 377 878 overwrite = options.overwrite 378 879 interactive = options.interactive 379 880 recursive = options.recursive 881 dbimport = not options.no_dbimport 882 import_from_files = options.fromfiles and dbimport 883 metafiles = options.metafiles 380 884 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 381 889 if len(arguments) < 1: 382 print "Please give the file/directoryto be scanned as argument."890 print "Please give the paths to be scanned as argument." 383 891 sys.exit(1) 892 paths = arguments 384 893 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()) 390 911 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) 392 920 393 921 sys.exit(0) 394 922 -
mythvideo/mythvideo/scripts/imdbpy.py
old new 8 8 it to make this script work. 9 9 10 10 This 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. 11 Pekka JÀÀskelÀinen (gmail: pekka.jaaskelainen). 22 12 """ 23 13 24 14 import sys … … 28 18 try: 29 19 import imdb 30 20 except 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)" 33 23 sys.exit(1) 34 24 35 25 def detect_series_query(search_string): … … 66 56 # Probably indexing exception in case the episode/season 67 57 # is not found. 68 58 continue 59 # Found an exact episode match, return that match only. 60 matches = [] 69 61 matches.append([imdb_access.get_imdbID(ep), 70 62 title.title().strip() + ", S" + season + " E" + 71 63 episode, int(serie['year'])]) 64 return matches 72 65 else: 73 66 matches.append([imdb_access.get_imdbID(serie), 74 67 serie['title'], int(serie['year'])]) … … 89 82 90 83 imdb_access = imdb.IMDb() 91 84 #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')) 93 86 94 87 if movies is None or len(movies) == 0: 95 88 return None … … 133 126 movies.append([imdb_access.get_imdbID(m), m['title'], int(m['year'])]) 134 127 return movies 135 128 136 def poster_search(imdb_id): 129 def find_poster_url(imdb_id): 130 137 131 imdb_access = imdb.IMDb() 138 132 movie = imdb_access.get_movie(imdb_id) 139 133 imdb_access.update(movie) 140 134 url = None 141 135 if 'cover url' in movie.keys(): 142 136 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': 146 139 series = movie['episode of'] 147 140 imdb_access.update(series) 148 141 if 'cover url' in series.keys(): 149 142 url = series['cover url'] 150 if url is not None: 151 print url 143 return url 144 145 def poster_search(imdb_id): 146 url = find_poster_url(imdb_id) 147 if url is not None: 148 print url 152 149 153 150 def metadata_search(imdb_id): 154 151 metadata = unicode() … … 202 199 203 200 if 'plot' in movie.keys(): 204 201 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']): 206 203 plots.append("Outline::" + movie['plot outline']) 207 204 if plots is not None: 208 205 # Find the shortest plot. … … 258 255 elif options.poster_search is not None: 259 256 poster_search(options.poster_search) 260 257 elif options.metadata_search is not None: 261 print metadata_search(options.metadata_search) 258 print metadata_search(options.metadata_search).encode("utf8") 262 259 else: 263 260 p.print_help() 264 261 sys.exit(0)