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());
 }
Example #4
0
  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);
      }
    }
  }