/**
   * Find tv episodes.
   *
   * @param tvShow the tv show
   * @param dir the dir
   */
  private void findTvEpisodes(TvShow tvShow, File dir) {
    LOGGER.debug("parsing " + dir.getPath());
    // crawl this folder and try to find every episode and its corresponding files in it
    File[] content = dir.listFiles();
    if (content == null) {
      LOGGER.error("Whops. Cannot access directory: " + dir.getName());
      return;
    }

    Arrays.sort(content);
    for (File file : content) {
      if (file.isFile()) {
        if (!file.getName().startsWith(skipFilesStartingWith)) {
          MediaFile mf = new MediaFile(file);
          // check filetype - we only proceed here if it's a video file
          if (!mf.getType().equals(MediaFileType.VIDEO)) {
            continue;
          }

          // is this file already assigned to another episode?
          List<TvShowEpisode> episodes = tvShowList.getTvEpisodesByFile(tvShow, file);
          if (episodes.size() == 0) {
            // try to check what episode//season
            // EpisodeMatchingResult result =
            // TvShowEpisodeAndSeasonParser.detectEpisodeFromFilename(file);
            String relativePath =
                new File(tvShow.getPath()).toURI().relativize(file.toURI()).getPath();
            EpisodeMatchingResult result =
                TvShowEpisodeAndSeasonParser.detectEpisodeFromFilenameAlternative(
                    relativePath, tvShow.getTitle());

            // second check: is the detected episode (>-1; season >-1) already in tmm and any valid
            // stacking markers found?
            if (result.episodes.size() == 1 && result.season > -1 && result.stackingMarkerFound) {
              // get any assigned episode
              TvShowEpisode ep = tvShow.getEpisode(result.season, result.episodes.get(0));
              if (ep != null) {
                ep.setNewlyAdded(true);
                ep.addToMediaFiles(mf);
                continue;
              }
            }

            if (result.episodes.size() == 0) {
              // try to parse out episodes/season from parent directory
              result =
                  TvShowEpisodeAndSeasonParser.detectEpisodeFromDirectory(dir, tvShow.getPath());
            }

            if (result.season == -1) {
              // did the search find a season?
              // no -> search for it in the folder name (relative path between tv show root and the
              // current dir)
              result.season = TvShowEpisodeAndSeasonParser.detectSeason(relativePath);
            }

            List<TvShowEpisode> episodesInNfo = TvShowEpisode.parseNFO(file);

            // did we find any episodes in the NFO?
            if (episodesInNfo.size() > 0) {
              // these have priority!
              for (TvShowEpisode e : episodesInNfo) {
                e.setPath(dir.getPath());
                e.setTvShow(tvShow);
                e.addToMediaFiles(mf);
                e.setDateAddedFromMediaFile(mf);
                findAdditionalEpisodeFiles(e, file, content);
                e.setNewlyAdded(true);
                e.saveToDb();
                tvShow.addEpisode(e);
              }
            } else if (result.episodes.size() > 0) {
              // something found with the season detection?
              for (int ep : result.episodes) {
                TvShowEpisode episode = new TvShowEpisode();
                episode.setDvdOrder(Globals.settings.getTvShowSettings().isDvdOrder());
                episode.setEpisode(ep);
                episode.setSeason(result.season);
                episode.setFirstAired(result.date);

                if (result.name.isEmpty()) {
                  result.name = FilenameUtils.getBaseName(file.getName());
                }
                episode.setTitle(result.name);

                episode.setPath(dir.getPath());
                episode.setTvShow(tvShow);
                episode.addToMediaFiles(mf);
                episode.setDateAddedFromMediaFile(mf);
                findAdditionalEpisodeFiles(episode, file, content);
                episode.setNewlyAdded(true);
                episode.saveToDb();
                tvShow.addEpisode(episode);
              }
            } else {
              // episode detection found nothing - simply add this file
              TvShowEpisode episode = new TvShowEpisode();
              episode.setDvdOrder(Globals.settings.getTvShowSettings().isDvdOrder());
              episode.setEpisode(-1);
              episode.setSeason(-1);
              episode.setPath(dir.getPath());

              episode.setTitle(FilenameUtils.getBaseName(file.getName()));
              episode.setTvShow(tvShow);
              episode.setFirstAired(result.date);
              episode.addToMediaFiles(mf);
              episode.setDateAddedFromMediaFile(mf);
              findAdditionalEpisodeFiles(episode, file, content);
              episode.setNewlyAdded(true);
              episode.saveToDb();
              tvShow.addEpisode(episode);
            }
          } else {
            // episode already added; look if any new additional files have been added
            for (TvShowEpisode episode : episodes) {
              if (findAdditionalEpisodeFiles(episode, file, content)) {
                episode.saveToDb();
              }
            }
          }
        } // end skipFilesStartingWith
      } // end isFile

      if (file.isDirectory()
          && !skipFolders.contains(file.getName().toUpperCase())
          && !file.getName().matches(skipFoldersRegex)
          && !TvShowModuleManager.TV_SHOW_SETTINGS
              .getTvShowSkipFolders()
              .contains(file.getAbsolutePath())) {
        // check if that directory contains a .tmmignore file
        File tmmIgnore = new File(file, ".tmmignore");
        if (!tmmIgnore.exists()) {
          // dig deeper
          if (file.getName().toUpperCase().equals("VIDEO_TS")) {
            findTvEpisodesAsDisc(tvShow, file);
          } else if (file.getName().toUpperCase().equals("BDMV")) {
            findTvEpisodesAsDisc(tvShow, file);
          } else {
            findTvEpisodes(tvShow, file);
          }
        }
      }
    }
  }
  /*
   * update one or more datasources
   */
  private void updateDatasource() {
    List<File> imageFiles = new ArrayList<File>();

    for (String path : dataSources) {
      File[] dirs = new File(path).listFiles();
      // check whether the path is accessible (eg disconnected shares)
      if (dirs == null || dirs.length == 0) {
        // error - continue with next datasource
        LOGGER.warn("Datasource not available/empty " + path);
        MessageManager.instance.pushMessage(
            new Message(
                MessageLevel.ERROR,
                "update.datasource",
                "update.datasource.unavailable",
                new String[] {path}));
        continue;
      }

      // one thread here - more threads killed the UI
      initThreadPool(1, "update");

      for (File subdir : dirs) {
        if (cancel) {
          break;
        }

        String directoryName = subdir.getName();
        // check against unwanted dirs
        if (skipFolders.contains(directoryName.toUpperCase())
            || directoryName.matches(skipFoldersRegex)
            || TvShowModuleManager.TV_SHOW_SETTINGS
                .getTvShowSkipFolders()
                .contains(subdir.getAbsolutePath())) {
          LOGGER.info("ignoring directory " + directoryName);
          continue;
        }

        // check this dir as TV show dir
        if (subdir.isDirectory()) {
          // check if there is a .tmmignore in this directory
          File tmmIgnore = new File(subdir, ".tmmignore");
          if (!tmmIgnore.exists()) {
            submitTask(new FindTvShowTask(subdir, path));
          }
        }

        // video FILE in DS root - not supported!
        if (subdir.isFile()
            && Globals.settings
                .getVideoFileType()
                .contains("." + FilenameUtils.getExtension(subdir.getName()))) {
          MessageManager.instance.pushMessage(
              new Message(
                  MessageLevel.ERROR,
                  "update.datasource",
                  "update.datasource.episodeinroot",
                  new String[] {subdir.getName()}));
        }
      }

      waitForCompletionOrCancel();
      if (cancel) {
        break;
      }

      // cleanup
      setTaskName(BUNDLE.getString("update.cleanup"));
      setTaskDescription(null);
      setProgressDone(0);
      setWorkUnits(0);
      publishState();
      LOGGER.info("removing orphaned tv shows/files...");
      for (int i = tvShowList.getTvShows().size() - 1; i >= 0; i--) {
        if (cancel) {
          break;
        }
        TvShow tvShow = tvShowList.getTvShows().get(i);
        if (!new File(path).equals(new File(tvShow.getDataSource()))) {
          // check only Tv shows matching datasource
          continue;
        }

        File tvShowDir = new File(tvShow.getPath());
        if (!tvShowDir.exists()) {
          tvShowList.removeTvShow(tvShow);
        } else {
          // do a cleanup
          cleanup(tvShow);
        }
      }

      // mediainfo
      setTaskName(BUNDLE.getString("update.mediainfo"));
      publishState();

      initThreadPool(1, "mediainfo");
      LOGGER.info("getting Mediainfo...");
      for (int i = tvShowList.getTvShows().size() - 1; i >= 0; i--) {
        if (cancel) {
          break;
        }
        TvShow tvShow = tvShowList.getTvShows().get(i);
        if (!new File(path).equals(new File(tvShow.getDataSource()))) {
          // check only Tv shows matching datasource
          continue;
        }

        gatherMediaInformationForUngatheredMediaFiles(tvShow);
      }

      waitForCompletionOrCancel();
      if (cancel) {
        break;
      }

      // build image cache on import
      if (Globals.settings.getTvShowSettings().isBuildImageCacheOnImport()) {
        for (TvShow tvShow : new ArrayList<TvShow>(tvShowList.getTvShows())) {
          if (!new File(path).equals(new File(tvShow.getDataSource()))) {
            continue;
          }
          for (MediaFile mf : new ArrayList<MediaFile>(tvShow.getMediaFiles())) {
            if (mf.isGraphic()) {
              imageFiles.add(mf.getFile());
            }
          }
          for (TvShowEpisode episode : tvShow.getEpisodes()) {
            for (MediaFile mf : new ArrayList<MediaFile>(episode.getMediaFiles())) {
              if (mf.isGraphic()) {
                imageFiles.add(mf.getFile());
              }
            }
          }
        }
      }
    }

    if (cancel) {
      return;
    }

    if (imageFiles.size() > 0) {
      ImageCacheTask task = new ImageCacheTask(imageFiles);
      TmmTaskManager.getInstance().addUnnamedTask(task);
    }

    if (Globals.settings.getTvShowSettings().getSyncTrakt()) {
      TmmTask task = new SyncTraktTvTask(false, false, true, true);
      TmmTaskManager.getInstance().addUnnamedTask(task);
    }
  }