From c45abb729161325ec6c0062f6e613763d9b40f63 Mon Sep 17 00:00:00 2001 From: Reinhard Pointner Date: Wed, 22 Jan 2014 11:31:55 +0000 Subject: [PATCH] + support for smart-skip/override for keeping the higher-quality video via --conflict auto --- .../filebot/cli/CmdlineOperations.java | 36 ++++-- .../filebot/format/MediaBindingBean.java | 4 +- .../filebot/media/MediaDetection.java | 53 +++++++- .../filebot/ui/analyze/ExtractTool.java | 2 +- .../net/sourceforge/filebot/vfs/FileInfo.java | 11 +- .../filebot/vfs/SimpleFileInfo.java | 32 ++--- .../web/OpenSubtitlesSubtitleDescriptor.java | 114 +++++------------- 7 files changed, 132 insertions(+), 120 deletions(-) diff --git a/source/net/sourceforge/filebot/cli/CmdlineOperations.java b/source/net/sourceforge/filebot/cli/CmdlineOperations.java index 5c40fb0c..4cb5a47f 100644 --- a/source/net/sourceforge/filebot/cli/CmdlineOperations.java +++ b/source/net/sourceforge/filebot/cli/CmdlineOperations.java @@ -60,6 +60,7 @@ import net.sourceforge.filebot.subtitle.SubtitleFormat; import net.sourceforge.filebot.subtitle.SubtitleNaming; import net.sourceforge.filebot.vfs.FileInfo; import net.sourceforge.filebot.vfs.MemoryFile; +import net.sourceforge.filebot.vfs.SimpleFileInfo; import net.sourceforge.filebot.web.AudioTrack; import net.sourceforge.filebot.web.Episode; import net.sourceforge.filebot.web.EpisodeFormat; @@ -574,7 +575,7 @@ public class CmdlineOperations implements CmdlineInterface { throw new Exception("File already exists: " + destination); } - if (conflictAction == ConflictAction.OVERRIDE) { + if (conflictAction == ConflictAction.OVERRIDE || (conflictAction == ConflictAction.AUTO && VIDEO_SIZE_ORDER.compare(source, destination) > 0)) { if (!destination.delete()) { CLILogger.log(Level.SEVERE, "Failed to override file: " + destination); } @@ -1085,14 +1086,15 @@ public class CmdlineOperations implements CmdlineInterface { CLILogger.info(String.format("Read archive [%s] and extract to [%s]", file.getName(), outputFolder)); final FileMapper outputMapper = new FileMapper(outputFolder, false); - final List outputMapping = new ArrayList(); - for (FileInfo entry : archive.listFiles()) { - outputMapping.add(outputMapper.getOutputFile(new File(entry.getPath()))); + final List outputMapping = new ArrayList(); + for (FileInfo it : archive.listFiles()) { + File outputPath = outputMapper.getOutputFile(it.toFile()); + outputMapping.add(new SimpleFileInfo(outputPath.getPath(), it.getLength())); } - final Set selection = new TreeSet(); - for (File future : outputMapping) { - if (filter == null || filter.accept(future)) { + final Set selection = new TreeSet(); + for (FileInfo future : outputMapping) { + if (filter == null || filter.accept(future.toFile())) { selection.add(future); } } @@ -1103,18 +1105,27 @@ public class CmdlineOperations implements CmdlineInterface { } boolean skip = true; - for (File future : filter == null || forceExtractAll ? outputMapping : selection) { - skip &= future.exists(); + for (FileInfo future : filter == null || forceExtractAll ? outputMapping : selection) { + if (conflictAction == ConflictAction.AUTO) { + skip &= (future.toFile().exists() && future.getLength() == future.toFile().length()); + } else { + skip &= (future.toFile().exists()); + } } if (!skip || conflictAction == ConflictAction.OVERRIDE) { if (filter == null || forceExtractAll) { CLILogger.finest("Extracting files " + outputMapping); + // extract all files archive.extract(outputMapper); - extractedFiles.addAll(outputMapping); + + for (FileInfo it : outputMapping) { + extractedFiles.add(it.toFile()); + } } else { CLILogger.finest("Extracting files " + selection); + // extract files selected by the given filter archive.extract(outputMapper, new FileFilter() { @@ -1123,7 +1134,10 @@ public class CmdlineOperations implements CmdlineInterface { return selection.contains(outputMapper.getOutputFile(entry)); } }); - extractedFiles.addAll(selection); + + for (FileInfo it : selection) { + extractedFiles.add(it.toFile()); + } } } else { CLILogger.finest("Skipped extracting files " + selection); diff --git a/source/net/sourceforge/filebot/format/MediaBindingBean.java b/source/net/sourceforge/filebot/format/MediaBindingBean.java index 97c1b720..9294e2d2 100644 --- a/source/net/sourceforge/filebot/format/MediaBindingBean.java +++ b/source/net/sourceforge/filebot/format/MediaBindingBean.java @@ -707,7 +707,7 @@ public class MediaBindingBean { return JsonWriter.objectToJson(infoObject); } - private File getInferredMediaFile() { + public File getInferredMediaFile() { // make sure media file is defined checkMediaFile(); @@ -717,7 +717,7 @@ public class MediaBindingBean { if (videos.size() > 0) { return videos.iterator().next(); } - } else if ((infoObject instanceof Episode || infoObject instanceof Movie) && !VIDEO_FILES.accept(mediaFile)) { + } else if (SUBTITLE_FILES.accept(mediaFile) || ((infoObject instanceof Episode || infoObject instanceof Movie) && !VIDEO_FILES.accept(mediaFile))) { // prefer equal match from current context if possible if (getContext() != null) { for (Entry it : getContext().entrySet()) { diff --git a/source/net/sourceforge/filebot/media/MediaDetection.java b/source/net/sourceforge/filebot/media/MediaDetection.java index ab109a9e..0396872d 100644 --- a/source/net/sourceforge/filebot/media/MediaDetection.java +++ b/source/net/sourceforge/filebot/media/MediaDetection.java @@ -17,6 +17,7 @@ import java.net.URL; import java.text.CollationKey; import java.text.Collator; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -40,6 +41,7 @@ import java.util.regex.Pattern; import net.sourceforge.filebot.WebServices; import net.sourceforge.filebot.archive.Archive; +import net.sourceforge.filebot.format.MediaBindingBean; import net.sourceforge.filebot.similarity.CommonSequenceMatcher; import net.sourceforge.filebot.similarity.DateMatcher; import net.sourceforge.filebot.similarity.DateMetric; @@ -97,7 +99,7 @@ public class MediaDetection { Archive iso = new Archive(file); try { for (FileInfo it : iso.listFiles()) { - for (File entry : listPath(new File(it.getPath()))) { + for (File entry : listPath(it.toFile())) { if (diskFolderEntryFilter.accept(entry)) { return true; } @@ -1251,6 +1253,55 @@ public class MediaDetection { } } + public static Comparator VIDEO_SIZE_ORDER = new Comparator() { + + @Override + public int compare(File f1, File f2) { + long[] v1 = getSizeValues(f1); + long[] v2 = getSizeValues(f2); + + for (int i = 0; i < v1.length; i++) { + // best to worst + int d = new Long(v1[i]).compareTo(new Long(v2[i])); + if (d != 0) { + return d; + } + } + return 0; + } + + public long[] getSizeValues(File f) { + long[] v = new long[] { 0, 0 }; + + try { + if (VIDEO_FILES.accept(f) || SUBTITLE_FILES.accept(f)) { + MediaBindingBean media = new MediaBindingBean(null, f, null); + + // 1. Video Resolution + List dim = media.getDimension(); + v[0] = dim.get(0).longValue() * dim.get(1).longValue(); + + // 2. File Size + v[1] = media.getInferredMediaFile().length(); + } else if (AUDIO_FILES.accept(f)) { + // 1. Audio BitRate + v[0] = 0; + + // 2. File Size + v[1] = f.length(); + } + } catch (Exception e) { + // negative values for invalid files + Logger.getLogger(MediaDetection.class.getClass().getName()).warning(String.format("Unable to read media info: %s [%s]", e.getMessage(), f.getName())); + + Arrays.fill(v, -1); + return v; + } + + return v; + } + }; + public static void storeMetaInfo(File file, Object model, String original) { // only for Episode / Movie objects if ((model instanceof Episode || model instanceof Movie) && file.exists()) { diff --git a/source/net/sourceforge/filebot/ui/analyze/ExtractTool.java b/source/net/sourceforge/filebot/ui/analyze/ExtractTool.java index 7be8b423..8f1b77cc 100644 --- a/source/net/sourceforge/filebot/ui/analyze/ExtractTool.java +++ b/source/net/sourceforge/filebot/ui/analyze/ExtractTool.java @@ -217,7 +217,7 @@ class ExtractTool extends Tool { return data[row].entry.getName(); case 1: File root = new File(data[row].archive.getName()); - File prefix = new File(data[row].entry.getPath()).getParentFile(); + File prefix = data[row].entry.toFile().getParentFile(); File path = (prefix == null) ? root : new File(root, prefix.getPath()); return normalizePathSeparators(path.getPath()); case 2: diff --git a/source/net/sourceforge/filebot/vfs/FileInfo.java b/source/net/sourceforge/filebot/vfs/FileInfo.java index c917b5f2..97fd7fe7 100644 --- a/source/net/sourceforge/filebot/vfs/FileInfo.java +++ b/source/net/sourceforge/filebot/vfs/FileInfo.java @@ -1,18 +1,17 @@ - package net.sourceforge.filebot.vfs; +import java.io.File; public interface FileInfo { - + public String getPath(); - public String getName(); - public String getType(); - public long getLength(); - + + public File toFile(); + } diff --git a/source/net/sourceforge/filebot/vfs/SimpleFileInfo.java b/source/net/sourceforge/filebot/vfs/SimpleFileInfo.java index c9d1efa7..74057d14 100644 --- a/source/net/sourceforge/filebot/vfs/SimpleFileInfo.java +++ b/source/net/sourceforge/filebot/vfs/SimpleFileInfo.java @@ -1,45 +1,42 @@ - package net.sourceforge.filebot.vfs; - import static net.sourceforge.tuned.FileUtilities.*; +import java.io.File; import java.util.Arrays; +public class SimpleFileInfo implements FileInfo, Comparable { -public class SimpleFileInfo implements FileInfo { - private final String path; private final long length; - public SimpleFileInfo(String path, long length) { this.path = path; this.length = length; } - @Override public String getPath() { return path; } - public String getName() { return getNameWithoutExtension(path); } - @Override public String getType() { return getExtension(path); } - public long getLength() { return length; } - + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[] { getPath(), getLength() }); + } @Override public boolean equals(Object obj) { @@ -47,20 +44,23 @@ public class SimpleFileInfo implements FileInfo { FileInfo other = (FileInfo) obj; return other.getLength() == getLength() && other.getPath().equals(getPath()); } - + return false; } - @Override - public int hashCode() { - return Arrays.hashCode(new Object[] { getPath(), getLength() }); + public int compareTo(FileInfo other) { + return getPath().compareTo(other.getPath()); } - @Override public String toString() { return getPath(); } - + + @Override + public File toFile() { + return new File(path); + } + } diff --git a/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleDescriptor.java b/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleDescriptor.java index 87c60987..2a28087d 100644 --- a/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleDescriptor.java +++ b/source/net/sourceforge/filebot/web/OpenSubtitlesSubtitleDescriptor.java @@ -1,7 +1,6 @@ - package net.sourceforge.filebot.web; - +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; @@ -16,62 +15,19 @@ import java.util.zip.ZipException; import net.sourceforge.tuned.ByteBufferOutputStream; import net.sourceforge.tuned.FileUtilities; - /** * Describes a subtitle on OpenSubtitles. * * @see OpenSubtitlesXmlRpc */ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor, Serializable { - + public static enum Property { - IDSubtitle, - IDSubtitleFile, - IDSubMovieFile, - IDMovie, - IDMovieImdb, - SubFileName, - SubFormat, - SubHash, - SubSize, - MovieHash, - MovieByteSize, - MovieName, - MovieNameEng, - MovieYear, - MovieReleaseName, - MovieTimeMS, - MovieFPS, - MovieImdbRating, - MovieKind, - SeriesSeason, - SeriesEpisode, - SeriesIMDBParent, - SubLanguageID, - ISO639, - LanguageName, - UserID, - UserRank, - UserNickName, - SubAddDate, - SubAuthorComment, - SubFeatured, - SubComments, - SubDownloadsCnt, - SubHearingImpaired, - SubRating, - SubHD, - SubBad, - SubActualCD, - SubSumCD, - MatchedBy, - SubtitlesLink, - SubDownloadLink, - ZipDownloadLink; - + IDSubtitle, IDSubtitleFile, IDSubMovieFile, IDMovie, IDMovieImdb, SubFileName, SubFormat, SubHash, SubSize, MovieHash, MovieByteSize, MovieName, MovieNameEng, MovieYear, MovieReleaseName, MovieTimeMS, MovieFPS, MovieImdbRating, MovieKind, SeriesSeason, SeriesEpisode, SeriesIMDBParent, SubLanguageID, ISO639, LanguageName, UserID, UserRank, UserNickName, SubAddDate, SubAuthorComment, SubFeatured, SubComments, SubDownloadsCnt, SubHearingImpaired, SubRating, SubHD, SubBad, SubActualCD, SubSumCD, MatchedBy, SubtitlesLink, SubDownloadLink, ZipDownloadLink; + public static EnumMap asEnumMap(Map stringMap) { EnumMap enumMap = new EnumMap(Property.class); - + // copy entry set to enum map for (Entry entry : stringMap.entrySet()) { try { @@ -80,109 +36,101 @@ public class OpenSubtitlesSubtitleDescriptor implements SubtitleDescriptor, Seri // illegal enum constant, just ignore } } - + return enumMap; } } - + private final Map properties; - - + public OpenSubtitlesSubtitleDescriptor(Map properties) { this.properties = properties; } - - + public String getProperty(Property key) { return properties.get(key); } - - + @Override public String getPath() { return getProperty(Property.SubFileName); } - - + @Override public String getName() { return FileUtilities.getNameWithoutExtension(getProperty(Property.SubFileName)); } - - + @Override public String getLanguageName() { return getProperty(Property.LanguageName); } - - + @Override public String getType() { return getProperty(Property.SubFormat); } - - + @Override public long getLength() { return Long.parseLong(getProperty(Property.SubSize)); } - - + public String getMovieHash() { return getProperty(Property.MovieHash); } - - + public long getMovieByteSize() { return Long.parseLong(getProperty(Property.MovieByteSize)); } - - + @Override public ByteBuffer fetch() throws Exception { URL resource = new URL(getProperty(Property.SubDownloadLink)); InputStream stream = resource.openStream(); - + try { ByteBufferOutputStream buffer = new ByteBufferOutputStream(getLength()); - + // extract gzipped subtitle on-the-fly try { stream = new GZIPInputStream(stream); } catch (ZipException e) { throw new IOException(String.format("%s: anti-leech limit may have been reached", e.getMessage())); } - + // fully download buffer.transferFully(stream); - + return buffer.getByteBuffer(); } finally { stream.close(); } } - - + @Override public int hashCode() { return getProperty(Property.IDSubtitle).hashCode(); } - - + @Override public boolean equals(Object object) { if (object instanceof OpenSubtitlesSubtitleDescriptor) { OpenSubtitlesSubtitleDescriptor other = (OpenSubtitlesSubtitleDescriptor) object; return getProperty(Property.IDSubtitle).equals(other.getProperty(Property.IDSubtitle)); } - + return false; } - - + @Override public String toString() { return String.format("%s [%s]", getName(), getLanguageName()); } - + + @Override + public File toFile() { + return new File(getPath()); + } + }