/* * detect which mediafiles has to be parsed and start a thread to do that */ private void gatherMediaInformationForUngatheredMediaFiles(TvShow tvShow) { // get mediainfo for tv show (fanart/poster..) ArrayList<MediaFile> ungatheredMediaFiles = new ArrayList<MediaFile>(); for (MediaFile mf : tvShow.getMediaFiles()) { if (StringUtils.isBlank(mf.getContainerFormat())) { ungatheredMediaFiles.add(mf); } } if (ungatheredMediaFiles.size() > 0) { submitTask(new MediaFileInformationFetcherTask(ungatheredMediaFiles, tvShow, false)); } // get mediainfo for all episodes within this tv show for (TvShowEpisode episode : new ArrayList<TvShowEpisode>(tvShow.getEpisodes())) { ungatheredMediaFiles = new ArrayList<MediaFile>(); for (MediaFile mf : episode.getMediaFiles()) { if (StringUtils.isBlank(mf.getContainerFormat())) { ungatheredMediaFiles.add(mf); } } if (ungatheredMediaFiles.size() > 0) { submitTask(new MediaFileInformationFetcherTask(ungatheredMediaFiles, episode, false)); } } }
private void cleanup(TvShow tvShow) { boolean dirty = false; if (!tvShow.isNewlyAdded()) { // check and delete all not found MediaFiles List<MediaFile> mediaFiles = new ArrayList<MediaFile>(tvShow.getMediaFiles()); for (MediaFile mf : mediaFiles) { if (!mf.getFile().exists()) { tvShow.removeFromMediaFiles(mf); dirty = true; } } List<TvShowEpisode> episodes = new ArrayList<TvShowEpisode>(tvShow.getEpisodes()); for (TvShowEpisode episode : episodes) { mediaFiles = new ArrayList<MediaFile>(episode.getMediaFiles()); for (MediaFile mf : mediaFiles) { if (!mf.getFile().exists()) { episode.removeFromMediaFiles(mf); dirty = true; } } // lets have a look if there is at least one video file for this episode List<MediaFile> mfs = episode.getMediaFiles(MediaFileType.VIDEO); if (mfs.size() == 0) { tvShow.removeEpisode(episode); dirty = true; } } } if (dirty) { tvShow.saveToDb(); } }
@Override public void doInBackground() { // check if there is at least one DS to update Utils.removeEmptyStringsFromList(dataSources); if (dataSources.isEmpty() && tvShowFolders.isEmpty()) { LOGGER.info("no datasource to update"); MessageManager.instance.pushMessage( new Message(MessageLevel.ERROR, "update.datasource", "update.datasource.nonespecified")); return; } try { long start = System.currentTimeMillis(); start(); // cleanup newlyadded for a new UDS run for (TvShow tvShow : tvShowList.getTvShows()) { for (TvShowEpisode episode : tvShow.getEpisodes()) { episode.setNewlyAdded(false); } tvShow.setNewlyAdded(false); } // here we have 2 ways of updating: // - per datasource -> update ds / remove orphaned / update MFs // - per TV show -> udpate TV show / update MFs if (tvShowFolders.size() == 0) { // update ds updateDatasource(); } else { // update TV show updateTvShows(); } long end = System.currentTimeMillis(); LOGGER.info("Done updating datasource :) - took " + Utils.MSECtoHHMMSS(end - start)); } catch (Exception e) { LOGGER.error("Thread crashed", e); MessageManager.instance.pushMessage( new Message(MessageLevel.ERROR, "update.datasource", "message.update.threadcrashed")); } }
/** * renames the TvSHow root folder and updates all mediaFiles * * @param show the show */ public static void renameTvShowRoot(TvShow show) { LOGGER.debug("TV show year: " + show.getYear()); LOGGER.debug("TV show path: " + show.getPath()); String newPathname = generateTvShowDir(SETTINGS.getRenamerTvShowFoldername(), show); String oldPathname = show.getPath(); if (!newPathname.isEmpty()) { // newPathname = show.getDataSource() + File.separator + newPathname; File srcDir = new File(oldPathname); File destDir = new File(newPathname); // move directory if needed // if (!srcDir.equals(destDir)) { if (!srcDir.getAbsolutePath().equals(destDir.getAbsolutePath())) { try { // FileUtils.moveDirectory(srcDir, destDir); boolean ok = Utils.moveDirectorySafe(srcDir, destDir); if (ok) { show.updateMediaFilePath(srcDir, destDir); // TvShow MFs show.setPath(newPathname); for (TvShowEpisode episode : new ArrayList<TvShowEpisode>(show.getEpisodes())) { episode.replacePathForRenamedFolder(srcDir, destDir); episode.updateMediaFilePath(srcDir, destDir); } show.saveToDb(); show.writeNFO(); } } catch (Exception e) { LOGGER.error("error moving folder: ", e.getMessage()); MessageManager.instance.pushMessage( new Message( MessageLevel.ERROR, srcDir.getPath(), "message.renamer.failedrename", new String[] {":", e.getLocalizedMessage()})); } } } }
/** * Rename Episode (PLUS all Episodes having the same MediaFile!!!). * * @param episode the Episode */ public static void renameEpisode(TvShowEpisode episode) { // test for valid season/episode number if (episode.getSeason() < 0 || episode.getEpisode() < 0) { LOGGER.warn( "failed to rename episode " + episode.getTitle() + " (TV show " + episode.getTvShow().getTitle() + ") - invalid season/episode number"); MessageManager.instance.pushMessage( new Message( MessageLevel.ERROR, episode.getTvShow().getTitle(), "tvshow.renamer.failedrename", new String[] {episode.getTitle()})); return; } LOGGER.info( "Renaming TvShow '" + episode.getTvShow().getTitle() + "' Episode " + episode.getEpisode()); for (MediaFile mf : new ArrayList<MediaFile>(episode.getMediaFiles())) { renameMediaFile(mf, episode.getTvShow()); } }
public static String generateSeasonDir(String template, TvShowEpisode episode) { String seasonDir = template; // replace $1 and $2 as the only episode specific tokens seasonDir = seasonDir.replace("$1", String.valueOf(episode.getSeason())); seasonDir = seasonDir.replace("$2", lz(episode.getSeason())); seasonDir = seasonDir.replace("$1", String.valueOf(episode.getDvdSeason())); seasonDir = seasonDir.replace("$2", lz(episode.getDvdSeason())); // replace all other tokens seasonDir = createDestination(seasonDir, episode.getTvShow(), new ArrayList<TvShowEpisode>()); // only allow empty season dir if the season is in the filename if (StringUtils.isBlank(seasonDir) && !(SETTINGS.getRenamerFilename().contains("$1") || SETTINGS.getRenamerFilename().contains("$2") || SETTINGS.getRenamerFilename().contains("$3") || SETTINGS.getRenamerFilename().contains("$4"))) { seasonDir = "Season " + String.valueOf(episode.getSeason()); } return seasonDir; }
private SyncShow toSyncShow(TvShow tmmShow, boolean watched) { SyncShow show = null; ShowIds ids = new ShowIds(); if (!tmmShow.getIdAsString(Constants.IMDBID).isEmpty()) { ids.imdb = tmmShow.getIdAsString(Constants.IMDBID); } if (tmmShow.getIdAsInt(Constants.TMDBID) != 0) { ids.tmdb = tmmShow.getIdAsInt(Constants.TMDBID); } if (tmmShow.getIdAsInt(Constants.TVDBID) != 0) { ids.tvdb = tmmShow.getIdAsInt(Constants.TVDBID); } if (tmmShow.getIdAsInt(Constants.TRAKTID) != 0) { ids.trakt = tmmShow.getIdAsInt(Constants.TRAKTID); } if (tmmShow.getIdAsInt(Constants.TVRAGEID) != 0) { ids.tvrage = tmmShow.getIdAsInt(Constants.TVRAGEID); } ArrayList<SyncSeason> ss = new ArrayList<SyncSeason>(); boolean foundS = false; for (TvShowSeason tmmSeason : tmmShow.getSeasons()) { boolean foundEP = false; ArrayList<SyncEpisode> se = new ArrayList<SyncEpisode>(); for (TvShowEpisode tmmEp : tmmSeason.getEpisodes()) { // we have to decide what we send; trakt behaves differenty when sending data to // sync collection and sync history. if (watched) { // sync history if (tmmEp.isWatched() && tmmEp.getLastWatched() == null) { // watched in tmm and not in trakt -> sync se.add( new SyncEpisode() .number(tmmEp.getEpisode()) .watchedAt(new DateTime(tmmEp.getLastWatched()))); foundEP = true; } } else { // sync collection se.add( new SyncEpisode() .number(tmmEp.getEpisode()) .collectedAt(new DateTime(tmmEp.getDateAdded()))); foundEP = true; } } if (foundEP) { // do not send empty seasons foundS = true; ss.add(new SyncSeason().number(tmmSeason.getSeason()).episodes(se)); } } if (foundS) { // we have at least one season/episode, so add it show = new SyncShow().id(ids).collectedAt(new DateTime(tmmShow.getDateAdded())).seasons(ss); } // if nothing added, do NOT send an empty show (to add all) return show; }
public void syncTraktTvShowWatched(List<TvShow> tvShowsInTmm) { if (!isEnabled()) { return; } // create a local copy of the list List<TvShow> tvShows = new ArrayList<TvShow>(tvShowsInTmm); List<BaseShow> traktShows = new ArrayList<BaseShow>(); try { traktShows = TRAKT.sync().watchedShows(Extended.DEFAULT_MIN); } catch (RetrofitError e) { handleRetrofitError(e); return; } catch (UnauthorizedException e) { // not authorized - maybe access token revoked - relogin if (this.Login()) { // ok, it worked, lets try once again :) try { traktShows = TRAKT.sync().watchedShows(Extended.DEFAULT_MIN); } catch (UnauthorizedException e1) { return; } } else { handleRetrofitError((RetrofitError) e.getCause()); return; } } LOGGER.info("You have " + traktShows.size() + " TvShows marked as watched on Trakt.tv"); for (BaseShow traktShow : traktShows) { for (TvShow tmmShow : tvShows) { if (matches(tmmShow, traktShow.show.ids)) { // ok, we have a show match // update show IDs from trakt boolean dirty = updateIDs(tmmShow, traktShow.show.ids); // update watched date from trakt (show) if (traktShow.last_watched_at != null && !(traktShow.last_watched_at.toDate().equals(tmmShow.getLastWatched()))) { // always set from trakt, if not matched (Trakt = master) LOGGER.trace( "Marking TvShow '" + tmmShow.getTitle() + "' as watched on " + traktShow.last_watched_at.toDate() + " (was " + tmmShow.getLastWatched() + ")"); tmmShow.setLastWatched(traktShow.last_watched_at.toDate()); dirty = true; } // update collection date from trakt (episodes) for (BaseSeason bs : traktShow.seasons) { for (BaseEpisode be : bs.episodes) { TvShowEpisode tmmEP = tmmShow.getEpisode(bs.number, be.number); // update ep IDs - NOT YET POSSIBLE // boolean dirty = updateIDs(tmmEP, be.ids); if (tmmEP != null && be.last_watched_at != null && !(be.last_watched_at.toDate().equals(tmmEP.getLastWatched()))) { tmmEP.setLastWatched(be.last_watched_at.toDate()); tmmEP.setWatched(true); dirty = true; } } } if (dirty) { tmmShow.saveToDb(); } } } } // ***************************************************************************** // 2) add all our shows to Trakt watched // ***************************************************************************** LOGGER.info("Adding " + tvShows.size() + " TvShows as watched on Trakt.tv"); // send show per show; sending all together may result too often in a timeout for (TvShow show : tvShows) { // get items to sync SyncShow sync = toSyncShow(show, true); if (sync == null) { continue; } try { SyncItems items = new SyncItems().shows(sync); response = TRAKT.sync().addItemsToWatchedHistory(items); LOGGER.debug("Trakt add-to-library status: " + show.getTitle()); printStatus(response); } catch (RetrofitError e) { handleRetrofitError(e); } catch (UnauthorizedException e) { handleRetrofitError((RetrofitError) e.getCause()); } } }
/** * Find additional episode files.<br> * adds everything which starts with "videoFile name"<br> * scans subs/subtitle directories aswell * * @param episode the episode * @param videoFile the video file * @param directoryContents the directory contents * @return indicator whether something new has been found or not */ private boolean findAdditionalEpisodeFiles( TvShowEpisode episode, File videoFile, File[] directoryContents) { boolean newFileFound = false; List<MediaFile> existingMediaFiles = episode.getMediaFiles(); for (File file : directoryContents) { if (file.isFile()) { MediaFile mf = new MediaFile(file); if (existingMediaFiles.contains(mf)) { continue; } if (mf.getType().equals(MediaFileType.VIDEO) || !mf.getBasename().startsWith(FilenameUtils.getBaseName(videoFile.getName())) || file.getName().startsWith(skipFilesStartingWith)) { // MacOS ignore) continue; } if (mf.getType() == MediaFileType.SUBTITLE) { episode.setSubtitles(true); } // check if it is a poster if (mf.getType() == MediaFileType.GRAPHIC) { LOGGER.debug("parsing unknown graphic " + mf.getFilename()); String vfilename = FilenameUtils.getBaseName(videoFile.getName()); if (vfilename.equals(FilenameUtils.getBaseName(mf.getFilename())) // basename match || Utils.cleanStackingMarkers(vfilename) .trim() .equals(FilenameUtils.getBaseName(mf.getFilename())) // basename w/o stacking || episode .getTitle() .equals(FilenameUtils.getBaseName(mf.getFilename()))) { // title match mf.setType(MediaFileType.THUMB); } } episode.addToMediaFiles(mf); newFileFound = true; } else { if (file.getName().equalsIgnoreCase("subs") || file.getName().equalsIgnoreCase("subtitle")) { File[] subDirContent = file.listFiles(); if (subDirContent == null) { LOGGER.error("Whops. Cannot access directory: " + file.getName()); } else { for (File subDirFile : subDirContent) { if (FilenameUtils.getBaseName(subDirFile.getName()) .startsWith(FilenameUtils.getBaseName(videoFile.getName()))) { MediaFile mf = new MediaFile(subDirFile); if (existingMediaFiles.contains(mf)) { continue; } if (mf.getType() == MediaFileType.SUBTITLE) { episode.setSubtitles(true); } episode.addToMediaFiles(mf); newFileFound = true; } } } } } } return newFileFound; }
/** * Find tv episodes. * * @param tvShow the tv show * @param dir the dir */ private void findTvEpisodesAsDisc(TvShow tvShow, File dir) { String parentDir = dir.getParent(); LOGGER.debug("parsing disc structure in " + dir.getPath() + " parent: " + parentDir); // crawl this folder and try to find every episode in it List<MediaFile> videoFiles = new ArrayList<MediaFile>(); File firstVideoFile = null; File[] content = dir.listFiles(); if (content == null) { LOGGER.error("Whops. Cannot access directory: " + dir.getName()); return; } for (File file : content) { if (file.isFile()) { // check filetype if (!Globals.settings .getVideoFileType() .contains("." + FilenameUtils.getExtension(file.getName()).toLowerCase()) || file.getName().startsWith(skipFilesStartingWith)) { // MacOS ignore continue; } videoFiles.add(new MediaFile(file)); if (firstVideoFile == null) { firstVideoFile = file; } } } List<TvShowEpisode> episodes = tvShowList.getTvEpisodesByFile(tvShow, firstVideoFile); if (episodes.size() == 0) { String relativePath = new File(tvShow.getPath()).toURI().relativize(firstVideoFile.toURI()).getPath(); EpisodeMatchingResult result = TvShowEpisodeAndSeasonParser.detectEpisodeFromFilenameAlternative( relativePath, tvShow.getTitle()); 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); } if (result.episodes.size() == 0) { // try to parse out episodes/season from parent directory result = TvShowEpisodeAndSeasonParser.detectEpisodeFromDirectory( dir.getParentFile(), tvShow.getPath()); } List<TvShowEpisode> episodesInNfo = TvShowEpisode.parseNFO(firstVideoFile); // FIXME: Episode root is outside of disc folders ?! while (dir.getPath().toUpperCase().contains("BDMV") || dir.getPath().toUpperCase().contains("VIDEO_TS")) { dir = dir.getParentFile(); } if (result.episodes.size() > 0) { // add it for (int ep : result.episodes) { TvShowEpisode episode = null; // search in the NFO list if an episode has been found for (int i = episodesInNfo.size() - 1; i >= 0; i--) { TvShowEpisode e = episodesInNfo.get(i); if (e.getSeason() == result.season && e.getEpisode() == ep) { episode = e; episodesInNfo.remove(i); break; } } if (episode == null) { episode = new TvShowEpisode(); episode.setDvdOrder(Globals.settings.getTvShowSettings().isDvdOrder()); episode.setEpisode(ep); episode.setSeason(result.season); episode.setNewlyAdded(true); episode.setFirstAired(result.date); } episode.setPath(dir.getPath()); episode.setTvShow(tvShow); episode.setDisc(true); episode.setNewlyAdded(true); episode.addToMediaFiles(videoFiles); episode.setDateAddedFromMediaFile(new MediaFile(firstVideoFile)); findAdditionalEpisodeFiles(episode, firstVideoFile, content); episode.saveToDb(); tvShow.addEpisode(episode); } } else { // episode detection found nothing - simply add this file if (episodesInNfo.size() > 0) { for (TvShowEpisode e : episodesInNfo) { e.setPath(dir.getPath()); e.setTvShow(tvShow); e.addToMediaFiles(videoFiles); e.setNewlyAdded(true); // e.findImages(); e.setDateAddedFromMediaFile(new MediaFile(firstVideoFile)); findAdditionalEpisodeFiles(e, firstVideoFile, content); e.saveToDb(); tvShow.addEpisode(e); } } else { TvShowEpisode episode = new TvShowEpisode(); episode.setPath(dir.getPath()); episode.setDvdOrder(Globals.settings.getTvShowSettings().isDvdOrder()); episode.setEpisode(-1); episode.setSeason(-1); episode.setTvShow(tvShow); episode.setFirstAired(result.date); episode.setDisc(true); episode.setNewlyAdded(true); episode.addToMediaFiles(videoFiles); episode.setDateAddedFromMediaFile(new MediaFile(firstVideoFile)); findAdditionalEpisodeFiles(episode, firstVideoFile, content); 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, firstVideoFile, content)) { episode.saveToDb(); } } } }
/** * 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 a single TV show */ private void updateTvShows() { // one thread here - more threads killed the UI initThreadPool(1, "update"); for (File tvShowFolder : tvShowFolders) { // check if the tv show dir is accessible File[] filesInDatasourceRoot = tvShowFolder.getParentFile().listFiles(); if (filesInDatasourceRoot == null || filesInDatasourceRoot.length == 0) { LOGGER.warn("TvShow folder not available/empty " + tvShowFolder); MessageManager.instance.pushMessage( new Message( MessageLevel.ERROR, "update.datasource", "update.datasource.unavailable", new String[] {tvShowFolder.getParent()})); continue; } if (tvShowFolder.isDirectory()) { submitTask(new FindTvShowTask(tvShowFolder, tvShowFolder.getParent())); } } waitForCompletionOrCancel(); // cleanup setTaskName(BUNDLE.getString("update.cleanup")); setTaskDescription(null); setProgressDone(0); setWorkUnits(0); publishState(); LOGGER.info("removing orphaned movies/files..."); for (int i = tvShowList.getTvShows().size() - 1; i >= 0; i--) { if (cancel) { break; } TvShow tvShow = tvShowList.getTvShows().get(i); // check only Tv shows matching datasource if (!tvShowFolders.contains(new File(tvShow.getPath()))) { continue; } // check and delete all not found MediaFiles cleanup(tvShow); } // start MI 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); // check only Tv shows matching datasource if (!tvShowFolders.contains(new File(tvShow.getPath()))) { continue; } gatherMediaInformationForUngatheredMediaFiles(tvShow); } waitForCompletionOrCancel(); if (cancel) { return; } // build up the image cache if (Globals.settings.getTvShowSettings().isBuildImageCacheOnImport()) { List<File> imageFiles = new ArrayList<File>(); for (int i = tvShowList.getTvShows().size() - 1; i >= 0; i--) { if (cancel) { break; } TvShow tvShow = tvShowList.getTvShows().get(i); // check only Tv shows matching datasource if (!tvShowFolders.contains(new File(tvShow.getPath()))) { 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()); } } } } ImageCacheTask task = new ImageCacheTask(imageFiles); TmmTaskManager.getInstance().addUnnamedTask(task); } }
/* * 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); } }
/** * Creates the new file/folder name according to template string * * @param template the template * @param show the TV show * @param episodes the TV show episodes; nullable for TV show root foldername * @return the string */ public static String createDestination( String template, TvShow show, List<TvShowEpisode> episodes) { String newDestination = template; TvShowEpisode firstEp = null; // replace token show title ($N) if (newDestination.contains("$N")) { newDestination = replaceToken(newDestination, "$N", show.getTitle()); } // parse out episode depended tokens - for multi EP naming if (!episodes.isEmpty()) { Matcher matcher = multiEpisodeTokenPattern.matcher(template); String episodeTokens = ""; if (matcher.find()) { episodeTokens = matcher.group(0); } String combinedEpisodeParts = ""; for (TvShowEpisode episode : episodes) { String episodePart = episodeTokens; // remember first episode for media file tokens if (firstEp == null) { firstEp = episode; } // Season w/o leading zeros ($1) if (episodePart.contains("$1")) { episodePart = replaceToken(episodePart, "$1", String.valueOf(episode.getSeason())); } // Season leading zeros ($2) if (episodePart.contains("$2")) { episodePart = replaceToken(episodePart, "$2", lz(episode.getSeason())); } // DVD-Season w/o leading zeros ($3) if (episodePart.contains("$3")) { episodePart = replaceToken(episodePart, "$3", String.valueOf(episode.getDvdSeason())); } // DVD-Season leading zeros ($4) if (episodePart.contains("$4")) { episodePart = replaceToken(episodePart, "$4", lz(episode.getDvdSeason())); } // episode number if (episodePart.contains("$E")) { episodePart = replaceToken(episodePart, "$E", lz(episode.getEpisode())); } // DVD-episode number if (episodePart.contains("$D")) { episodePart = replaceToken(episodePart, "$D", lz(episode.getDvdEpisode())); } // episode title if (episodePart.contains("$T")) { episodePart = replaceToken(episodePart, "$T", episode.getTitle()); } combinedEpisodeParts += episodePart + " "; } // and now fill in the (multiple) episode parts if (StringUtils.isNotBlank(episodeTokens)) { newDestination = newDestination.replace(episodeTokens, combinedEpisodeParts); } } else { // we're in either TV show folder or season folder generation; // strip out episode tokens newDestination = newDestination.replace("$E", ""); newDestination = newDestination.replace("$T", ""); } // replace token year ($Y) if (newDestination.contains("$Y")) { if (show.getYear().equals("0")) { newDestination = newDestination.replace("$Y", ""); } else { newDestination = replaceToken(newDestination, "$Y", show.getYear()); } } if (firstEp != null && firstEp.getMediaFiles(MediaFileType.VIDEO).size() > 0) { MediaFile mf = firstEp.getMediaFiles(MediaFileType.VIDEO).get(0); // replace token resolution ($R) if (newDestination.contains("$R")) { newDestination = replaceToken(newDestination, "$R", mf.getVideoResolution()); } // replace token audio codec + channels ($A) if (newDestination.contains("$A")) { newDestination = replaceToken( newDestination, "$A", mf.getAudioCodec() + (mf.getAudioCodec().isEmpty() ? "" : "-") + mf.getAudioChannels()); } // replace token video codec + format ($V) if (newDestination.contains("$V")) { newDestination = replaceToken( newDestination, "$V", mf.getVideoCodec() + (mf.getVideoCodec().isEmpty() ? "" : "-") + mf.getVideoFormat()); } // replace token video format ($F) if (newDestination.contains("$F")) { newDestination = replaceToken(newDestination, "$F", mf.getVideoFormat()); } } else { // no mediafiles; remove at least token (if available) newDestination = newDestination.replace("$R", ""); newDestination = newDestination.replace("$A", ""); newDestination = newDestination.replace("$V", ""); newDestination = newDestination.replace("$F", ""); } // replace empty brackets newDestination = newDestination.replaceAll("\\(\\)", ""); newDestination = newDestination.replaceAll("\\[\\]", ""); // if there are multiple file separators in a row - strip them out if (SystemUtils.IS_OS_WINDOWS) { // we need to mask it in windows newDestination = newDestination.replaceAll("\\\\{2,}", "\\\\"); newDestination = newDestination.replaceAll("^\\\\", ""); } else { newDestination = newDestination.replaceAll(File.separator + "{2,}", File.separator); newDestination = newDestination.replaceAll("^" + File.separator, ""); } // ASCII replacement if (SETTINGS.isAsciiReplacement()) { newDestination = StrgUtils.convertToAscii(newDestination, false); } // trim out unnecessary whitespaces newDestination = newDestination.trim(); // any whitespace replacements? if (SETTINGS.isRenamerSpaceSubstitution()) { newDestination = newDestination.replaceAll(" ", SETTINGS.getRenamerSpaceReplacement()); } // replace trailing dots and spaces newDestination = newDestination.replaceAll("[ \\.]+$", ""); return newDestination.trim(); }
/** * Renames a MediaFiles<br> * gets all episodes of it, creates season folder, updates MFs & DB * * @param mf the MediaFile * @param show the tvshow (only needed for path) */ public static void renameMediaFile(MediaFile mf, TvShow show) { // ####################################################### // Assumption: all multi-episodes share the same season!!! // ####################################################### List<TvShowEpisode> eps = TvShowList.getInstance().getTvEpisodesByFile(show, mf.getFile()); if (eps == null || eps.size() == 0) { // this should not happen, but unluckily ODB does it sometimes; try a second time to get the // episode try { Thread.sleep(250); } catch (Exception e) { } eps = TvShowList.getInstance().getTvEpisodesByFile(show, mf.getFile()); } if (eps == null || eps.size() == 0) { // FIXME: workaround for r1972 // when moving video file, all NFOs get deleted and a new gets created. // so this OLD NFO is not found anylonger - just delete it if (mf.getType() == MediaFileType.NFO) { FileUtils.deleteQuietly(mf.getFile()); return; } LOGGER.warn("No episodes found for file '" + mf.getFilename() + "' - skipping"); return; } // get first, for isDisc and season TvShowEpisode ep = eps.get(0); // test access rights or return LOGGER.debug( "testing file S:" + ep.getSeason() + " E:" + ep.getEpisode() + " MF:" + mf.getFile().getAbsolutePath()); File f = mf.getFile(); boolean testRenameOk = false; for (int i = 0; i < 5; i++) { testRenameOk = f.renameTo(f); // haahaa, try to rename to itself :P if (testRenameOk) { break; // ok it worked, step out } try { if (!f.exists()) { LOGGER.debug("Hmmm... file " + f + " does not even exists; delete from DB"); // delete from MF for (TvShowEpisode e : eps) { e.removeFromMediaFiles(mf); e.saveToDb(); e.writeNFO(); } return; } LOGGER.debug("rename did not work - sleep a while and try again..."); Thread.sleep(1000); } catch (InterruptedException e) { LOGGER.warn("I'm so excited - could not sleep"); } } if (!testRenameOk) { LOGGER.warn("File " + mf.getFile().getAbsolutePath() + " is not accessible!"); MessageManager.instance.pushMessage( new Message(MessageLevel.ERROR, mf.getFilename(), "message.renamer.failedrename")); return; } // create SeasonDir // String seasonName = "Season " + String.valueOf(ep.getSeason()); String seasonName = generateSeasonDir(SETTINGS.getRenamerSeasonFoldername(), ep); File seasonDir = null; if (StringUtils.isNotBlank(seasonName)) { seasonDir = new File(show.getPath(), seasonName); if (!seasonDir.exists()) { seasonDir.mkdir(); } } else { seasonDir = new File(show.getPath()); } // rename epFolder accordingly if (ep.isDisc() || mf.isDiscFile()) { // \Season 1\S01E02E03\VIDEO_TS\VIDEO_TS.VOB // ........ \epFolder \disc... \ file File disc = mf.getFile().getParentFile(); File epFolder = disc.getParentFile(); // sanity check if (!disc.getName().equalsIgnoreCase("BDMV") && !disc.getName().equalsIgnoreCase("VIDEO_TS")) { LOGGER.error( "Episode is labeled as 'on BD/DVD', but structure seems not to match. Better exit and do nothing... o_O"); return; } String newFoldername = FilenameUtils.getBaseName(generateFolderename(show, mf)); // w/o extension if (newFoldername != null && !newFoldername.isEmpty()) { File newEpFolder = new File(seasonDir + File.separator + newFoldername); File newDisc = new File(newEpFolder + File.separator + disc.getName()); // old disc name try { // if (!epFolder.equals(newEpFolder)) { if (!epFolder.getAbsolutePath().equals(newEpFolder.getAbsolutePath())) { boolean ok = false; try { ok = Utils.moveDirectorySafe(epFolder, newEpFolder); } catch (Exception e) { LOGGER.error(e.getMessage()); MessageManager.instance.pushMessage( new Message( MessageLevel.ERROR, epFolder.getName(), "message.renamer.failedrename", new String[] {":", e.getLocalizedMessage()})); } if (ok) { // iterate over all EPs & MFs and fix new path LOGGER.debug("updating *all* MFs for new path -> " + newEpFolder); for (TvShowEpisode e : eps) { e.updateMediaFilePath(disc, newDisc); e.setPath(newEpFolder.getPath()); e.saveToDb(); e.writeNFO(); } } // and cleanup cleanEmptyDir(epFolder); } else { // old and new folder are equal, do nothing } } catch (Exception e) { LOGGER.error("error moving video file " + disc.getName() + " to " + newFoldername, e); MessageManager.instance.pushMessage( new Message( MessageLevel.ERROR, mf.getFilename(), "message.renamer.failedrename", new String[] {":", e.getLocalizedMessage()})); } } } // end isDisc else { MediaFile newMF = new MediaFile(mf); // clone MF if (mf.getType().equals(MediaFileType.TRAILER)) { // move trailer into separate dir - not supported by XBMC File sample = new File(seasonDir, "sample"); if (!sample.exists()) { sample.mkdir(); } seasonDir = sample; // change directory storage } String filename = generateFilename(show, mf); LOGGER.debug("new filename should be " + filename); if (filename != null && !filename.isEmpty()) { File newFile = new File(seasonDir, filename); try { // if (!mf.getFile().equals(newFile)) { if (!mf.getFile().getAbsolutePath().equals(newFile.getAbsolutePath())) { File oldMfFile = mf.getFile(); boolean ok = false; try { ok = Utils.moveFileSafe(oldMfFile, newFile); } catch (Exception e) { LOGGER.error(e.getMessage()); MessageManager.instance.pushMessage( new Message( MessageLevel.ERROR, oldMfFile.getPath(), "message.renamer.failedrename", new String[] {":", e.getLocalizedMessage()})); } if (ok) { newMF.setPath(seasonDir.getAbsolutePath()); newMF.setFilename(filename); // iterate over all EPs and delete old / set new MF for (TvShowEpisode e : eps) { e.removeFromMediaFiles(mf); e.addToMediaFiles(newMF); e.setPath(seasonDir.getAbsolutePath()); e.saveToDb(); e.writeNFO(); } } // and cleanup cleanEmptyDir(oldMfFile.getParentFile()); } else { // old and new file are equal, keep MF } } catch (Exception e) { LOGGER.error( "error moving video file " + mf.getFilename() + " to " + newFile.getPath(), e); MessageManager.instance.pushMessage( new Message( MessageLevel.ERROR, mf.getFilename(), "message.renamer.failedrename", new String[] {":", e.getLocalizedMessage()})); } } } }