private static boolean addHuluOrCBSTvEpisodeMetaData( MyLibraryFile video, String seasonEpisodeNaming) { // parse season/episode numbers try { // get the series and title boolean normalMethodSuccess = Archiver.addTVMetaDataFromSxxExx(video, seasonEpisodeNaming); if (!normalMethodSuccess) { // This means the series title comes before this in the fileLabel // looks like: "Pretty Little Liars - s1e1 - Pilot" or "Pretty Little Liars - s1e1: Pilot" String[] splitters = new String[] {" - " + seasonEpisodeNaming + " - ", " - " + seasonEpisodeNaming + ": "}; for (String matchOn : splitters) { int matchIndex = video.getFileLabel().indexOf(matchOn); if (matchIndex != -1) { video.setSeries(video.getFileLabel().substring(0, matchIndex)); // get the title video.setTitle( video .getFileLabel() .substring(matchIndex + matchOn.length(), video.getFileLabel().length())); break; } } } if (!valid(video.getSeries())) { Logger.WARN("Series cannot be found from Hulu/CBS path: " + video.getFullPathEscaped()); return false; } // get the title (not required for scraping if (!valid(video.getTitle())) { Logger.INFO( "Title cannot be parsed for this video, it will be set to the file name: \"" + video.getFileLabel() + "\""); video.setTitle(video.getFileLabel()); } return true; } catch (Exception x) { Logger.WARN( "This Hulu/CBS video was thought to be a TV show, but the season/episode numbers and/or title could not be parsed: " + video.getFullPathEscaped(), x); return false; } }
public static boolean doHuluCBS(MyLibraryFile video, boolean isHulu, boolean isCBS) { String matchingPattern = null; if (!video.knownType() || video.isTvShow()) { Pattern seasonEpisodePattern = Pattern.compile("s[0-9]+e[0-9]+", Pattern.CASE_INSENSITIVE); Matcher seasonEpisodeMatcher = seasonEpisodePattern.matcher(video.getFullPath()); boolean match = seasonEpisodeMatcher.find(); if (match) { matchingPattern = seasonEpisodeMatcher.group(); video.setType(TV_SHOW); } else if (!video.isTvShow()) // dont over-ride config param video.setType(MOVIE); } boolean success; if (video.isTvShow()) success = addHuluOrCBSTvEpisodeMetaData(video, matchingPattern); else if (video.isMovie()) { video.setTitle(video.getFileLabel()); success = valid(video.getTitle()); } else // do hulu/cbs have music videos? { Logger.WARN( "Cannot Archive: Type of content cannot be auto-determined for Hulu video: " + video.getFullPathEscaped()); success = false; } Logger.DEBUG( (isCBS ? "CBS" : "Hulu") + ": success=" + success + "; " + (video.isTvShow() ? "TV: series=" + video.getSeries() + "; title=" + video.getTitle() + ", " + "season=" + video.getSeasonNumber() + ", episode=" + video.getEpisodeNumber() : "Movie: " + video.getTitle()) + " --- " + video.getFullPathEscaped()); return success; }
public static void copyVideoMetaData(MyLibraryFile source, MyLibraryFile dest) { dest.setTitle(source.getTitle()); dest.setType(source.getType()); dest.setArtist(source.getArtist()); dest.setEpisodeNumber(source.getEpisodeNumber()); dest.setFileLabel(source.getFileLabel()); dest.setFinalLocation(source.getFinalLocation()); dest.setHasBeenLookedUpOnTVDB(source.hasBeenLookedUpOnTVDB()); dest.setMultiFileVideo(source.isMultiFileVideo()); dest.setOriginalAirDate(source.getOriginalAirDate()); if (source.isDuplicate()) dest.setAsDuplicateTo(source.getOriginalVideo()); else dest.setOriginalVideo(source.getOriginalVideo()); dest.setSeasonNumber(source.getSeasonNumber()); dest.setSeries(source.getSeries()); dest.setSkippedBecauseAlreadyArchived(source.skippedBecauseAlreadyArchived); dest.setSubfolder(source.getSubfolder()); dest.setTVDBId(source.getTVDBId()); dest.setYear(source.getYear()); }
public static boolean lookupTVShow(MyLibraryFile video) { video.setHasBeenLookedUpOnTVDB(true); boolean lookupWasSuccessful = false; // determine if this has already been looked up successfully and skip the query if (video.getSubfolder() == null || !video.getSubfolder().forceTVDBLookup()) { // tvdb lookup is NOT forced true Logger.DEBUG( "Checking if this video has already been looked up on TVDB based on original path of: " + video.getFullPathEscaped()); MyLibraryFile previouslyLookedupVideo = tools.getVideoFromOriginalLocation(video.getFullPath()); // not escaped if (previouslyLookedupVideo == null || !previouslyLookedupVideo.hasBeenLookedUpOnTVDB()) { Logger.INFO( "This video has not been succesfully looked up on the TVDB before, will attempt lookup now."); } else { if (previouslyLookedupVideo.hasValidMetaData()) { Logger.INFO( "This video has previously been successfully looked up on the TVDB, will use saved meta-data instead of querying TVDB.com"); // copy over all data // make sure we keep the original sufolder and don't use the dummy subfolder Subfolder subf = video.getSubfolder(); MyLibraryFile.copyVideoMetaData(previouslyLookedupVideo, video); video.setSubfolder(subf); return true; } else Logger.INFO( "This video has been queried on the TVDB before, but it was unsuccessful. Will try again now: " + video.getFullPathEscaped()); } } else Logger.DEBUG( "Force TVDB lookup is true, skipping DB data check and continuing with TVDB lookup."); String seriesTitle = video.getSeries(); String episodeTitle = video.getTitle(); String originalAirDate = video.getOriginalAirDate(); boolean canLookupSeriedId = valid(seriesTitle) || video.isTVDBIdOverridden(); if (!canLookupSeriedId) { Logger.WARN( "Cannot lookup TV series because series name is unknown: " + video.getFullPathEscaped()); return false; } boolean canLookupEpisode = valid(episodeTitle) || valid(originalAirDate); if (!canLookupEpisode) { Logger.WARN( "Cannot lookup TV episode because neither episode title nor original air date are known: " + video.getFullPathEscaped()); return false; } String tvdbURL = null; List<String> seriesIds = new ArrayList<String>(); try // tvdb lookup { int maxSeries = 3; if (!video.isTVDBIdOverridden()) { tvdbURL = "http://www.thetvdb.com/api/GetSeries.php?seriesname=" + java.net.URLEncoder.encode(seriesTitle, "UTF-8"); Logger.DEBUG( "Attempting to get series IDs (max of " + maxSeries + ") based on seriesname of '" + seriesTitle + "', url = " + tvdbURL); java.net.URL URL = new java.net.URL(tvdbURL); Document xml = tools.getXMLFromURL(URL); List<Element> children = xml.getRootElement().getChildren(); int seriesCount = 0; for (Element series : children) { seriesCount++; String seriesId = series.getChildText("seriesid"); seriesIds.add(seriesId); String seriesName = series.getChildText("SeriesName"); Logger.DEBUG( "Adding Series #" + seriesCount + " found from thetvdb: seriesName= \"" + seriesName + "\" id = \"" + seriesId + "\""); if (seriesCount == maxSeries) break; } if (seriesIds.isEmpty()) { Logger.WARN( "Ending lookup. No series could be found by querying TheTVDB: " + tvdbURL + ". Will try to archive this video again later."); return false; // unsuccessful lookup } } else { seriesIds.add(video.getTVDBId()); Logger.INFO( "TheTVDB series ID is already known, no need to look it up. Using ID of: \"" + video.getTVDBId() + "\""); } boolean originalAirDateIsAvailable = valid(tools.normalize(video.getOriginalAirDate())); if (!originalAirDateIsAvailable) Logger.DEBUG("Original air date is not known, will not try to match on it."); boolean episodeTitleIsAvailable = valid(tools.normalize(video.getTitle())); if (!episodeTitleIsAvailable) Logger.DEBUG("No episode title is available, will not try to match on it."); if (!originalAirDateIsAvailable && !episodeTitleIsAvailable) { Logger.WARN( "Neither title nor original air date are available. Need at least 1 to lookup on thetvdb.com. File = " + video.getFullPathEscaped() + "."); return false; } for (String seriesId : seriesIds) { // get episode info by orig air date and title tvdbURL = "http://www.thetvdb.com/api/" + Config.TVDB_API_KEY + "/series/" + seriesId + "/all/en.xml"; Logger.INFO( "Attempting to find matching episode with" + (originalAirDateIsAvailable ? " original air date = \"" + tools.normalize(originalAirDate) + "\"" : "") + (episodeTitleIsAvailable ? " title = \"" + tools.normalize(episodeTitle) + "\"" : "") + " from thetvdb, url = " + tvdbURL.replace(Config.TVDB_API_KEY, Config.TVDB_API_KEY_OBSCURED)); java.net.URL URL = new java.net.URL(tvdbURL); Document xml = tools.getXMLFromURL(URL); // TODO: implement // if(xml != null && xml.getRootElement() != null) // addTVDBQuery(video.getFullPathToOriginalRecording(),System.currentTimeMillis());//track // query time to prevent overly-frequent queries List<Element> episodes = xml.getRootElement().getChildren("Episode"); // if both can not be matched on, will use a single match if it exists. single match means 1 // and only 1 episode matched on 1 of the criteria boolean singleMatch = false; boolean hasTVDBImage = false; String criteriaUsedForSingleMatch = null; String singleMatchTVDBFullInfo = null; Element singleMatchEpisode = null; Logger.DEBUG( "Found " + episodes.size() + " episodes for series id " + seriesId + ". Will look for match now..."); if (episodes == null || episodes.isEmpty()) { Logger.WARN( "No episodes found on TheTVDB for series " + seriesTitle + " (" + seriesId + "). " + "You may need to add the series/episodes on TheTVDB.com, or manually provide the correct TVDB id in the config file."); return false; } else // look for matching episode for (Iterator<Element> i = episodes.iterator(); i.hasNext(); ) { boolean isFuzzyMatch = false; // dfault boolean titleMatch = false; // dfault boolean dateMatch = false; // dfault Element episode = i.next(); String tvdbFirstAired = episode.getChildText("FirstAired"); String tvdbEpisodeTitle = episode.getChildText("EpisodeName"); String tvdbSeasonNumber = episode.getChildText("SeasonNumber"); String tvdbEpisodeNumber = episode.getChildText("EpisodeNumber"); hasTVDBImage = valid( episode.getChildText( "filename")); // <filename> stores the path to the .jpg image for the // episode String tvdbFullInfo = " Season " + tvdbSeasonNumber + ", Episode " + tvdbEpisodeNumber + ", Titled \"" + tvdbEpisodeTitle + "\", " + "first aired on " + tvdbFirstAired + ", has episode image: " + hasTVDBImage; if (originalAirDateIsAvailable) { if (tools .normalize(video.getOriginalAirDate()) .equalsIgnoreCase(tools.normalize(tvdbFirstAired))) { dateMatch = true; Logger.DEBUG("DATE MATCH: " + tvdbFullInfo); if (!singleMatch) { singleMatch = true; criteriaUsedForSingleMatch = "Original Air Date"; singleMatchTVDBFullInfo = tvdbFullInfo; singleMatchEpisode = episode; } else // single match was already found, this is another match, means not a single // match anymore { singleMatch = false; // a second (or greater) match was found. There is no longer a single // episode match } } else Logger.DEBUG( "NO DATE MATCH: \"" + tools.normalize(video.getOriginalAirDate()) + "\" != \"" + tools.normalize(tvdbFirstAired) + "\""); } else // no orig air date avail, dont need to find a match for it { dateMatch = true; } if (episodeTitleIsAvailable) { boolean exactMatch = tools.normalize(episodeTitle).equalsIgnoreCase(tools.normalize(tvdbEpisodeTitle)); boolean fuzzyMatch = false; if (!exactMatch) fuzzyMatch = tools.fuzzyTitleMatch( episodeTitle, tvdbEpisodeTitle, 15); // allow 15 percent discrepency if (exactMatch || fuzzyMatch) { if (fuzzyMatch) isFuzzyMatch = true; titleMatch = true; Logger.DEBUG((fuzzyMatch ? "FUZZY " : "") + "TITLE MATCH: " + tvdbFullInfo); if (!singleMatch) { singleMatch = true; criteriaUsedForSingleMatch = "Episode Title"; singleMatchTVDBFullInfo = tvdbFullInfo; singleMatchEpisode = episode; } else // single match was already found, this is another match, means not a single // match anymore { singleMatch = false; // a second (or greater) match was found. There is no longer a single // episode match } } } else // no title is available, dont need to find a match for it { titleMatch = true; } if (titleMatch && dateMatch) { Logger.INFO("Title and date match, saving data for this match..."); // add the season/episode numbers if (!video.addTVDBSeriesEpisodeNumbers(tvdbSeasonNumber, tvdbEpisodeNumber)) { Logger.ERROR( "Found a match on thetvdb.com for " + video.getFullPathEscaped() + ", " + "but the season and episode numbers are invalid (\"" + tvdbSeasonNumber + "\", \"" + tvdbEpisodeNumber + "\"). " + "Skipping..."); return false; } if (!valid(episodeTitle)) { if (valid(tvdbEpisodeTitle)) { Logger.DEBUG( "Setting video title to title from the TVDB: \"" + tvdbEpisodeTitle + "\""); video.setTitle(tvdbEpisodeTitle); } } lookupWasSuccessful = true; Logger.INFO( "SUCCESSFUL LOOKUP" + (isFuzzyMatch ? " (fuzzy match)" : " (exact match)") + ": Found matching episdode on thetvdb: " + tvdbFullInfo); if (tvdbSeasonNumber.equals("0") || isFuzzyMatch) { if (isFuzzyMatch) Logger.DEBUG( "Since this was a fuzzy match, will continue to search for exact matches and only use this match if no exact matches exist."); else Logger.DEBUG( "Since the season number is zero, will continue to look for matching episodes, and use a 'regular' season matching episode if it exists. " + "Otherwise will fall back to this 'special' episode."); } else break; // end loop } } // end looking for matching episode if (!lookupWasSuccessful) // if there was no traditional match, check for single criteria // match { if (singleMatch) { Logger.INFO( "Could not match on all criteria, but a single match was found based on " + criteriaUsedForSingleMatch + ". " + "Will use episode info: " + singleMatchTVDBFullInfo); String seasonNum = singleMatchEpisode.getChildText("SeasonNumber"); String episodeNum = singleMatchEpisode.getChildText("EpisodeNumber"); if (!video.addTVDBSeriesEpisodeNumbers(seasonNum, episodeNum)) { Logger.ERROR( "Found a multi-part match on thetvdb.com for " + video.getFullPathEscaped() + ", " + "but the season and episode numbers are invalid (\"" + seasonNum + "\", \"" + episodeNum + "\"). " + "Skipping and trying again later."); return false; } lookupWasSuccessful = true; } else { Logger.INFO( "NOT FOUND: Could not find any episode that matched " + (originalAirDateIsAvailable ? "original air date (" + video.getOriginalAirDate() + "), " : "") + (episodeTitleIsAvailable ? " episode title (" + video.getTitle() + ")" : "") + " for TVDB series id " + seriesId + "." + " For video at: " + video.getFullPathEscaped()); lookupWasSuccessful = false; } } // end if lookup was unsuccessful and check for multi episode / single parameter match if (lookupWasSuccessful) { video.setTVDBId(seriesId); // video.setHasTVDBImage(hasTVDBImage); return lookupWasSuccessful; } } // end looping through series ids return lookupWasSuccessful; // looped through all series ids } catch (Exception x) { Logger.ERROR( "Failed to get episode information from the TVDB for " + video.getFullPathEscaped() + ", URL = " + tvdbURL, x); return false; } }
private static boolean addNetflixTVSeriesAndTitle(MyLibraryFile video) { try { // find series and title String series = null, title = null; try // this method must be tried first (even though it matches less files); try using the // filename spit at ":"... catches things line Bob the Builder: Call in the Crew { if (video .getFileLabel() .contains(": ")) // somethign like /Bob the Builder: Call in the Crew { String[] sa = video.getFileLabel().split(": "); if (sa.length != 2) throw new Exception("File label contains more than one ':'. Cannot parse."); series = sa[0]; title = sa[1].trim(); boolean is24 = (series.trim().equals("24") || video.getFullPathEscaped().contains("/24/")) && (title.toLowerCase().contains("a.m") || title.toLowerCase().contains("p.m")); if (!is24) // exclude 24 becuase the series should be an integer { series = "24"; if (isInt(series)) // this is actually the episode number, not the series. { series = null; throw new Exception("Incorrect series (Integer found as series name)"); } } } else throw new Exception("No colon in file label, try secondary method"); } catch ( Exception x) // try secondary method, getting series as the parent folder and the file label // being the title { series = null; // Netflix/Instant Queue/#/30 Days/30 Days: Season 3/S03E03 - Animal Rights String[] folders = video.getFullPath().split(com.bradvido.xbmc.util.Constants.DELIM); if (folders.length > 1) { String parentFolder = folders[folders.length - 2]; // 30 Days: Season 3 if (valid(parentFolder) && parentFolder.contains(": ")) { series = parentFolder.substring(0, parentFolder.indexOf(": ")); } } // try looking in the folder(s) above this show for the series if (series == null) { // couldn't find it the conventional way if (Archiver.getSeriesFromParentFolder(video)) { series = video .getSeries(); // series was set in getSeriesFromParentFolder(). get it back // here for further checks } else { // didn't work try secondary method String[] serieses = video.getFullPath().split(com.bradvido.xbmc.util.Constants.DELIM); series = serieses[serieses.length - 2]; // the folder above the file name if (series.contains( ":")) // catches folders named like so: /Busytown Mysteries: Series 2/ series = series.substring(0, series.indexOf(":")); } } // get title (usually split from SxxExx by one of these) title = video.getFileLabel(); // S03E01 - Working in a Coal Mine String[] splitters = new String[] {" - ", ":"}; for (String splitter : splitters) { if (title.contains(splitter)) { title = title.substring(title.indexOf(splitter) + splitter.length(), title.length()); break; } } } if (!valid(series)) throw new Exception("Series cannot be parsed..."); video.setSeries(series.trim()); if (!valid(title)) throw new Exception("Title cannot be parsed..."); video.setTitle(title.trim()); return true; // successfully got the series and title // Logger.INFO( "Netflix episode meta data: series="+file.getSeries()+"; // title="+file.getTitle()+", season="+file.getSeasonNumber()+", // episode="+file.getEpidoseNumber()); } catch (Exception x) { if (!valid(video.getSeries()) || !valid(video.getTitle())) { Logger.WARN( "Cannot parse, and cannot lookup Netflix TV show (series=" + video.getSeries() + ", title=" + video.getTitle() + ")" + LINE_BRK + video.getFullPathEscaped() + LINE_BRK + x.getMessage()); return false; } else { Logger.INFO( "Cannot parse, but can lookup Netflix TV show (series=" + video.getSeries() + ", title=" + video.getTitle() + ")" + LINE_BRK + video.getFullPathEscaped() + LINE_BRK + x.getMessage()); Logger.INFO("Attempting to find metadata on TheTVDB.com"); return TVDB.lookupTVShow(video); } } }