#!/usr/bin/perl -w #created by Menny Even-Danan (mennyed at the g mail thing) #additional work by Mark Nolan, Laurie Odgers, cferrier, Lorenzo1985 and paulyche (from Mythbuntu forums). my $script_version = "1.1.0.6"; #July 31st 2010 - open source with open sources! (almost) #http://s.evendanan.net/mythvideo #ideas taken from: #shabba's script (http://www.skynet.ie/~shabba/tveps.txt). Thread at http://www.gossamer-threads.com/lists/mythtv/users/320471. #Video length and video thumbs from http://www.perlmonks.org/?node_id=604177. Thanks to MonkE. # Eli Bendersky's Perl Levenshtein Algorithm implementation - http://eli.thegreenplace.net/programs-and-code/ #This script uses www.thetvdb.com API to retreive information about TV shows. #This script uses www.themoviedb.org API to retreive information about movies. #This script uses www.tvsubtitles.net to download tv shows subtitles. #changelog: #version 1.1.0.6 # Issue 72 - banners jpg file has no name # 0.23 support #version 1.1.0.5 # Issue 71 - File date is taken from the filesystem - by fruitcak. #version 1.1.0.4 # Issue 70 - Lock file support - by fruitcak. #version 1.1.0.3 # Issue 67,68 - NFO overriding fix #version 1.1.0.2 # Issue 67,68 - NFO overriding fix #version 1.1.0.1 # Issue 66 - posters from tmdb. #version 1.1.0.0 # Issue 65 - removal of imdb scraping due to 'terms of use' violation. #version 1.0.9.3 # Issue 60 - Parental Levels - taking data from configuration #version 1.0.9.2 # Issue 60 - Parental Levels some testing #version 1.0.9.1 # Issue 61 - quotes in titles and SQL. #version 1.0.9.0 # General issues with 0.22 #version 1.0.8.8 # Issue 53 - fixing the DVD rip issue. #version 1.0.8.7 # Much better HTTP caching #version 1.0.8.6 # Issue 57 - trying to fix the accented characters #version 1.0.8.5 # Issue 55 - myth-tv version is detected automatically (override is still possible). #version 1.0.8 # Added TMDB as grabber # fixed some movies being detected as episodes #version 1.0.7 #*Issue 47: Taking into account the year of the movie when locating it in IMDB. #*bug fixes #version 1.0.6 #*Issue 5: The season folder should show the season's art-work, and not the series. The series folder should show the series art-work #*bug fixes #version 1.0.5 #*Issue 45: MythTV 0.22 update for thetvdb.com. Use command arguments: # -MYTHTV_022 [0/1] # 0(default)-Disable featching # 1-Fetch additional Fanart, Banner, and ScreenShot metadata #*Issue 43: Return rating (G, PG, PG-13, R etc) for movies #version 1.0.4 #*bug fixes - issue 41 #version 1.0.3 (by cferrier) #*Motechnet posters retrieval fixed for motechposters.com domain and structure change. #This includes the neccessary referer spoofing put in to prevent hotlinking. #*bug fixes #version 1.0.2 #*Video folder now supports ":" separating. #*bug fixes #version 1.0.1 #*Entire folder skip markers - same overriding mechanism for file, has been extended to folder: #If you wish to override an entire folder, create a file with a name in this format: folder.[folder-name].nfo #E.g., for folder "MyMovies", create inside this folder a file named "folder.MyMovies.nfo". #the override options are as file: # * To skip a folder completely (just ignore, whatever in the database, if any, is OK): The file should contain '' # * To skip a folder parsing, and just add the file with a snapshot to the database (usefull for private videos): The file should contain '' #*bug fixes #version 1.0 #*New IMDB parser. No need for IMDB::Film perl module! #*New command line arguments: # -HOST [hostname] : Override the automatic hostname resolving. # -TV_TITLE_TYPE [0/1/2] # 0-[Filename] # 1(default)-[Episode number]: [Episode name] # 2-[Show] S[Season number]E[Episode number] [Episode name] # 3-[Show] [Season number]x[Episode number] [Episode name] # -MOVIE_TITLE_TYPE [0/1/2] # 0-[Filename] # 1(default)-[Movie name from IMDB] # -THUMB_OFFSET [seconds] : take the video snaphost image from the specified time. #*Can use an external script to retrieve movies posters. #*Taking hi-res movie posters from posters.motechnet.com #*Bug fixes # #version 0.9.3 #* New command line argument: # * Override Art-Work folder: -ARTFOLDER [full path to folder] #* DVD folder fixes. #* bug fixes. #version 0.9.2 #* Removed dependency of grep's perl regexp implementation. #* Added support for tv-show format: "ShowName SSEE.avi" #* bug fixes. #version 0.9.1 #* Support for DVD rips under VIDEO_TS folder (as MythVideo requires). #* bug fixes. #version 0.9 #* It is possible to override the search for a tv show or movie, by creating a file within the video file folder ("nfo" extension) #which includes the video file info: # * TV Show: # * To override the series: any .nfo file within the video file folder which contain '' # * To override the entire video file information: create a file with the same name as the file but with .nfo extension. The file should contain '' # * To override to a specific episode (e.g., a special): create a file with the same name as the file but with .nfo extension. The file should contain '' # * Movie: # * To override the video file: create a file with the same name as the file but with .nfo extension. The file should contain '' # * Skip markers: # * To skip a file complete (just ignore, whatever in the database , if any, is OK): create a file with the same name as the file but with .nfo extension. The file should contain '' # * To skip a file parsing, and just add the file with a snapshot to the database (usefull for private videos): create a file with the same name as the file but with .nfo extension. The file should contain '' #NOTE: NFO files can be hidden. #* Selects the most similar TV show from all results, rather than the first. #* New command line argument: # * Work on a specific file (can be usefull for replacing imdb.pl script): -F [full path to file] # * Change the default snapshot width (height is calcualted by the aspect ratio): -SNAPSHOT_WIDTH [number] # * Output episode titles in a short or long format (by paulyche): -SHORT_NAME [1/0] # * Change the IP of THE-TV-DB (this should be used rarely!): -TVIP [HOST/IP] #* SQL password is taken from the MythTV MySQL text file (by Lorenzo1985). #* bug fixes - thanks to Uter, Lorenzo1985 and paulyche. #version 0.8 #* Imaegs are saved in the artwork folder - now it is possible to get the images in mythweb! (by Lorenzo1985) #* images names will no longer have spaces in them (by Lorenzo1985) #* Redundant DB entries (e.g., video file does not exist) will be removed. #* Un-needed images (e.g., of deleted video files), will be deleted from the artwork folder #version 0.7: #* Moved away from WWW::TV perl module. Now uses www.thetvdb.com API! #* New command line arguments: # * Take snapshots for movies (don't take IMDB's poster) - "0" for take from IMDB.COM, "1" generate snapshot from file: -MOVIE_SNAP [1/0] # * Take snapshots for TV shows (don't take The TV DB's poster) - "0" for take from www.thetvdb.com, "1" generate snapshot from file: -TV_SNAP [1/0] #* major change in images download. #* bug fixes #* Known bug: images are not saved in the ART folder, but next to the video files - which means no images in the MythWeb. #version 0.6: #* command line support: # * Scan specific directory, recursive (overriding the automatic media folder detection): -D [path] # * Perform "full" (0)/"new files only" (1) scan (overriding the default specified): -N [1/0] # * Perform subtitles download: -S [1/0] # * Specify DB password: -DBP [whatever] #* better IMDB handling... Will take data only if the title is levenshtein similar. #* bug fixes - including spaces in cover filenames #version 0.5: #* support for downloading subtitles - will be downloaded if no subtitle file exists. #* video snapshot will be created anyway. #* if file was not found in TV.COM it will be handled as a movie #* bug fixes #version 0.4: #* better posters handling #* IMDB support #* Since this script supports TV and Movies, there is no need to specify the folder. It will be taken from the database. #* No need for extention specification. It will be taken from the database. #* New filesystem files will be automatically be added to MythVideo database. #* Bug fixes #version 0.3: #* bug fixes #version 0.2: #* A bit more logic into the parent folder art. #* support for [ShowName] [SeasonNumber(in one digit)][EpisodeNumber(two digits]: "My.Show 108.avi" #* better image naming. #version 0.1: Initial release use DBI; use LWP::UserAgent; use Sys::Hostname; use POSIX qw(strftime); use strict; #Your SQL server host. If the backend is in this computer, put 'localhost' here. my $dbhost = 'localhost'; #This is the default setting of MythTV my $db = 'mythconverg'; #this is the default user of MythTV my $dbuser = 'mythtv'; #Here you should put the SQL password my $dbpass = ''; #Set as 1 if you want to scan for new files only, #or 0 if you want to get information for all files (even scanned ones) my $only_new_files = 1; #Set as 1 it will download picture files, #or if set to 0 it will not download any pictures my $download_picture_files = 1; #Set as 1 if you want to automatically download subtitles for TV-shows my $download_subtitles = 0; my $subtitles_lang = "english"; #Set as 0 if you want to take posters from themoviedb.org, #or 1 if you want to generate from file. #or 2 if you want to take posters from an external script my $movies_snapshot = 0; my $external_poster_command_line = '/usr/share/mythtv/mythvideo/scripts/tmdb.pl -P '; #Set as 0 if you want to take posters from thetvdb.com, #or 1 if you want to generate from file. my $tv_snapshot = 1; #Sets the video snapshot width. Height is calculated from the video's aspect ratio. my $snapshot_width = 320; #this API key can easily be given if you register at www.thetvdb.com. #do not use this API key for other scripts! my $thetvdb_API_key = "6770D263BF045B96"; my $THETVDB = "www.thetvdb.com"; #this API key can be obtained by asking it from themoviedb.org admin. #do not use this API key for other scripts! my $themoviedb_API_key = "79302e9ad1a5d71e8d62a82334cdbda4"; #Overrides the auto-detection. Use "-ART_FOLDER" parameter. my $ArtworkDirectory = ''; my $BannerDirectory = ''; my $FanartDirectory = ''; my $ScreenshotDirectory = ''; my $cleanArtFolder = 1; #sets the tv show title format: #you should use the command line argument: -TV_TITLE_TYPE [0/1/2] # 0-[Filename] # 1(default)-[Episode number]: [Episode name] # 2-[Show] S[Season number]E[Episode number] [Episode name] # 3-[Show] [Season number]x[Episode number] [Episode name] my $tv_show_title_format = 1; #sets the movie title format: #you should use the command line argument: -MOVIE_TITLE_TYPE [0/1/2] # 0-[Filename] # 1(default)-[Movie name from themoviedb.org] my $movie_title_format = 1; #Sets this value to something higher than 0, if you want the video snapshot to be from a specific time point, #else it will be a random point in the video. my $video_thumb_offset = -1; #Set as 1 if you have mythtv 0.22, #or 0 if you a lower version of mythtv my $mythtv_022 = 0; my $mythtv_023 = 0; # Lock file. Set this to the name of a file to use as a lock file # so that only one instance of the script is running at a time my $lock_file = ""; # Force date added to be the same as the creation date of the file my $force_date_added_to_create_date = 1; # # NOTICE! # # Do not touch anything pass this point! # #global variables my $global_user_agent = LWP::UserAgent->new; $global_user_agent->timeout(45); $global_user_agent->agent("MythVideoFiller/".$script_version); #this is for HTTP debuging. #$global_user_agent->show_progress(1); sub get_http_response_lwp { my $request_url = $_[0]; print "About to call GET HTTP url: '$request_url'\n"; my $req = HTTP::Request->new(GET => $request_url); # Pass request to the user agent and get a response back my $res = $global_user_agent->request($req); # Check the outcome of the response if ($res->is_success) { my $response_content = $res->content; #print "Got HTTP response:\n".$response_content."\n"; return $response_content; } else { print "Failed to get response! Status '".$res->status_line."'\n"; return ""; } } sub get_http_response_wget { my $request_url = $_[0]; print "About to call WGET HTTP url: '$request_url'\n"; my $target_file = "/tmp/myth_video_filler_resp.html"; system("wget ".$request_url." -O ".$target_file); open FILE, $target_file; my $string = join("", ); close FILE; return $string; } my %urls_cache = (); sub get_http_response { my $url = $_[0]; #checking if URL is cached already my $cachedContect = $urls_cache{$url}; if ($cachedContect) { #print "'".$url."' is cached.\n"; return $cachedContect; } print "'".$url."' is not cached.\n"; my $content = get_http_response_lwp($url); $content =~ s/(\n|\r|\f)/ /gi; $urls_cache{$url} = $content; return $content; } my $videodir = ""; my @myfiles = ();#empty list to begin with. May it will be overriden with a command line argument #functions sub convert_for_url { my $show_name = $_[0]; $show_name =~ s/ /%20/gi; return $show_name; } my $poster_download_last_url = ''; my $poster_download_last_target = ''; sub download_poster { my $poster_url = $_[0]; my $target = $_[1]; my $is_this_motech = $poster_url =~ m/motechposters/; my $motech_referer = 0; if ($is_this_motech) { $motech_referer = $poster_url; $motech_referer =~ s/http:\/\/www.motechposters.com\/posters\//http:\/\/www.motechposters.com\/title\//; $motech_referer =~ s/_poster.jpg/\//; } if ((! -e "$target" || ($only_new_files eq 0)) && ($download_picture_files eq 1)) { #downloading or local copy? if ($poster_download_last_url eq $poster_url) { print "No need to download $poster_url. I have it locally at $poster_download_last_target.\n"; system("cp '$poster_download_last_target' '$target'"); return 1; } else { my $return_code; if ($motech_referer) { my @args = ('wget',$poster_url,'-O',$target,'-U',"MythVideoFiller/".$script_version,'--referer',$motech_referer); print "Downloading $poster_url with referer $motech_referer to $target\n"; $return_code = system( @args ); } else { my @args = ('wget',$poster_url,'-O',$target,'-U',"MythVideoFiller/".$script_version); print "Downloading $poster_url to $target\n"; $return_code = system( @args ); } my $this_poster_filesize = -s $target; if (!$this_poster_filesize) {$this_poster_filesize = 0;} if (($return_code eq 0) && ($this_poster_filesize != 0)) { $poster_download_last_url = $poster_url; $poster_download_last_target = $target; return 1; } else { if ($this_poster_filesize == 0) { unlink($target); print "Poster retrieved is zero bytes!\n"; return 0; } else { print "Failed to download poster! Return code: ".$return_code."\n"; return 0; } } } } else { print "Folder art for '$target' already exist.\n"; } } sub trim($) { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } sub get_video_length { my $fullfilename = $_[0]; my ($video_len) = grep(/^ID_LENGTH=/, (`mplayer -identify \"$fullfilename\" -nosound -vc dummy -vo null`)); #in DVD rips (VIDEO_TS), the mplayer will not return the length. if (($video_len) && ($video_len =~ /^[^=]+=(.*)$/ )) { $video_len = 0+$1; #myth like it in minutes $video_len = $video_len / 60; } else { $video_len = 0; } return $video_len; } sub create_video_thumb { my $fullfilename = $_[0]; my $video_length = $_[1]; my $thumb_path = $_[2]; my $thumb_offset; #it comes in minutes. I need in seconds. $video_length = $video_length * 60; if ($video_thumb_offset > 0) { if ($video_thumb_offset >= $video_length) { $thumb_offset = int($video_length * 0.5); } else { $thumb_offset = int($video_thumb_offset); } } else { $thumb_offset = int((0.3 + (rand() * 0.5)) * $video_length); print "Random point at second: $thumb_offset\n"; } my $cmd = "mplayer -zoom -ss $thumb_offset -nosound -vo jpeg:outdir=/tmp -frames 1 -vf scale=$snapshot_width:-3 \"$fullfilename\" > /dev/null"; system($cmd); system ("mv /tmp/*1.jpg \"$thumb_path\""); } my $set_show_level = 1;#default yes. It will by overwritten on startup, when readying from the database. my $show_level_1 = ''; my $show_level_2 = ''; my $show_level_3 = ''; my $show_level_4 = ''; my $default_show_level = 1; #my $showlevel = parse_show_level($content_rating); sub parse_show_level { my $rating_string = $_[0]; if (($set_show_level eq 0) || (!$rating_string)) { return 'showlevel';#this will force the re-assignment of whatever already stored in the database. } #In myth, levels 2, 3, and 4 requires passwords #Level 1 (the lowest) does not require password. if ($rating_string =~ m/($show_level_1)/i) { return 1; } if ($rating_string =~ m/($show_level_2)/i) { return 2; } if ($rating_string =~ m/($show_level_3)/i) { return 3; } if ($rating_string =~ m/($show_level_4)/i) { return 4; } return $default_show_level; } sub ensure_string_is_valid_for_SQL { my $text = $_[0]; if (!$text) { return ""; } else { #http://www.theukwebdesigncompany.com/articles/entity-escape-characters.php #issue36: sometimes, the string is already fixed! $text =~ s/\\?\"/\\"/gi; #html escape codes $text =~ s/&/&/gi; $text =~ s/ / /gi; $text =~ s/"/\\"/gi; $text =~ s/'/\\'/gi; $text =~ s/<//gi; #issue 57. Now we suppose to support HTML escape codes. $text =~ s/(\&\#\d{2}\;)/chr(substr($1, 2, 2))/eg; $text =~ s/(\&\#x[a-fA-F0-9]{2}\;)/chr(hex(substr($1, 3, 2)))/eg; #if I missed something. $text =~ s/&#\d{1,8}?;/ /gi; $text =~ s/&#x\d{1,8}?;/ /gi; return $text; } } my $subtitles_front_page_cache = ''; my $show_subtitles_name_cache = ''; my $show_subtitles_season_cache = ''; my $show_subtitles_show_id = ''; my $show_subtitles_page_cache = ''; sub download_tv_subtitle { my $tv_show = $_[0]; my $full_subtitle_filename = $_[1]; my $season = $_[2]; my $episode = $_[3]; if ($episode < 10) { $episode = "0".$episode; } if (-e $full_subtitle_filename) { print "Subtitle exists.\n"; return; } if ($subtitles_front_page_cache eq '') { my $url = "http://www.tvsubtitles.net/tvshows.html"; $subtitles_front_page_cache = get_http_response($url); } if ($show_subtitles_name_cache ne $tv_show) { $show_subtitles_season_cache = ''; $show_subtitles_show_id = ''; $show_subtitles_page_cache = ''; #print "page content:\n$subtitles_front_page_cache\n"; if ($subtitles_front_page_cache =~ m/$tv_show/i) { $show_subtitles_name_cache = $tv_show; $show_subtitles_show_id = $1; print "Show ID for subtitle is '$show_subtitles_show_id'\n"; } else { print "Unable to locate show '$tv_show' subtitles page.\n"; return; } } if ($show_subtitles_season_cache ne $season) { my $show_subtitles_url = "http://www.tvsubtitles.net/tvshow-".$show_subtitles_show_id."-".$season.".html"; print "Loading season subttitles page from '$show_subtitles_url'\n"; $show_subtitles_page_cache = get_http_response($show_subtitles_url); $show_subtitles_season_cache = $season; } my $episode_key = $season."x".$episode; #print "page content:\n$show_subtitles_page_cache\n"; if ($show_subtitles_page_cache =~ m/$episode_key<\/td>.*?/i) { my $download_the_subtitle_url = "http://www.tvsubtitles.net/download-".$1.".html"; print "Download subtitle for $episode_key from '$download_the_subtitle_url'\n"; system("wget $download_the_subtitle_url -O subtitle.gz"); system("gzip -d -c -f subtitle.gz >> '$full_subtitle_filename'"); } else { print "Can not locate $subtitles_lang subtitles!\n"; return; } } else { print "Can not locate subtitle for $episode_key!\n"; return; } } sub levenshtein { # $s1 and $s2 are the two strings # $len1 and $len2 are their respective lengths # my ($s1, $s2) = @_; # Enhancement by Laurie Odgers # I want only letters digits and spaces $s1 =~ s/[^a-zA-Z\d\s]*//g; $s2 =~ s/[^a-zA-Z\d\s]*//g; #ofcourse, all should be lowercase. $s1 =~ tr/[A-Z]/[a-z]/; $s2 =~ tr/[A-Z]/[a-z]/; #no n-spaces. $s1 =~ s/\s+/ /g; $s2 =~ s/\s+/ /g; #print "s1: '$s1'. s2: '$s2'\n"; my ($len1, $len2) = (length $s1, length $s2); # If one of the strings is empty, the distance is the length # of the other string # return $len2 if ($len1 == 0); return $len1 if ($len2 == 0); # if the strings match exactly then the distance is 0 return 0 if ($s1 eq $s2); my %mat; # Init the distance matrix # # The first row to 0..$len1 # The first column to 0..$len2 # The rest to 0 # # The first row and column are initialized so to denote distance # from the empty string # for (my $i = 0; $i <= $len1; ++$i) { for (my $j = 0; $j <= $len2; ++$j) { $mat{$i}{$j} = 0; $mat{0}{$j} = $j; } $mat{$i}{0} = $i; } # Some char-by-char processing is ahead, so prepare # array of chars from the strings # my @ar1 = split(//, $s1); my @ar2 = split(//, $s2); for (my $i = 1; $i <= $len1; ++$i) { for (my $j = 1; $j <= $len2; ++$j) { # Set the cost to 1 iff the ith char of $s1 # equals the jth of $s2 # # Denotes a substitution cost. When the char are equal # there is no need to substitute, so the cost is 0 # my $cost = ($ar1[$i-1] eq $ar2[$j-1]) ? 0 : 1; # Cell $mat{$i}{$j} equals the minimum of: # # - The cell immediately above plus 1 # - The cell immediately to the left plus 1 # - The cell diagonally above and to the left plus the cost # # We can either insert a new char, delete a char or # substitute an existing char (with an associated cost) # $mat{$i}{$j} = min([$mat{$i-1}{$j} + 1, $mat{$i}{$j-1} + 1, $mat{$i-1}{$j-1} + $cost]); } } # Finally, the Levenshtein distance equals the rightmost bottom cell # of the matrix # # Note that $mat{$x}{$y} denotes the distance between the substrings # 1..$x and 1..$y # my $lev_result = $mat{$len1}{$len2}; return $lev_result; #Now, I want to soften it a bit #So, i'm taking the distance between the strings, but not the added letters. #my $string_length_diff = abs($len1 - $len2); #return abs($lev_result - $string_length_diff); } # minimal element of a list # sub min { my @list = @{$_[0]}; my $min = $list[0]; foreach my $i (@list) { $min = $i if ($i < $min); } return $min; } sub no_folder_art_at_folder { my $directory = $_[0]; if ((-e $directory."folder.jpg") || (-e $directory."folder.png") || (-e $directory."folder.gif") || (-e $directory."folder.bmp")) { return 0; } else { return 1; } } sub locate_thetvdb_data_in_file { my $possible_nfo = $_[0]; open NFO_FILE, "<", $possible_nfo; my @file_lines = ; close NFO_FILE; foreach(@file_lines) { my $a_line = $_; if ($a_line =~ m//i) { #print "Found seriesid = $1\n"; return ($1, "", "", ""); } elsif ($a_line =~ m//i) { #print "Found seriesid=$1, season=$2, episode=$3\n"; return ($1, $2, $3, ""); } elsif ($a_line =~ m//i) { #print "Found episodeid=$1\n"; return ("", "", "", $1); } } #no data in file in file return ("", "", "", ""); } my $cached_nfo_folder = ""; my $cached_nfo_folder_series_id = ""; sub locate_thetvdb_series_id_in_any_nfo_file { my $folder = $_[0]; if ($cached_nfo_folder eq $folder) { print "Returning cached series ID '$cached_nfo_folder_series_id' for folder '$folder'\n"; return $cached_nfo_folder_series_id; } #my $search_path = $folder."*.nfo"; print "Will look at '$folder'\n"; opendir(DIR, $folder); my @all_nfo_files = grep { /\.nfo$/ } readdir(DIR); closedir(DIR); #globbing does not work when files/folders contain spaces. #my @all_nfo_files = glob($search_path); foreach(@all_nfo_files) { my $possible_nfo = $folder.$_; print "nfo: '$possible_nfo'\n"; #looking for series id inside the file (open for read) my ($seriesid, $season, $episode, $episode_id) = locate_thetvdb_data_in_file($possible_nfo); if (length $seriesid > 0) { print "Found id=$seriesid in file $possible_nfo, will not search for the searies in THETVDB.\n"; #saving for future calls $cached_nfo_folder = $folder; $cached_nfo_folder_series_id = $seriesid; return $seriesid; } } #did not find - caching this too.. $cached_nfo_folder = $folder; $cached_nfo_folder_series_id = ""; return ""; } sub is_simple_meta_data { my $possible_nfo = $_[0]; if (-e $possible_nfo) { open NFO_FILE, "<", $possible_nfo; my @file_lines = ; close NFO_FILE; foreach(@file_lines) { my $a_line = $_; if ($a_line =~ m//i) { return 1; } } } return 0; } sub is_skip_file { my $possible_nfo = $_[0]; if (-e $possible_nfo) { open NFO_FILE, "<", $possible_nfo; my @file_lines = ; close NFO_FILE; foreach(@file_lines) { my $a_line = $_; if ($a_line =~ m//i) { return 1; } } } return 0; } sub locate_imdb_data_in_file { my $possible_nfo = $_[0]; open NFO_FILE, "<", $possible_nfo; my @file_lines = ; close NFO_FILE; foreach(@file_lines) { my $a_line = $_; #I just need the digit code. if ($a_line =~ m//i) { #print "Found imdb id = $1\n"; return $1; } } return ""; } my $cached_tmdb_movie_id = ""; my $cached_imdb_movie_name = ""; my $cached_imdb_movie_id = ""; my ($cached_imdb_title_url, $cached_imdb_title, $cached_imdb_plot, $cached_imdb_director, $cached_imdb_release_date, $cached_imdb_rating, $cached_imdb_poster, $cached_imdb_fanart, $cached_motechnet_poster, $cached_content_rating); sub get_movie_meta_data_from_tmdb { #TMDB added by Mark Nolan my $imdb_id = $_[0]; my $tmdb_id= $_[1]; if ((!$tmdb_id) || length($tmdb_id) eq 0) { #Get TMDB_ID my $url = "http://api.themoviedb.org/2.1/Movie.imdbLookup/en/xml/".$themoviedb_API_key."/tt".$imdb_id; my $id_page = get_http_response($url); if ($id_page =~ m/(.*?)<\/id>/i) { $tmdb_id = $1; } } if (($tmdb_id ne $cached_tmdb_movie_id)) { my $cached_tmdb_title_url = "http://api.themoviedb.org/2.1/Movie.getInfo/en/xml/".$themoviedb_API_key."/".$tmdb_id; my $movie_page = get_http_response($cached_tmdb_title_url); ($cached_imdb_title) = $movie_page =~ m/(.*?)<\/name>/i; ($cached_imdb_poster) = $movie_page =~ m/(.*?)<\/rating>/i; if (!$cached_imdb_rating) { $cached_imdb_rating = 0; } ($cached_imdb_director) = $movie_page =~ m//i; if (!$cached_imdb_director) {$cached_imdb_director = "";} ($cached_imdb_release_date) = $movie_page =~ m/(\d{4}).*?(.*?)<\/overview>/i; if (!$cached_imdb_plot) {$cached_imdb_plot = "-";} if (!$cached_content_rating) {$cached_content_rating = "NR";} print "title: '$cached_imdb_title'\n"; print "poster: '$cached_imdb_poster'\n"; print "rating: '$cached_imdb_rating'\n"; print "director: '$cached_imdb_director'\n"; print "release_date: '$cached_imdb_release_date'\n"; print "plot: '$cached_imdb_plot'\n"; $cached_imdb_movie_id = $imdb_id; my $cached_imdb_movie_id = $tmdb_id; } return ($cached_imdb_movie_id, $cached_imdb_title_url, $cached_imdb_title, $cached_imdb_plot, $cached_imdb_director, $cached_imdb_release_date, $cached_imdb_poster, $cached_imdb_fanart, $cached_imdb_rating, $cached_motechnet_poster, $cached_content_rating); } my $cached_tmdb_ID = ""; my $cached_imdb_ID = ""; sub get_imdb_ID_for_movie_name_tmdb { my $movie_name = $_[0]; my $releaseYear = $_[1]; my $imdb_ID = ""; my $tmdb_ID = ""; if ($movie_name ne $cached_imdb_movie_name) { $cached_imdb_movie_name = $movie_name; #http://api.themoviedb.org/2.0/Movie.search?title=Transformers&api_key=79302e9ad1a5d71e8d62a82334cdbda4 #http://api.themoviedb.org/2.1/Movie.search/en/xml/79302e9ad1a5d71e8d62a82334cdbda4/Transformers my $tmdb_response = get_http_response("http://api.themoviedb.org/2.1/Movie.search/en/xml/".$themoviedb_API_key."/".$movie_name); my $best_similarity = 1000000; while ($tmdb_response =~ m/(.+?)<\/movie>/gi) { my $movie = $1; if ($movie eq "Your query didn't return any results.") { print "Can not locate movie '$movie_name' in themoviedb.\n"; return ("", ""); } my $possible_name = ""; ($possible_name) = $movie =~ m/(.*?)<\/name>/i; my $possible_tmdb = ""; ($possible_tmdb) = $movie =~ m/(.*?)<\/id>/i; my $possible_imdb = ""; ($possible_imdb) = $movie =~ m/tt(.*?)<\/imdb_id>/i; my $possible_year = ""; ($possible_year) = $movie =~ m/(.+?)-(.+?)-(.+?)<\/released>/i; my $current_similarity = levenshtein($movie_name, $possible_name); if ($releaseYear) {#Check Release Date if ($releaseYear ne $possible_year) { $current_similarity = 1000000; } else { $current_similarity = $current_similarity - 1000; } } if ($current_similarity < $best_similarity) { if ($tmdb_ID eq "") { print "Found a possible match for '$movie_name' as '$possible_name' (ID $possible_tmdb)\n"; } else { print "Found a better possible match for '$movie_name' as '$possible_name' (ID $possible_tmdb)\n"; } $best_similarity = $current_similarity; $imdb_ID = $possible_imdb; $tmdb_ID = $possible_tmdb; } } if ($tmdb_ID) { print "Found ID '$tmdb_ID' for movie '$movie_name' at themoviedb.\n"; $cached_imdb_ID = $imdb_ID; $cached_tmdb_ID = $tmdb_ID; return ($cached_imdb_ID, $cached_tmdb_ID); } else { print "Can not locate movie '$movie_name' in themoviedb.\n"; return ("", ""); } } return ($cached_imdb_ID, $cached_tmdb_ID); } sub search_the_tv_db_for_series { my $series = $_[0]; print "Searching THE-TV-DB for series '$series'\n"; my $series_for_url = $series; my $scontent = get_http_response("http://".$THETVDB."/api/GetSeries.php?seriesname=$series_for_url"); my $series_id = ""; my $plot = ""; my $tvdb_series_name = ""; my $best_similarity = 1000000; #? marks the regexp as ungreedy (don't look for the longest, look for the first - which is actually IS a greedy algorithm...) #"gi" at the end makes it recursive #while ($scontent =~ m/(.+?)<\/seriesid>.*?(.+?)<\/seriesname>.*?(.+?)<\/banner>.*?(.+?)<\/overview>/gi) while ($scontent =~ m/.*?(.+?)<\/seriesid>.*?(.+?)<\/seriesname>(.+?)<\/series>/gi) { my $temp_series_id = $1; my $temp_series_name = $2; my $current_similarity = levenshtein($series, $temp_series_name); #print "++++++++++++++++++++++++++++++++++++++++++\n"; #print "Best similarity: $best_similarity Current: $current_similarity\n"; if ($current_similarity < $best_similarity) { if ($series_id eq "") { print "Found a possible match for '$series' as '$temp_series_name' (ID $temp_series_id)\n"; } else { print "Found a better possible match for '$series' as '$temp_series_name' (ID $temp_series_id)\n"; } $best_similarity = $current_similarity; $series_id = $1; $tvdb_series_name = $2; my $rest_of_the_data = $3; if ($rest_of_the_data =~ m/(.+?)<\/overview>/i){$plot = $1;} else {$plot = "";} } #print "++++++++++++++++++++++++++++++++++++++++++\n"; } if ((length $series_id) > 0) { print "Found ID '$series_id' for series '$series' at THE-TV-DB.\n"; return ($series_id); } else { print "Can not locate series '$series' in THE-TV-DB.\n"; return (""); } } sub get_series_meta_data { my $series_id = $_[0]; my $series_name = $_[1]; my $folder_path = $_[2]; my $actual_show_name; if ((length $series_id) eq 0) { $series_id = search_the_tv_db_for_series($series_name); } if ((length $series_id) gt 0) { print "the-tv-db info found '$series_name' at '$folder_path'.\n"; my $scontent = get_http_response("http://".$THETVDB."/api/".$thetvdb_API_key."/series/".$series_id."/en.xml"); my $content_rating = "NR"; my $imdb; my $poster = ""; my $banner = ""; my $fanart = ""; my $plot = ""; if ($scontent =~ m/(.+)<\/ContentRating>/i) { $content_rating = $1; } if ($scontent =~ m/(.+)<\/IMDB_ID>/i) { $imdb = $1; $imdb =~ s/tt//g; } if ($scontent =~ m/(.+)<\/overview>/i) { $plot = $1; } if ($scontent =~ m/(.+)<\/seriesname>/i) { $actual_show_name = $1; } if ($scontent =~ m/(.+)<\/banner>/i) { $banner = "http://".$THETVDB."/banners/".$1; } if ($scontent =~ m/(.+)<\/fanart>/i) { $fanart = "http://".$THETVDB."/banners/".$1; } if ($scontent =~ m/(.+)<\/poster>/i) { $poster = "http://".$THETVDB."/banners/".$1; } if ((length $series_name) eq 0) {#if the name was not provided, we'll take it from the response. This will help us cache the series ID (see the call of 'get_series_meta_data' function). $series_name = $actual_show_name; } if ($content_rating eq "") { $content_rating = "NR" } return ($series_id, $series_name, $poster, $banner, $fanart, $plot, $actual_show_name, $content_rating, $imdb); } return ("", "", "", "", "", "", ""); } sub get_season_banners { my $series_id = $_[0]; my $season = $_[1]; if (((length $series_id) gt 0) && ((length $season) gt 0)) { #http://www.thetvdb.com/api/6770D263BF045B96/series/80348/banners.xml my $seacontent = get_http_response("http://".$THETVDB."/api/".$thetvdb_API_key."/series/".$series_id."/banners.xml"); while ($seacontent =~ m/.*?(.+?)<\/id>.*?(.+?)<\/BannerPath>.*?(.+?)<\/BannerType>.*?en<\/Language>.*?(.+?)<\/Season>.*?<\/Banner>/gi) { if (($4 eq $season) && ($3 eq "season")) {return ("http://".$THETVDB."/banners/".$2);} } } return (""); } sub create_episode_url { my $series_id = $_[0]; my $season = $_[1]; my $episode = $_[2]; return "http://".$THETVDB."/api/".$thetvdb_API_key."/series/".$series_id."/default/".$season."/".$episode."/en.xml"; } sub create_episode_url_by_episode_id { my $episode_id = $_[0]; #http://www.thetvdb.com/api/6770D263BF045B96/episodes/54545/en.xml return "http://".$THETVDB."/api/".$thetvdb_API_key."/episodes/".$episode_id."/en.xml"; } sub get_episode_meta_data { my $series_id = $_[0]; my $season = $_[1]; my $episode = $_[2]; my $episode_url = create_episode_url($series_id, $season, $episode); my $scontent = get_http_response($episode_url); #if the episode is 0 (a pilot?), and there is no content, we'll try the first episode if (((length $scontent) eq 0) && ($episode eq '0')) { $episode_url = create_episode_url($series_id, $season, "1"); $scontent = get_http_response($episode_url); } return parse_episode_content($scontent); } my $cached_get_episode_identifies_id = ""; my $cached_get_episode_identifies_content = ""; sub get_episode_meta_data_by_episode_id { my $episode_id = $_[0]; if ($episode_id ne $cached_get_episode_identifies_id) { my $episode_url = create_episode_url_by_episode_id($episode_id); $cached_get_episode_identifies_content = get_http_response($episode_url); $cached_get_episode_identifies_id = $episode_id; } return parse_episode_content($cached_get_episode_identifies_content); } sub parse_episode_content { my $content = $_[0]; #print "parse_episode_content: ".$content."\n"; my $episode_name = ""; my $plot = ""; my $rating = 0; my $poster = ""; my $director = ""; my $air_date = 0; my $episode_number = ""; my $season_number = ""; my $series_id = ""; my $episode_id = ""; my $imdb_id = ""; if ($content =~ m/(.+)<\/EpisodeName>/i) { $episode_name = $1; } if ($content =~ m/((.|\n|\r|\f)+)<\/Overview>/i) { $plot = $1; } if ($content =~ m/(.+)<\/Rating>/i) { $rating = int($1); } if ($content =~ m/(.+)<\/filename>/i) { $poster = "http://".$THETVDB."/banners/".$1; } if ($content =~ m/(.+)<\/Director>/i) { $director = $1; if ($director =~ m/\|(.+)\|/i) { $director = $1; } $director =~ s/\|/, /g; } if ($content =~ m/(\d+).*<\/FirstAired>/i)#just the year { $air_date = int($1); } if ($content =~ m/(.+)<\/id>/i) { $episode_id = $1; } if ($content =~ m/(.+)<\/EpisodeNumber>/i) { $episode_number = $1; } if ($content =~ m/(.+)<\/SeasonNumber>/i) { $season_number = $1; } if ($content =~ m/(.+)<\/seriesid>/i) { $series_id = $1; } if ($content =~ m/(.+)<\/IMDB_ID>/i) { $imdb_id = $1; $imdb_id =~ s/tt//g; } return ($series_id, $episode_id, $season_number, $episode_number, $episode_name, $plot, $rating, $poster, $director, $air_date, $imdb_id); } sub format_episode_title { my $filename = $_[0]; my $episode = $_[1]; my $episode_name = $_[2]; my $show = $_[3]; my $season = $_[4]; my $tv_show_title_format = $_[5]; my($episode_with_zero,$season_with_zero); if (length $episode eq 1) {$episode_with_zero = "0".$episode;} else{$episode_with_zero = $episode;} if (length $season eq 1) {$season_with_zero = "0".$season;} else{$season_with_zero = $season;} if ($tv_show_title_format eq 0) { return $filename; } elsif ($tv_show_title_format eq 2) { return $show." S".$season_with_zero."E".$episode_with_zero." ".$episode_name; } elsif ($tv_show_title_format eq 3) { return $show." ".$season."x".$episode_with_zero." ".$episode_name; } #default return $episode_with_zero.": ".$episode_name; } sub format_movie_title { my $filename = $_[0]; my $movie_name = $_[1]; my $movie_title_format = $_[2]; if ($movie_title_format eq 0) { return $filename; } #default return $movie_name; } # extracts the year released from the movie filename # takes 1 argument - the filename of the movie & returns the most appropriate year that it found sub extract_year_released { # an array to hold the found years my @foundYears; my @possibleYears; my $possibleYear; my $filename = $_[0]; # Get the local year (my $sec,my $min,my $hour,my $mday,my $mon,my $thisYear,my $wday,my $yday,my $isdst) = localtime(time); $thisYear += 1900; @possibleYears = $filename =~ /[\[,\(,\{]\d{4}[\],\),\}]/g; foreach $possibleYear (@possibleYears) { # strip the delimeter characters from the string $possibleYear =~ s/[\[,\(,\{,\],\),\}]//g; # if the digits are > 1900 and <= the current year its safe to assume its a year if (($possibleYear >= 1900) && ($possibleYear <= $thisYear)) { push(@foundYears, $possibleYear); } } # if we have a number of years in the array then we will use the one at the end if (scalar(@foundYears) > 1) { return $foundYears[scalar(@foundYears)-1]; } #print "COUNT: " . scalar(@foundYears) . "\n\n\n"; if ((scalar(@foundYears) eq 0) || (!$foundYears[0])) { return ""; } else { return $foundYears[0]; } } sub validate_lock_file { if( length($lock_file) == 0 ) { # No lock file specified ignore it and return return; } if( -e $lock_file ) { print "Lock file exists. '$lock_file'. Exiting\n"; exit; } open(my $fh_lock, '>>', $lock_file) or die "Could not open '$lock_file' - $!\n"; close($fh_lock) or die "Could not close '$lock_file' - $!\n"; } sub release_lock_file { if( length($lock_file) == 0 ) { # No lock file specified ignore it and return return; } print "Releasing lock file ('$lock_file').\n"; unlink($lock_file); } ###################################################################################### # ####################################################### #program start. my $local_machine_hostname = ""; #checking command line arguments for (my $arg_index = 0; $arg_index < (1 + $#ARGV); $arg_index++) { my $arg_type = $ARGV[$arg_index]; print "looking at '$arg_type'\n"; if ($arg_type eq "-D") { $arg_index++; $videodir = $ARGV[$arg_index]; if ($videodir !~ /\/$/) { $videodir = $videodir."/"; } print "Overriding DB folder. Will use '$videodir'\n"; } elsif($arg_type eq "-N") { $arg_index++; if ($ARGV[$arg_index] eq "1") { $only_new_files = 1; } else { $only_new_files = 0; } print "Overriding new files scan. Will use '$only_new_files'\n"; } elsif($arg_type eq "-P") { $arg_index++; if ($ARGV[$arg_index] eq "1") { $download_picture_files = 1; } else { $download_picture_files = 0; } print "Overriding download pictures. Will use '$download_picture_files'\n"; } elsif($arg_type eq "-S") { $arg_index++; if ($ARGV[$arg_index] eq "1") { $download_subtitles = 1; } else { $download_subtitles = 0; } print "Overriding subtitles download. Will use '$download_subtitles'\n"; } elsif($arg_type eq "-DBP") { $arg_index++; $dbpass = $ARGV[$arg_index]; print "Overriding DB password.\n"; } elsif($arg_type eq "-MOVIE_SNAP") { $arg_index++; if ($ARGV[$arg_index] eq '1') { $movies_snapshot = 1; } elsif ($ARGV[$arg_index] eq '2') { $movies_snapshot = 2; } else { $movies_snapshot = 0; } print "Overriding movies snapshot to '$movies_snapshot'.\n"; } elsif($arg_type eq "-TV_SNAP") { $arg_index++; if ($ARGV[$arg_index] eq '1') { $tv_snapshot = 1; } else { $tv_snapshot = 0; } print "Overriding tv show snapshot to '$tv_snapshot'.\n"; } elsif($arg_type eq "-SHORT_NAME") { $arg_index++; my $short_tv_name = int($ARGV[$arg_index]); if ($short_tv_name eq 0) {$tv_show_title_format = 2;} else {$tv_show_title_format = 1;} print "Obsolete! use '-TV_TITLE_TYPE' instead!..\n"; } elsif($arg_type eq "-TV_TITLE_TYPE") { $arg_index++; $tv_show_title_format = int($ARGV[$arg_index]); print "Overriding TV title name format to '$tv_show_title_format'.\n"; } elsif($arg_type eq "-MOVIE_TITLE_TYPE") { $arg_index++; $movie_title_format = int($ARGV[$arg_index]); print "Overriding movie title name format to '$movie_title_format'.\n"; } elsif($arg_type eq "-F") { $arg_index++; @myfiles = ($ARGV[$arg_index]); print "One file mode. Target file is '$ARGV[$arg_index]'.\n"; } elsif($arg_type eq "-SNAPSHOT_WIDTH") { $arg_index++; $snapshot_width = int($ARGV[$arg_index]); print "Snapshot width has been set to $snapshot_width pixels.\n"; } elsif($arg_type eq "-TVIP") { $arg_index++; $THETVDB = $ARGV[$arg_index]; print "THE-TV-DV is at '$THETVDB'.\n"; } elsif($arg_type eq "-ARTFOLDER") { $arg_index++; $ArtworkDirectory = $ARGV[$arg_index]; print "Artwork folder is at '$ArtworkDirectory'.\n"; } elsif($arg_type eq "-CLEAN_ARTFOLDER") { $arg_index++; if ($ARGV[$arg_index] eq "1") { $cleanArtFolder = 1; } else { $cleanArtFolder = 0; } print "Overriding clean art folder. Will use '$cleanArtFolder'\n"; } elsif($arg_type eq "-HOSTNAME") { $arg_index++; $local_machine_hostname = $ARGV[$arg_index]; print "machine hostname is '$local_machine_hostname'.\n"; } elsif($arg_type eq "-THUMB_OFFSET") { $arg_index++; $video_thumb_offset = int($ARGV[$arg_index]); print "Snapshot offset will be taken from second '$video_thumb_offset'.\n"; } elsif($arg_type eq "-PROXY") { $arg_index++; my $proxyadr =$ARGV[$arg_index];;#ip $proxyadr ="http://$proxyadr/"; $global_user_agent->proxy('http', $proxyadr) if length($proxyadr) >4; $global_user_agent->timeout(45); print "proxy server is '$proxyadr'.\n"; } elsif($arg_type eq "-PARENTAL") { $arg_index++; if ($ARGV[$arg_index] eq '0') { $set_show_level = 0; } else { $set_show_level = 1; } } elsif($arg_type eq "-LOCKFILE") { $arg_index++; $lock_file = $ARGV[$arg_index]; print "Lock file is '$lock_file'.\n"; } elsif($arg_type eq "-FORCEDATE") { $arg_index++; if ($ARGV[$arg_index] eq '0') { $force_date_added_to_create_date = 0; } else { $force_date_added_to_create_date = 1; } } else { print "Fill MythVideo Meta-Data v$script_version\n"; print "Command line arguments (all these arguments are optionals):\n"; print "-N [1/0]\n"; print " Perform \"full\" (0)/\"new files only\" (1) scan (overriding the default specified)\n"; print "-P [1/0]\n"; print " Download Pictures (0) Skip picture download (1)\n"; print "-D [path]\n"; print " Scan specific directory, recursive (overriding the automatic media folder detection).\n"; print "-F [full path to file]\n"; print " Work on a specific file (can be usefull for replacing imdb.pl script)\n"; print "-S [1/0]\n"; print " Perform subtitles download\n"; print "-DBP [whatever]\n"; print " Specify DB password\n"; print "-MOVIE_SNAP [0/1/2]\n"; print " For movies - generate snapshot from file (1)\n"; print " For movies - take snapshot from themoviedb.org (0)\n"; print " For movies - take snapshot from external script (command line must be defined) (2)\n"; print "-TV_SNAP [0/1]\n"; print " For TV shows - generate snapshot from file (1), or take it from the-tv-db.com (0)\n"; print "-SNAPSHOT_WIDTH [number]\n"; print " Change the default snapshot width (height is calcualted by the aspect ratio)\n"; print "-THUMB_OFFSET [seconds]\n"; print " Take the video snaphost image from the specified time\n"; print "-SHORT_NAME [0/1]\n"; print " Obsolete. Please use -TV_TITLE_TYPE.\n"; print "-TV_TITLE_TYPE [0/1/2]\n"; print " 0-[Filename]\n"; print " 1(default)-[Episode number]: [Episode name]\n"; print " 2-[Show] S[Season number]E[Episode number] [Episode name]\n"; print " 3-[Show] [Season number]x[Episode number] [Episode name]\n"; print "-MOVIE_TITLE_TYPE [0/1/2]\n"; print " 0-[Filename]\n"; print " 1(default)-[Movie name from the-movie-db.org]\n"; print "-ARTFOLDER [full path to folder]\n"; print " Override Art-Work folder. Usefull in case the script fails to detect the correct folder (in multi boxes environment)\n"; print "-CLEAN_ARTFOLDER [1/0]\n"; print " Clean files not referenced in database (1), or leave files alone.\n"; print "-HOST [hostname]\n"; print " Override the automatic hostname resolving.\n"; print "-TVIP [HOST/IP]\n"; print " Change the IP of THE-TV-DB (this should be used rarely!)\n"; print "-PROXY [ip_address:port]\n"; print " Use a proxy server eg. '10.1.0.254:3128'.\n"; print "-PARENTAL [0/1]\n"; print " 1(default)-store parental show level \n"; print " 0-don't handle parental level\n"; print "-LOCKFILE [filename]\n"; print " Use [filename] as a lock file to ensure only one script at a time\n"; print "-FORCEDATE [0/1]\n"; print " 1(default)-force the date added field of metadata to match file creation date\n"; print " 0-leave date added field as when metadata record created\n"; print "\n"; exit; } } validate_lock_file(); if ((length $local_machine_hostname) < 1) { $local_machine_hostname = hostname(); } print "Local machine hostname is '$local_machine_hostname'\n"; if ((length $dbpass) < 1) { if (-e "/etc/mythtv/mysql.txt") { print "Using password from mysql.txt\n"; open( my $R, '<', "/etc/mythtv/mysql.txt" ) or die "couldn't open mysql.txt\n"; while ( my $line = <$R> ) { if ( $line =~ /DBPassword/ ) { $dbpass = $line; } } close $R; $dbpass =~ s/DBPassword=//g; chomp($dbpass); } } #checking variables if ((length $dbpass) < 1) { die "Please provide the SQL server password. Use the '-DBP' command argument.\nUse '-h' command argument for more details.\n"; } if ($only_new_files eq 0) { print "NOTICE: You have selected to update ALL files! This will overwrite all previous data with new data.\n"; } my $last_series_id = '';#this is saved across files. Will be reset if file parsing is required. #my @season_information; my $last_show_name = ''; #this member will be used for show title (actual, from the internet). my $actual_show_name = ''; my $last_folder_name = ''; my $last_folder_poster = ''; my $content_rating = "NR"; my $thetvdb_imdb_id = ''; my $season_banner= ''; my $banner = ''; my $fanart = ''; my $total_files_count = 0; my $updated_files = 0; print "Connecting to database...\n"; my $dbh = DBI->connect("dbi:mysql:$db:$dbhost","$dbuser","$dbpass") || die "Database connection not made: $DBI::errstr"; my $sqlstat; my $sth; #querying for videos folder my $mythvideo_video_dir; $sqlstat=qq {select data from settings where value="VideoStartupDir" and hostname="$local_machine_hostname"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$mythvideo_video_dir ); $sth->fetch(); print "MythVideos folder is: $mythvideo_video_dir\n"; #querying for Banner folder $sqlstat=qq {select data from settings where value="mythvideo.bannerDir" and hostname="$local_machine_hostname"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$BannerDirectory); $sth->fetch(); #making sure it does not end with '/', cause we add it. if ($BannerDirectory =~ m/(.+)\/$/gi) { $BannerDirectory = $1; } print "Banner folder is: $BannerDirectory\n"; if ((length $BannerDirectory gt 0) && (!-e $BannerDirectory)) { print "*** Banners folder does not exists!!!!!!!\n"; } #querying for Fanart folder $sqlstat=qq {select data from settings where value="mythvideo.fanartDir" and hostname="$local_machine_hostname"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$FanartDirectory); $sth->fetch(); if ($FanartDirectory =~ m/(.+)\/$/gi) { $FanartDirectory = $1; } print "Fanart folder is: $FanartDirectory\n"; if ((length $FanartDirectory gt 0) && (!-e $FanartDirectory)) { print "*** Fan art folder does not exists!!!!!!!\n"; } #querying for Screenshot folder $sqlstat=qq {select data from settings where value="mythvideo.screenshotDir" and hostname="$local_machine_hostname"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$ScreenshotDirectory); $sth->fetch(); if ($ScreenshotDirectory =~ m/(.+)\/$/gi) { $ScreenshotDirectory = $1; } print "Screenshot folder is: $ScreenshotDirectory\n"; if ((length $ScreenshotDirectory gt 0) && (!-e $ScreenshotDirectory)) { print "*** Screenshots folder does not exists!!!!!!!\n"; } if (length $videodir == 0) { $videodir = $mythvideo_video_dir; } print "Will scan videos from folder: $videodir\n"; if (length $ArtworkDirectory == 0) { #querying for Artwork folder $sqlstat=qq {select data from settings where value="VideoArtworkDir" and hostname="$local_machine_hostname"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$ArtworkDirectory ); $sth->fetch(); if ($ArtworkDirectory =~ m/(.+)\/$/gi) { $ArtworkDirectory = $1; } } print "Artwork folder is: $ArtworkDirectory\n"; $sqlstat=qq {select extension from videotypes where f_ignore=0}; $sth = $dbh->prepare($sqlstat); $sth->execute(); my $a_video_ext; $sth->bind_columns( undef, \$a_video_ext ); $sth->fetch(); my $video_exts = $a_video_ext; while($sth->fetch()) { $video_exts = $video_exts."|".$a_video_ext; } print "Extensions: $video_exts\n"; if (scalar(@myfiles) eq 0) { #it may be ":" separated. my @video_folders = split(':', $videodir); foreach(@video_folders) { my $a_video_folder = $_; my @video_files = `find -L "$a_video_folder"`; push(@myfiles, @video_files); } } $sqlstat=qq {select data from settings where value = 'DBSchemaVer'}; $sth = $dbh->prepare($sqlstat); $sth->execute(); #I'm doing the query, and defining the variable in the global scoop, so we'll have this #easily accessable for any future needs. my $mythtvDBSchemaVersion; $sth->bind_columns( undef, \$mythtvDBSchemaVersion ); $sth->fetch(); print "Found DB schema version: $mythtvDBSchemaVersion.\n"; #check if the user has specified the special mythtv 0.22 features usages. #see issue 55 about this. if ($mythtvDBSchemaVersion >= 1244) { print "DB schema is of Myth TV 0.22 (or higher). Enabling extended features.\n"; $mythtv_022 = 1; } if ($mythtvDBSchemaVersion >= 1254) { print "DB schema is of Myth TV 0.23 (or higher).\n"; $mythtv_023 = 1; } if ($mythtv_022 eq 1) { $sqlstat=qq {select data from settings where value = 'mythvideo.ParentalLevelFromRating'}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$set_show_level ); $sth->fetch(); my $enabled = 'NO'; if ($set_show_level ne 0) { $enabled = 'YES'; } print "Parental level support: $enabled.\n"; if ($set_show_level ne 0)#should do it { $sqlstat=qq {select data from settings where value = 'mythvideo.AutoR2PL1'}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$show_level_1 ); $sth->fetch(); $sqlstat=qq {select data from settings where value = 'mythvideo.AutoR2PL2'}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$show_level_2 ); $sth->fetch(); $sqlstat=qq {select data from settings where value = 'mythvideo.AutoR2PL3'}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$show_level_3 ); $sth->fetch(); $sqlstat=qq {select data from settings where value = 'mythvideo.AutoR2PL4'}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $sth->bind_columns( undef, \$show_level_4 ); $sth->fetch(); #fixing the regex from 'R:NC-17' to '^(R|NC-17)$' $show_level_1 =~ s/:/\x7C/g; $show_level_2 =~ s/:/\x7C/g; $show_level_3 =~ s/:/\x7C/g; $show_level_4 =~ s/:/\x7C/g; #prefix and postfix $show_level_1 = '^('.$show_level_1.')$'; $show_level_2 = '^('.$show_level_2.')$'; $show_level_3 = '^('.$show_level_3.')$'; $show_level_4 = '^('.$show_level_4.')$'; print 'Parental Level regex are:\nLevel 1: '.$show_level_1.'\nLevel 2: '.$show_level_2.'\nLevel 3: '.$show_level_3.'\nLevel 4: '.$show_level_4.'\n'; } else { } } else { #i'm only supporting 0.22 with this. $set_show_level = 0; } foreach (@myfiles) { my $isDVD = 0;#issue 53 my $fullfilename = $_; $fullfilename =~ s/\W*$//gi; #skiping any file which is not in the file extension list if ($fullfilename !~ m/($video_exts)$/gi) { #print "file '$fullfilename' is not in the file extensions list. Skipping.\n"; next; } elsif ($fullfilename =~ m/\/VIDEO_TS.+$/i) { #print "Skipping DVD rip file...\n"; next; } elsif ($fullfilename =~ m/\/AUDIO_TS.+$/i) { #print "Skipping DVD rip file (audio files)...\n"; next; } elsif ($fullfilename =~ m/\/VIDEO_TS$/i) { $isDVD = 1;#issue 53 print "DVD root folder...\n"; $fullfilename =~ s/\/VIDEO_TS$//i; print "New fullfilename is $fullfilename\n"; #next; } print "======================================================================\n"; print "File ".$total_files_count." out of ".scalar(@myfiles)."...\n"; $total_files_count++; #print "File: $fullfilename\n"; #next; my($directory, $filename) = $fullfilename =~ m/(.*\/)(.*)$/; my $sql_fullfilename = ensure_string_is_valid_for_SQL($fullfilename); $sqlstat=qq {select inetref from videometadata where filename="$sql_fullfilename"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); my $current_show_ref; $sth->bind_columns( undef, \$current_show_ref ); my $video_has_meta_data = 0; if (!$sth->fetch()) { print "Video file '$filename' does not exist in the DB metadata. Inserting dummy row...\n"; my $sql_filename = ensure_string_is_valid_for_SQL($filename); $sqlstat=qq {insert videometadata (title,rating,showlevel,browse,filename) values ("$sql_filename","NR", $default_show_level, 1, "$sql_fullfilename")}; $sth = $dbh->prepare($sqlstat); $sth->execute(); $video_has_meta_data = 0; $current_show_ref = ""; } elsif (($current_show_ref) && (length $current_show_ref > 0)) { print "Video file '$filename' has metadata in the database.\n"; $video_has_meta_data = 1; #maybe the cover is not complete, in this case we also query for the meta-data $sqlstat=qq {select coverfile from videometadata where filename="$sql_fullfilename"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); my $coverfile; $sth->bind_columns( undef, \$coverfile ); my $invalid_cover_file = 0; if (!$sth->fetch()) { print "Could not locate cover file in the database.\n"; $invalid_cover_file = 1; } elsif ((!$coverfile) || (length $coverfile eq 0) || (!-e $coverfile) || (-s $coverfile < 100) || ($coverfile =~ m/folder\.\w{3}$/))#empty or ends with folder.* { print "Invalid cover file '$coverfile'!\n"; $invalid_cover_file = 1; } if ($invalid_cover_file eq 1) { print "The cover of the video file is invalid! I'll try to regenerate it...\n"; $video_has_meta_data = 0; } } else { print "Video file '$filename' does have not metadata in database.\n"; $video_has_meta_data = 0; $current_show_ref = ""; } #did the user requested full override? if ($only_new_files eq 0) { $video_has_meta_data = 0; } if( $video_has_meta_data eq 0 and $force_date_added_to_create_date eq 1 ) { my $ctime; $ctime = (stat($fullfilename))[10]; my $time_string = strftime "%Y-%m-%d %T", localtime($ctime); my $sql_fullfilename = ensure_string_is_valid_for_SQL($fullfilename); $sqlstat = qq { update videometadata set insertdate="$time_string" where filename="$sql_fullfilename" }; $sth = $dbh->prepare($sqlstat); $sth->execute(); } print "D=$directory, F=$filename\n"; my $is_tv_show = 0; my($series_id, $show, $season, $episode, $episode_id) = ("", "", "", "", ""); my $imdb_id = ""; #before anything, I'll check for NFO file my $filename_parsing_required = 1;#it will be changed to '0' if I'll find NFO for the file my $video_file_with_no_ext; if ($isDVD eq 1)#issue 53 {#Ho, it is a DVD in the HD #print "Note: DVD in the HD, This is partly supported (filenames with spaces)...\n"; $filename_parsing_required = 0; $is_tv_show = 0; print "DVD Folder: $directory\n"; print "DVD Name: $filename\n"; $video_file_with_no_ext = $filename; print "The NFO file will be looked from at '".$directory.$video_file_with_no_ext.".nfo\n"; } else { ($video_file_with_no_ext) = $filename =~ m/(.*)\..*/; } #support for same filename with nfo ext my $video_nfo_file; if ($video_file_with_no_ext)#it could be that the file is not a file, but a folder (VIDEO_TS?) { $video_nfo_file = $directory.$video_file_with_no_ext.".nfo"; if (!-e $video_nfo_file) { #support for same filename with nfo ext, but hidden $video_nfo_file = $directory.".".$video_file_with_no_ext.".nfo"; if (!-e $video_nfo_file) { #maybe entire folder override? my ($currect_folder_name) = $directory =~ m/^.*\/(.*?)\/$/; $video_nfo_file = $directory."folder.".$currect_folder_name.".nfo"; } } } #print "looking for '$video_nfo_file' for video file details.\n"; if (($video_nfo_file) && (-e $video_nfo_file)) { print "Video file has NFO file. Looking for video information in it...\n"; #my guess is that mostly it will be used for movies, so i'll check it first $imdb_id = locate_imdb_data_in_file($video_nfo_file); if (length $imdb_id > 0) { $is_tv_show = 0;#movie, nota tv show $filename_parsing_required = 0; if ($imdb_id eq $current_show_ref) { #print "same imdb ID in the database, and in the NFO file. No need to update\n"; #leaving the '$video_has_meta_data' as it was (maybe "-N 0" was used) } else { print "Found IMDB information in NFO file (imdb id: $imdb_id), using it to get meta-data about the file\n"; $video_has_meta_data = 0; } } else { ($series_id, $season, $episode, $episode_id) = locate_thetvdb_data_in_file($video_nfo_file); print "Located TV show data in NFO file: Series ID:".$series_id." Season:".$season." Episode:".$episode." Episode ID:".$episode_id."\n"; if ((length $series_id > 0) && (length $season > 0) && (length $episode > 0)) { $filename_parsing_required = 0; $is_tv_show = 1;#a tv show my $episode_url = create_episode_url($series_id, $season, $episode); if ($current_show_ref eq $episode_url) { #print "same URL in the database, and in the NFO file. No need to update\n"; #leaving the '$video_has_meta_data' as it was (maybe "-N 0" was used) } else { print "Found THE-TV-DB information in NFO file (series id: $series_id, season: $season, episode: $episode), using it to get meta-data about the file\n"; $video_has_meta_data = 0; $show = "id_".$series_id; } } elsif (length $episode_id > 0) { $filename_parsing_required = 0; $is_tv_show = 1;#a tv show my $episode_url = create_episode_url_by_episode_id($episode_id); #print "Will get episode data from ".$episode_url."\n"; if (($video_has_meta_data eq 1) && $current_show_ref eq $episode_url) { #print "same URL in the database, and in the NFO file. No need to update\n"; #leaving the '$video_has_meta_data' as it was (maybe "-N 0" was used) } else { print "Found THE-TV-DB information in NFO file (episode id: $episode_id). Getting identifies parameters...\n"; ($series_id, $episode_id, $season, $episode) = get_episode_meta_data_by_episode_id($episode_id); print "Found THE-TV-DB information in NFO file (series id: $series_id, season: $season, episode: $episode), using it to get meta-data about the file\n"; $video_has_meta_data = 0; $show = "id_".$series_id; } } else { #maybe skippers #they are two types of skipper: enitre data, or just get simple data if (is_skip_file($video_nfo_file)) { $filename_parsing_required = 0; print "File have been marked as 'no_update'. Skiping it, what's in the database will stay as is.\n"; next; } elsif (is_simple_meta_data($video_nfo_file)) { $filename_parsing_required = 0; print "File have been marked as 'simple_update'. Clearing meta-data, and setting snapshot.\n"; #getting video length my $video_length = get_video_length($fullfilename); #creating video snapshot my $thumb_path = $filename.".jpg"; #making the name a bit more friendly... $thumb_path =~ s/ /_/gi; $thumb_path =~ s/'/_/gi; #now adding to the full path - this is done here, so it will be possible to have spaces in the artwork folder $thumb_path = $ArtworkDirectory."/".$thumb_path; if (-e $thumb_path) { print "Video file already exists. Not over-writing.\n"; } else { create_video_thumb($fullfilename, $video_length, $thumb_path); } my $sql_filename = ensure_string_is_valid_for_SQL($filename); my $sql_path = ensure_string_is_valid_for_SQL($thumb_path); my $sql_fullfilename = ensure_string_is_valid_for_SQL($fullfilename); $sqlstat= qq { update videometadata set title="$sql_filename", plot="", director="", year=0, inetref="", coverfile="$sql_path", length=$video_length, userrating=0 where filename="$sql_fullfilename" }; $sth = $dbh->prepare($sqlstat); $sth->execute(); #next file please. next; } else { print "NFO file is irrelevant.\n"; } } } } #now I know if their is a need to get meta-data for the file if ($video_has_meta_data eq 1) { print "No need to get meta-data for file.\n"; next; } if ($filename_parsing_required == 1)#did not file NFO file { #no nfo, and the parsing will get a new show ID $series_id = "";#resetting, so it will be searched. #scrubs S04E22.tvrip.avi if ($filename =~ m/^(.+)S(\d{1,2})E(\d{1,2})\D.*$/i) { $show = $1; $season = $2; $episode = $3; print "TV format 1: Show: '$show', S:'$season', E:'$episode'\n"; $is_tv_show = 1; } #scrubs.4x22.tvrip.avi elsif ($filename =~ m/^(.+)\W+(\d{1,2})x(\d{1,2})\D.*$/i) { $show = $1; $season = $2; $episode = $3; print "TV format 2: Show: '$show', S:'$season', E:'$episode'\n"; $is_tv_show = 1; } #should support the following: #scrubs.422.tvrip.avi #My Name is Earl 422 - Pinky.720p.h264.tvrip.mkv #The Simpsons 2015 - Wedding For Disaster.720p.h264.tvrip.mkv #Big Bang Theory, The 301 - The Electric Can Opener Fluctuation.720p.h264.tvrip.avi #will ignore (2000) as movie year and ?,000 for 10,000 BC #will ignore (Dates 2009-01-01) Series - 2009-01-01 elsif (!($filename =~ /[\[,\,\{]\d{4}[\],\,\}]/g) && !($filename =~ m/^.+\d{1,2},\d{3}.*$/i) && !($filename =~ m/^.+\d{2,4}-\d{1,2}-\d{1,2}.*$/i) && ($filename =~ m/^(.*?)\D(\d{1,2})(\d{2})\D.*$/i))#This must be the last one, since we are in search for its tokens. { $show = $1; $season = $2; $episode = $3; print "TV format 3: Show: '$show', S:'$season', E:'$episode'\n"; $is_tv_show = 1; } else { print "Can not parse '$filename' as tv show. Will try as a movie...\n"; #$is_tv_show = 0;#starting with 0, why change? } } if ($is_tv_show eq 1) { #handling meta-data as TV show #fixing stuff $show =~ tr/[\.\_\-\[\]!\(\)]/ /; #to lower-case $show =~ tr/[A-Z]/[a-z]/; $show = trim($show); if ($show eq "hi 5") { $show = "hi-5"; } print "Show '$show' actual '$actual_show_name'.\n"; if ((length $show) eq 0) {exit;} $season = trim($season); $episode = trim($episode); #removing leading zeros $season =~ s/^0*//; $episode =~ s/^0*//; #ensuring that the items are not empty if ((length $season) eq 0) {$season = "0";} if ((length $episode) eq 0) {$episode = "0";} print "Found Show: (id: $series_id), '$show', season '$season', episode '$episode'. last ID: $last_series_id\n"; eval { #I want to get the meta-data for the file #I need the series ID, the season, and the episode. #The season and the episode are correct: They either parsed, or retreived from an NFO file #The series ID can be NFO parsed one or empty. #if it is not empty, then it is the correct one (I mean, I took it from an NFO file) #if it is empty, then I can search the current folder for NFO file which contains the series ID #series ID was found? Good. Not found: I may want to use the previous series ID. #the previous ID is valid ONLY if i'm in the same folder and the series name was not changed! #otherwise, I need to look for the series name in THE-TV-DB if ((length $series_id) eq 0) { #I have not detected a special NFO for the file, maybe there is for the show #looking for an nfo file inside the folder, maybe it wants to override the data #note: this function caches, so it is OK to call it multiple times. $series_id = locate_thetvdb_series_id_in_any_nfo_file($directory); #ok, I'll check the last_series_id if (($last_folder_name ne $directory) || ($last_show_name ne $show)) {#moved on, not valid anymore. $last_series_id = ""; } if ((length $series_id) eq 0) {#all my tries had not provided any series ID. I'll take the last (even if it is empty) $series_id = $last_series_id; } } print "Getting metadata for '$filename'...\n"; #note: the series_id may still be empty! But if so, the show name is not empty!! if (((length $series_id) eq 0) && ((length $show) eq 0)) { print "FATAL ERROR!! no series_id and no show name!!\n"; next; } #OK, everything I need #If I'm missing the ID I need to get the ID from the series name #If I moved to a new folder, I want to make sure I'm setting the correct folder art if (((length $series_id) eq 0) || ($last_folder_name ne $directory)) { if ((length $series_id) eq 0) { print "Missing series ID. Searching by series name '$show'...\n"; } else { print "Moved to a new folder. Need to get series meta-data for folder art...\n"; } my $plot; $thetvdb_imdb_id=""; ($series_id, $show, $last_folder_poster, $banner, $fanart, $plot, $actual_show_name, $content_rating, $thetvdb_imdb_id) = get_series_meta_data($series_id, $show, $directory); if (length($last_folder_poster) eq 0) { $last_folder_poster = $banner } #Download Banner if (($banner) && ($BannerDirectory)) { download_poster($banner, $BannerDirectory."/".$show.".jpg"); } #Download Fanart if (($fanart) && ($FanartDirectory)) { download_poster($fanart, $FanartDirectory."/".$show.".jpg"); } if ($last_folder_name ne $directory) { $last_folder_name = $directory; #show have a poster. I want to set the folder's art if (length $last_folder_poster > 0) { print "Downloading folder art for series from '$last_folder_poster' to '$directory'...\n"; #downloading poster for the series. This is for the parent folder! #this folder will get the season poster! my $parent_folder = $directory; # Skip Season directory will get later print "Skipping folder art at directory '$parent_folder'\n"; if ($parent_folder =~ m/^(\/.+\/).+\/$/) {$parent_folder = $1;} while(((length $parent_folder) >= (length $videodir)) && ((length $parent_folder) > (length $mythvideo_video_dir."/"))) { print "Checking folder art at directory '$parent_folder'\n"; #there could be a previous folder.jpg/png there if (no_folder_art_at_folder($parent_folder) eq 1) { download_poster($last_folder_poster, $parent_folder."folder.jpg"); } #now to check my parent if ($parent_folder =~ m/^(\/.+\/).+\/$/) {$parent_folder = $1;} else {last;} } } } } #if series_id is empty, then we could not locate the series in the-tv-db if (length $series_id eq 0) { $is_tv_show = 0; } else { #at this point, series_id holds the corrrect ID $last_series_id = $series_id; $last_show_name = $show; print "Getting episode information...\n"; my ($episode_name, $description, $rating, $episode_poster, $directors, $year); if ((length $episode_id) > 0)#I have the episode ID, I'll use it! {#($series_id, $episode_id, $season_number, $episode_number, $episode_name, $plot, $rating, $poster, $director, $air_date) my $temp_holder; my $temp_imdb_id; ($temp_holder, $episode_id, $temp_holder, $temp_holder, $episode_name, $description, $rating, $episode_poster, $directors, $year, $temp_imdb_id) = get_episode_meta_data_by_episode_id($episode_id); if ($temp_imdb_id) { $thetvdb_imdb_id = $temp_imdb_id; } else { $thetvdb_imdb_id = ""; } } else {#($series_id, $episode_id, $season_number, $episode_number, $episode_name, $plot, $rating, $poster, $director, $air_date) my $temp_holder; my $temp_imdb_id; ($temp_holder, $episode_id, $temp_holder, $temp_holder, $episode_name, $description, $rating, $episode_poster, $directors, $year, $temp_imdb_id) = get_episode_meta_data($series_id, $season, $episode); if ($temp_imdb_id) { $thetvdb_imdb_id = $temp_imdb_id; } else { $thetvdb_imdb_id = ""; } } print "Found meta-data: series id: $series_id, show: '$show', season: $season, episode: $episode.\n"; if (length $episode_name > 0) { $updated_files++; #print "$episode\n"; #print "$episode_name\n"; #print "$actual_show_name\n"; #print "$season\n"; #print "$tv_show_title_format\n"; $season_banner = get_season_banners($series_id, $season); if (! -e $directory."folder.jpg") { if (length($season_banner) gt 0) { download_poster($season_banner, $directory."folder.jpg"); } else { #Use Series poster download_poster($last_folder_poster, $directory."folder.jpg"); } } my $episode_title = format_episode_title($filename, $episode, $episode_name, $actual_show_name, $season, $tv_show_title_format); #filename should be in the plot $description .= "\n".$filename; my $video_length = get_video_length($fullfilename); #the video image filename (not fullpath) my $thumb_path = $show."-".$episode."-".$season.".jpg"; #the season image my $poster_path = $ArtworkDirectory."/".$show."-".$season.".jpg"; #the show image my $banner_path = $BannerDirectory."/".$show.".jpg"; my $fanart_path = $FanartDirectory."/".$show.".jpg"; #making the name a bit more friendly... $thumb_path =~ s/ /_/gi; $thumb_path =~ s/'/_/gi; #now adding to the full path - this is done here, so it will be possible to have spaces in the artwork folder if ($mythtv_022 eq 0) { $thumb_path = $ArtworkDirectory."/".$thumb_path; } else { #video image is stored in the screenshot folder $thumb_path = $ScreenshotDirectory."/".$thumb_path; if (length($season_banner) gt 0) { #we have a season banner, so we'll use that. download_poster($season_banner, $poster_path); } else { #no season banner, then show banner please. $poster_path = $banner_path; if (length($last_folder_poster) gt 0) { download_poster($last_folder_poster, $poster_path); } else { #have no show banner url, so I'll take the screenshot. $poster_path = $thumb_path; } } } if (($tv_snapshot eq 0) && (length $episode_poster > 0)) { print "downloading poster from '$episode_poster'...\n"; if (! -e $thumb_path) { download_poster($episode_poster, $thumb_path); } } else { print "Creating snapshot for '$fullfilename'...\n"; create_video_thumb($fullfilename, $video_length, $thumb_path); } #making sure that the cover is fine (I mean, it may be bad...) if ((! -e $thumb_path) || ((-s $thumb_path) < 100)) { print "The cover is invalid! Using the folder art instead.\n"; if (no_folder_art_at_folder($directory) eq 1) { $thumb_path = ""; } else { $thumb_path = $directory."folder.jpg"; if (! -e $thumb_path) { $thumb_path = $directory."folder.png"; if (! -e $thumb_path) { $thumb_path = $directory."folder.gif"; if (! -e $thumb_path) { $thumb_path = ""; } } } } } print "found '$episode_title': $description\n"; my $episode_url_for_inetref = ""; if (length($thetvdb_imdb_id) > 0) { $episode_url_for_inetref = $thetvdb_imdb_id; } else { $episode_url_for_inetref = create_episode_url_by_episode_id($episode_id); } my $sql_description = ensure_string_is_valid_for_SQL($description); my $sql_directors = ensure_string_is_valid_for_SQL($directors); my $sql_episode_url_for_inetref = ensure_string_is_valid_for_SQL($episode_url_for_inetref); my $sql_thumb_path = ensure_string_is_valid_for_SQL($thumb_path); my $sql_content_rating = ensure_string_is_valid_for_SQL($content_rating); my $sql_fullfilename = ensure_string_is_valid_for_SQL($fullfilename); my $showlevel = parse_show_level($content_rating); if ($mythtv_022 eq 0) { my $sql_episode_title = ensure_string_is_valid_for_SQL($episode_title); $sqlstat = qq { update videometadata set title="$sql_episode_title", plot="$sql_description", director="$sql_directors", year=$year, inetref="$sql_episode_url_for_inetref", coverfile="$sql_thumb_path", length=$video_length, userrating=$rating, rating="$sql_content_rating", showlevel=$showlevel where filename="$sql_fullfilename" }; } else { my $sql_show = ensure_string_is_valid_for_SQL($show); my $sql_episode_name = ensure_string_is_valid_for_SQL($episode_name); my $sql_poster_path = ensure_string_is_valid_for_SQL($poster_path); my $sql_banner_path = ensure_string_is_valid_for_SQL($banner_path); my $sql_fanart_path = ensure_string_is_valid_for_SQL($fanart_path); if ($mythtv_023 eq 0) { $sqlstat = qq { update videometadata set title="$sql_show", subtitle="$sql_episode_name", plot="$sql_description", director="$sql_directors", year=$year, inetref="$sql_episode_url_for_inetref", coverfile="$sql_poster_path", length=$video_length, userrating=$rating, rating="$sql_content_rating", season=$season, episode=$episode, screenshot="$sql_thumb_path", banner="$sql_banner_path", fanart="$sql_fanart_path", showlevel=$showlevel where filename="$sql_fullfilename" }; } else { #fixing paths to NOT include the top folder print "prior: \n$sql_poster_path\n$sql_thumb_path\n$sql_banner_path\n$sql_fanart_path\n$sql_fullfilename\n"; $sql_poster_path = substr($sql_poster_path, length($ArtworkDirectory."/")); $sql_thumb_path = substr($sql_thumb_path, length($ScreenshotDirectory."/")); $sql_banner_path = substr($sql_banner_path, length($BannerDirectory."/")); $sql_fanart_path = substr($sql_fanart_path, length($FanartDirectory."/")); $sql_fullfilename = substr($sql_fullfilename, length($mythvideo_video_dir."/")); print "after: \n$sql_poster_path\n$sql_thumb_path\n$sql_banner_path\n$sql_fanart_path\n$sql_fullfilename\n"; $sqlstat = qq { update videometadata set title="$sql_show", subtitle="$sql_episode_name", plot="$sql_description", director="$sql_directors", year=$year, inetref="$sql_episode_url_for_inetref", coverfile="$sql_poster_path", length=$video_length, userrating=$rating, rating="$sql_content_rating", season=$season, episode=$episode, screenshot="$sql_thumb_path", banner="$sql_banner_path", fanart="$sql_fanart_path", showlevel=$showlevel, host="$local_machine_hostname" where filename="$sql_fullfilename" }; } } $sth = $dbh->prepare($sqlstat); $sth->execute(); print "'$episode_title' is set!\n"; } else { print "Did not find meta-data for episode! Will try as a movie...\n"; $is_tv_show = 0; } } if ($download_subtitles eq 1) { my $full_subtitle_filename = substr($fullfilename, 0, rindex($fullfilename, '.')).".srt"; print "About to download subtitles for file '$fullfilename'. Target subtitle file is '$full_subtitle_filename'\n"; download_tv_subtitle($show, $full_subtitle_filename, $season, $episode); } }; if($@) { print "Error while working on file '$filename'! Will try it as a Movie. Error: $@\n"; $is_tv_show = 0; } } if ($is_tv_show eq 0)#this can be 0 if it was not parsed, or if it could not be found in the TV web-site { #handling meta-data as movie #fixing stuff # Give the filename a more meaningful name my $movie_name = $filename; # Enhanced metadata extraction/removal by Laurie Odgers # Remove metadata from the filename to get the movie name $movie_name =~ s/\.\w+$//; # the extension $movie_name =~ s/^\d+(\.|_|-|\s)//; # track number? $movie_name =~ s/\[\d{4}\](.*)$//; # sometimes videos include "[2008]", I want to remove EVERYTHING after that $movie_name =~ s/[\[,\(,\{]\d{4}[\],\),\}]//; #remove year $movie_name =~ tr/[\.\_\-\[\]!\(\)\:]/ /; # turn delimeter characters into spaces $movie_name =~ s/\s+/ /g; # convert n-whitespaces into a single whitespace $movie_name =~ tr/[A-Z]/[a-z]/; # change movie name to lower case $movie_name =~ s/\s(xvid|divx|h264|x264|ac3|mpg)(.*)$//g; # remove codecs - and everything after $movie_name =~ s/\s(internal|repack|proper|fixed|read nfo|readnfo|unrated|widescreen)(.*)$//g;# remove notes - and everything after $movie_name =~ s/\s(dvdrip|screener|hdtv|dsrip|dsr|dvd|bluray|blueray|720p|hr|workprint)(.*)$//g;# remove sources - and everything after # Part removal enhancement by Laurie Odgers # Remove anything after "part", "cd", "ep" or "webisode" $movie_name =~ s/((part|cd|ep|webisode)[\s]+\d+)(.*)$//g; # Numbering, matches: part 1, cd.1, ep1 # Roman Numerals, matches: part I, cd.V, webisodeX $movie_name =~ s/((part|cd|ep|webisode)[\s]+[i,v,x]+[\s])(.*)$//g; # Matches "moviename - part i [DivX]" $movie_name =~ s/((part|cd|ep|webisode)[\s]+[i,v,x]+$)(.*)$//g; # Matches "moviename - part i" $movie_name = trim($movie_name); if ($movie_name eq "hi 5") { $movie_name = "hi-5"; } eval { my $bad_movie = 0; #this is a movie? my ($imdb_title_url, $imdb_title, $imdb_plot, $imdb_director, $imdb_release_date, $imdb_poster, $imdb_fanart, $imdb_rating, $motechnet_poster); $content_rating ="NR"; if ((length $imdb_id) > 0) { ($imdb_id, $imdb_title_url, $imdb_title, $imdb_plot, $imdb_director, $imdb_release_date, $imdb_poster, $imdb_fanart, $imdb_rating, $motechnet_poster, $content_rating) = get_movie_meta_data_from_tmdb($imdb_id); } else { # get the year released from the filename if possible & my $yearReleased = extract_year_released($filename); my $imdb_possible_ID; my $tmdb_possible_ID; ($imdb_possible_ID, $tmdb_possible_ID) = get_imdb_ID_for_movie_name_tmdb($movie_name, $yearReleased); if (($imdb_possible_ID) or ($tmdb_possible_ID)) { ($imdb_id, $imdb_title_url, $imdb_title, $imdb_plot, $imdb_director, $imdb_release_date, $imdb_poster, $imdb_fanart, $imdb_rating, $motechnet_poster, $content_rating) = get_movie_meta_data_from_tmdb($imdb_possible_ID, $tmdb_possible_ID); } else { print "Can not load movie data for '$movie_name'.\n"; $bad_movie = 1; } } if ($bad_movie == 1) { my $sql_movie_name = ensure_string_is_valid_for_SQL($movie_name); $sqlstat=qq {update videometadata set title = "$sql_movie_name", browse=1 where filename="$sql_fullfilename"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); next; } my $video_length = get_video_length($fullfilename); my $thumb_path = $filename.".jpg"; my $fanart_path = $filename.".jpg"; #making the name a bit more freindly... $thumb_path =~ s/ /_/gi; $thumb_path =~ s/'/_/gi; $fanart_path =~ s/ /_/gi; $fanart_path =~ s/'/_/gi; #now adding to the full path - this is done here, so it will be possible to have spaces in the artwork folder $thumb_path = $ArtworkDirectory."/".$thumb_path; $fanart_path = $FanartDirectory."/".$fanart_path; if ($movies_snapshot eq 2) # use external script to retrieve posters { print "Attempting to get poster using the following command:\n"; print "$external_poster_command_line $imdb_id\n"; my $poster_url = qx{$external_poster_command_line $imdb_id}; download_poster($poster_url, $thumb_path); } elsif ($movies_snapshot eq 0) # retrieve posters from the-movie-db { my $good = 0; if ($motechnet_poster) { print "downloading poster from '$motechnet_poster'...\n"; $good = download_poster($motechnet_poster, $thumb_path); # first download from motechnet } if ($good eq 0) # if the motechnet download failed { if ($imdb_poster) # check to see if we have an imdb poster queued up, and if so download it { print "downloading poster from '$imdb_poster'...\n"; $good = download_poster($imdb_poster, $thumb_path); } if ($good eq 0) # if all this poster downloading business fails, just create a snapshot and use that { print "Creating snapshot for '$fullfilename'...\n"; create_video_thumb($fullfilename, $video_length, $thumb_path); } } } else # anything else means just create a thumbnail for the snapshot { print "Creating snapshot for '$fullfilename'...\n"; create_video_thumb($fullfilename, $video_length, $thumb_path); } if (($mythtv_022 ne 0) && ($imdb_fanart) && ($fanart_path)) { #download fanart download_poster($imdb_fanart, $fanart_path); } #what about the folder? my $folder_thumb_path = $directory."folder.jpg"; if ((-e $thumb_path) && (no_folder_art_at_folder($directory) eq 1)) { system("cp '$thumb_path' '$folder_thumb_path'"); } $imdb_plot .= "\n".$filename; print "found ($imdb_id) $imdb_title: $imdb_plot\n"; $imdb_title = format_movie_title($filename, $imdb_title, $movie_title_format); my $sql_imdb_title = ensure_string_is_valid_for_SQL($imdb_title); my $sql_imdb_plot = ensure_string_is_valid_for_SQL($imdb_plot); my $sql_imdb_director = ensure_string_is_valid_for_SQL($imdb_director); my $sql_imdb_release_date = ensure_string_is_valid_for_SQL($imdb_release_date); my $sql_thumb_path = ensure_string_is_valid_for_SQL($thumb_path); my $sql_imdb_id = ensure_string_is_valid_for_SQL($imdb_id); my $sql_content_rating = ensure_string_is_valid_for_SQL($content_rating); my $sql_fullfilename = ensure_string_is_valid_for_SQL($fullfilename); my $showlevel = parse_show_level($content_rating); if ($mythtv_022 eq 0) { $sqlstat = qq { update videometadata set title="$sql_imdb_title", plot="$sql_imdb_plot", director="$sql_imdb_director", year="$sql_imdb_release_date", coverfile="$sql_thumb_path", length=$video_length, userrating=$imdb_rating, inetref="$sql_imdb_id", rating="$sql_content_rating", showlevel=$showlevel where filename="$sql_fullfilename" }; } else { my $sql_fanart_path = ensure_string_is_valid_for_SQL($fanart_path); $sqlstat = qq { update videometadata set title="$sql_imdb_title", plot="$sql_imdb_plot", director="$sql_imdb_director", year="$sql_imdb_release_date", coverfile="$sql_thumb_path", length=$video_length, userrating=$imdb_rating, inetref="$sql_imdb_id", rating="$sql_content_rating", fanart="$sql_fanart_path", showlevel=$showlevel where filename="$sql_fullfilename" }; } #print "SQL:\n".$sqlstat."\n"; $sth = $dbh->prepare($sqlstat); $sth->execute(); print "'$imdb_title' is set!\n"; $updated_files++; }; if ($@) { print "Error while working on file '$filename'! Error: $@\n"; } } } print "======================================================================\n"; print "Finished going over all the files: Found $total_files_count, updated $updated_files files.\n"; my $files_count_for_deletion = 0; print "Going over all video files in the database, and deleting any redundant entries (i.e., video files no longer exist)...\n"; $sqlstat=qq {select filename from videometadata}; $sth = $dbh->prepare($sqlstat); $sth->execute(); my $video_file_name; $sth->bind_columns( undef, \$video_file_name ); my @files_to_delete; while ($sth->fetch()) { if ((! $video_file_name) || (length $video_file_name == 0) || (! -e $video_file_name)) { #print "Video file '$video_file_name' does not exist in filsystem! Will be removed from DB...\n"; push(@files_to_delete, $video_file_name); $files_count_for_deletion++; } else { #print "Video file '$video_file_name' exists in filsystem.\n"; } } foreach(@files_to_delete) { my $file_to_delete = $_; print "Deleting '$file_to_delete' from database...\n"; my $sql_file_to_delete = ensure_string_is_valid_for_SQL($file_to_delete); $sqlstat=qq {delete from videometadata where filename="$sql_file_to_delete"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); } print "Deleted $files_count_for_deletion entries from database!\n"; print "======================================================================\n"; if ($cleanArtFolder eq 1) { print "Going over all image files in the artwork folder (at '$ArtworkDirectory'), and deleting any redundant image files (i.e., owner video file does not exist)...\n"; $files_count_for_deletion = 0; my $image_files_path = $ArtworkDirectory."/*.jpg"; my @image_files = glob($image_files_path); foreach (@image_files) { my $full_image_filename = $_; #print "Image: '$full_image_filename'\n"; eval { my $sql_full_image_filename = ensure_string_is_valid_for_SQL($full_image_filename); $sqlstat=qq {select coverfile from videometadata where coverfile="$sql_full_image_filename"}; $sth = $dbh->prepare($sqlstat); $sth->execute(); my $cover_file; $sth->bind_columns( undef, \$cover_file ); my $video_exists = 0; if (!$sth->fetch()) { $video_exists = 0; } elsif (($cover_file) && (length $cover_file > 0)) { $video_exists = 1; } else { $video_exists = 0; } if ($video_exists eq 1) { #print "'$full_image_filename' is a cover for a video. It will not be deleted.\n"; } else { print "Image '$full_image_filename' does not exist in the DB metadata. deleting the file...\n"; unlink($full_image_filename); $files_count_for_deletion++; } }; if ($@) { print "Error while working on file '$full_image_filename'! Error: $@\n"; } } print "Deleted $files_count_for_deletion redundant image files from the artwork folder.\n"; print "======================================================================\n"; } print "Completing database...\n"; $sth->finish(); $dbh->disconnect(); print "======================================================================\n"; print "Thanks for using MythVideo Metadata Updater v$script_version.\n"; print "======================================================================\n"; release_lock_file();