@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")); } }
@Override public void startUp() throws Exception { // do a DB backup, and keep last 15 copies File db = new File(Settings.getInstance().getSettingsFolder(), MOVIE_DB); Utils.createBackupFile(db); Utils.deleteOldBackupFile(db, 15); // configure database mvStore = new MVStore.Builder() .fileName(Settings.getInstance().getSettingsFolder() + File.separatorChar + MOVIE_DB) .compressHigh() .open(); mvStore.setAutoCommitDelay(2000); // 2 sec mvStore.setRetentionTime(0); mvStore.setReuseSpace(true); // configure JSON objectMapper = new ObjectMapper(); objectMapper.configure(MapperFeature.AUTO_DETECT_GETTERS, false); objectMapper.configure(MapperFeature.AUTO_DETECT_IS_GETTERS, false); objectMapper.configure(MapperFeature.AUTO_DETECT_SETTERS, false); objectMapper.configure(MapperFeature.AUTO_DETECT_FIELDS, false); objectMapper.setTimeZone(TimeZone.getDefault()); objectMapper.setSerializationInclusion(Include.NON_DEFAULT); movieObjectWriter = objectMapper.writerFor(Movie.class); movieSetObjectWriter = objectMapper.writerFor(MovieSet.class); movieMap = mvStore.openMap("movies"); movieSetMap = mvStore.openMap("movieSets"); MovieList.getInstance().loadMoviesFromDatabase(movieMap, objectMapper); MovieList.getInstance().loadMovieSetsFromDatabase(movieSetMap, objectMapper); MovieList.getInstance().initDataAfterLoading(); enabled = true; }
/** * 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()})); } } } }
/** * 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; }
/** * 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()})); } } } }