2009-04-04 15:36:12 -04:00
|
|
|
|
|
|
|
package net.sourceforge.filebot.format;
|
|
|
|
|
|
|
|
|
2011-09-18 15:08:03 -04:00
|
|
|
import static java.util.Arrays.*;
|
|
|
|
import static java.util.ResourceBundle.*;
|
|
|
|
import static java.util.regex.Pattern.*;
|
2009-08-10 07:46:24 -04:00
|
|
|
import static net.sourceforge.filebot.MediaTypes.*;
|
2009-05-17 13:22:44 -04:00
|
|
|
import static net.sourceforge.filebot.format.Define.*;
|
2009-08-10 07:46:24 -04:00
|
|
|
import static net.sourceforge.filebot.hash.VerificationUtilities.*;
|
2009-04-04 15:36:12 -04:00
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
2011-09-18 15:08:03 -04:00
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
import java.nio.charset.Charset;
|
2009-04-25 03:59:08 -04:00
|
|
|
import java.util.Scanner;
|
2011-09-18 15:08:03 -04:00
|
|
|
import java.util.regex.Matcher;
|
|
|
|
import java.util.regex.Pattern;
|
2009-04-04 15:36:12 -04:00
|
|
|
|
|
|
|
import net.sf.ehcache.Cache;
|
|
|
|
import net.sf.ehcache.CacheManager;
|
|
|
|
import net.sf.ehcache.Element;
|
2009-08-10 07:46:24 -04:00
|
|
|
import net.sourceforge.filebot.hash.HashType;
|
2009-04-04 15:36:12 -04:00
|
|
|
import net.sourceforge.filebot.mediainfo.MediaInfo;
|
|
|
|
import net.sourceforge.filebot.mediainfo.MediaInfo.StreamKind;
|
2011-09-18 15:08:03 -04:00
|
|
|
import net.sourceforge.filebot.web.CachedResource;
|
2010-10-23 08:47:43 -04:00
|
|
|
import net.sourceforge.filebot.web.Date;
|
2009-04-04 15:36:12 -04:00
|
|
|
import net.sourceforge.filebot.web.Episode;
|
2011-09-18 15:08:03 -04:00
|
|
|
import net.sourceforge.filebot.web.MovieDescriptor;
|
|
|
|
import net.sourceforge.filebot.web.MoviePart;
|
2009-05-02 19:34:04 -04:00
|
|
|
import net.sourceforge.tuned.FileUtilities;
|
2009-04-04 15:36:12 -04:00
|
|
|
|
|
|
|
|
2011-09-18 15:08:03 -04:00
|
|
|
public class MediaBindingBean {
|
2009-04-04 15:36:12 -04:00
|
|
|
|
2011-09-18 15:08:03 -04:00
|
|
|
private final Object infoObject;
|
2009-04-04 15:36:12 -04:00
|
|
|
private final File mediaFile;
|
|
|
|
private MediaInfo mediaInfo;
|
|
|
|
|
2009-06-13 17:27:01 -04:00
|
|
|
|
2011-09-18 15:08:03 -04:00
|
|
|
public MediaBindingBean(Object infoObject, File mediaFile) {
|
|
|
|
this.infoObject = infoObject;
|
2009-04-04 15:36:12 -04:00
|
|
|
this.mediaFile = mediaFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define(undefined)
|
2011-09-18 15:08:03 -04:00
|
|
|
public <T> T undefined() {
|
2009-04-04 15:36:12 -04:00
|
|
|
// omit expressions that depend on undefined values
|
|
|
|
throw new RuntimeException("undefined");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("n")
|
2011-09-18 15:08:03 -04:00
|
|
|
public String getName() {
|
|
|
|
if (infoObject instanceof Episode)
|
|
|
|
return getEpisode().getSeriesName();
|
|
|
|
if (infoObject instanceof MovieDescriptor)
|
|
|
|
return getMovie().getName();
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("y")
|
|
|
|
public Integer getYear() {
|
|
|
|
if (infoObject instanceof Episode)
|
|
|
|
return getEpisode().airdate().getYear();
|
|
|
|
if (infoObject instanceof MovieDescriptor)
|
|
|
|
return getMovie().getYear();
|
|
|
|
|
|
|
|
return null;
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("s")
|
2010-10-24 08:10:30 -04:00
|
|
|
public Integer getSeasonNumber() {
|
2011-09-18 15:08:03 -04:00
|
|
|
return getEpisode().getSeason();
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("e")
|
2010-10-24 08:10:30 -04:00
|
|
|
public Integer getEpisodeNumber() {
|
2011-09-18 15:08:03 -04:00
|
|
|
return getEpisode().getEpisode();
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("t")
|
|
|
|
public String getTitle() {
|
2011-09-18 15:08:03 -04:00
|
|
|
return getEpisode().getTitle();
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-09 03:04:12 -05:00
|
|
|
@Define("airdate")
|
2010-10-23 08:47:43 -04:00
|
|
|
public Date airdate() {
|
2011-09-18 15:08:03 -04:00
|
|
|
return getEpisode().airdate();
|
2010-10-23 08:47:43 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-11-09 03:04:12 -05:00
|
|
|
@Define("absolute")
|
|
|
|
public Integer getAbsoluteEpisodeNumber() {
|
2011-09-18 15:08:03 -04:00
|
|
|
return getEpisode().getAbsolute();
|
2010-11-09 03:04:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("special")
|
|
|
|
public Integer getSpecialNumber() {
|
2011-09-18 15:08:03 -04:00
|
|
|
return getEpisode().getSpecial();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("imdb")
|
2011-09-19 10:21:21 -04:00
|
|
|
public String getImdbId() {
|
|
|
|
int imdb = getMovie().getImdbId();
|
|
|
|
|
|
|
|
if (imdb < 0)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
return String.format("%07d", imdb);
|
2010-11-09 03:04:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-04 15:36:12 -04:00
|
|
|
@Define("vc")
|
|
|
|
public String getVideoCodec() {
|
2009-08-23 11:17:32 -04:00
|
|
|
// e.g. XviD, x264, DivX 5, MPEG-4 Visual, AVC, etc.
|
|
|
|
String codec = getMediaInfo(StreamKind.Video, 0, "Encoded_Library/Name", "CodecID/Hint", "Format");
|
|
|
|
|
|
|
|
// get first token (e.g. DivX 5 => DivX)
|
|
|
|
return new Scanner(codec).next();
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("ac")
|
|
|
|
public String getAudioCodec() {
|
2009-08-23 11:17:32 -04:00
|
|
|
// e.g. AC-3, DTS, AAC, Vorbis, MP3, etc.
|
|
|
|
String codec = getMediaInfo(StreamKind.Audio, 0, "CodecID/Hint", "Format");
|
|
|
|
|
|
|
|
// remove punctuation (e.g. AC-3 => AC3)
|
|
|
|
return codec.replaceAll("\\p{Punct}", "");
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-05-02 19:34:04 -04:00
|
|
|
@Define("cf")
|
|
|
|
public String getContainerFormat() {
|
2009-08-23 11:17:32 -04:00
|
|
|
// container format extensions (e.g. avi, mkv mka mks, OGG, etc.)
|
|
|
|
String extensions = getMediaInfo(StreamKind.General, 0, "Codec/Extensions", "Format");
|
2009-05-02 19:34:04 -04:00
|
|
|
|
2009-08-23 11:17:32 -04:00
|
|
|
// get first extension
|
|
|
|
return new Scanner(extensions).next().toLowerCase();
|
2009-05-02 19:34:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-23 11:17:32 -04:00
|
|
|
@Define("vf")
|
|
|
|
public String getVideoFormat() {
|
2009-04-04 15:36:12 -04:00
|
|
|
String height = getMediaInfo(StreamKind.Video, 0, "Height");
|
2009-08-23 11:17:32 -04:00
|
|
|
String scanType = getMediaInfo(StreamKind.Video, 0, "ScanType");
|
2009-04-04 15:36:12 -04:00
|
|
|
|
2009-08-23 11:17:32 -04:00
|
|
|
if (height == null || scanType == null)
|
2009-04-04 15:36:12 -04:00
|
|
|
return null;
|
|
|
|
|
|
|
|
// e.g. 720p
|
2009-08-23 11:17:32 -04:00
|
|
|
return height + Character.toLowerCase(scanType.charAt(0));
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-18 15:08:03 -04:00
|
|
|
@Define("af")
|
|
|
|
public String getAudioChannels() {
|
|
|
|
String channels = getMediaInfo(StreamKind.Audio, 0, "Channel(s)");
|
|
|
|
|
|
|
|
if (channels == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
// e.g. 6ch
|
|
|
|
return channels + "ch";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-04 15:36:12 -04:00
|
|
|
@Define("resolution")
|
|
|
|
public String getVideoResolution() {
|
|
|
|
String width = getMediaInfo(StreamKind.Video, 0, "Width");
|
|
|
|
String height = getMediaInfo(StreamKind.Video, 0, "Height");
|
|
|
|
|
|
|
|
if (width == null || height == null)
|
|
|
|
return null;
|
|
|
|
|
|
|
|
// e.g. 1280x720
|
|
|
|
return width + 'x' + height;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("crc32")
|
2009-04-26 09:34:22 -04:00
|
|
|
public String getCRC32() throws IOException, InterruptedException {
|
2009-05-26 13:05:05 -04:00
|
|
|
// use inferred media file
|
|
|
|
File inferredMediaFile = getInferredMediaFile();
|
|
|
|
|
2009-05-03 11:21:04 -04:00
|
|
|
// try to get checksum from file name
|
2009-08-10 07:46:24 -04:00
|
|
|
String checksum = getEmbeddedChecksum(inferredMediaFile.getName());
|
2009-05-03 11:21:04 -04:00
|
|
|
|
|
|
|
if (checksum != null)
|
|
|
|
return checksum;
|
|
|
|
|
|
|
|
// try to get checksum from sfv file
|
2009-08-10 07:46:24 -04:00
|
|
|
checksum = getHashFromVerificationFile(inferredMediaFile, HashType.SFV, 3);
|
2009-05-03 11:21:04 -04:00
|
|
|
|
|
|
|
if (checksum != null)
|
|
|
|
return checksum;
|
|
|
|
|
|
|
|
// calculate checksum from file
|
2009-05-26 13:05:05 -04:00
|
|
|
return crc32(inferredMediaFile);
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
2009-04-06 16:34:33 -04:00
|
|
|
|
2010-10-24 08:10:30 -04:00
|
|
|
@Define("fn")
|
|
|
|
public String getFileName() {
|
|
|
|
// make sure media file is defined
|
|
|
|
checkMediaFile();
|
|
|
|
|
|
|
|
// file extension
|
|
|
|
return FileUtilities.getName(mediaFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-05-02 19:34:04 -04:00
|
|
|
@Define("ext")
|
2009-05-03 12:28:39 -04:00
|
|
|
public String getExtension() {
|
2009-05-03 11:21:04 -04:00
|
|
|
// make sure media file is defined
|
|
|
|
checkMediaFile();
|
|
|
|
|
2009-05-02 19:34:04 -04:00
|
|
|
// file extension
|
|
|
|
return FileUtilities.getExtension(mediaFile);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-18 15:08:03 -04:00
|
|
|
@Define("source")
|
|
|
|
public String getVideoSource() {
|
|
|
|
// use inferred media file
|
|
|
|
File inferredMediaFile = getInferredMediaFile();
|
|
|
|
|
|
|
|
// pattern matching any video source name
|
|
|
|
Pattern source = compile(getBundle(getClass().getName()).getString("pattern.video.source"), CASE_INSENSITIVE);
|
|
|
|
|
|
|
|
// look for video source patterns in media file and it's parent folder
|
|
|
|
String lastMatch = null;
|
|
|
|
for (File it : asList(inferredMediaFile.getParentFile(), inferredMediaFile)) {
|
|
|
|
for (String part : it.getName().split("[^\\p{Alnum}]")) {
|
|
|
|
if (source.matcher(part).matches()) {
|
|
|
|
lastMatch = part;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return lastMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("group")
|
|
|
|
public String getReleaseGroup() throws IOException {
|
|
|
|
// use inferred media file
|
|
|
|
File inferredMediaFile = getInferredMediaFile();
|
|
|
|
|
|
|
|
// pattern matching any release group name enclosed in separators
|
2011-09-19 10:21:21 -04:00
|
|
|
Pattern groups = compile("(?<!\\p{Alnum})(" + releaseGroups.get() + ")(?!\\p{Alnum})", CASE_INSENSITIVE);
|
2011-09-18 15:08:03 -04:00
|
|
|
|
|
|
|
// look for release group names in media file and it's parent folder
|
|
|
|
String lastMatch = null;
|
|
|
|
for (File it : asList(inferredMediaFile.getParentFile(), inferredMediaFile)) {
|
|
|
|
for (Matcher matcher = groups.matcher(it.getName()); matcher.find();) {
|
|
|
|
lastMatch = matcher.group();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return lastMatch;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-05-03 12:28:39 -04:00
|
|
|
@Define("media")
|
2009-04-06 16:34:33 -04:00
|
|
|
public Object getGeneralMediaInfo() {
|
|
|
|
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.General, 0));
|
2009-04-05 17:03:40 -04:00
|
|
|
}
|
|
|
|
|
2009-04-06 16:34:33 -04:00
|
|
|
|
2009-04-04 15:36:12 -04:00
|
|
|
@Define("video")
|
2009-04-06 16:34:33 -04:00
|
|
|
public Object getVideoInfo() {
|
|
|
|
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.Video, 0));
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("audio")
|
2009-04-06 16:34:33 -04:00
|
|
|
public Object getAudioInfo() {
|
|
|
|
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.Audio, 0));
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
2009-04-06 16:34:33 -04:00
|
|
|
|
2009-04-05 17:03:40 -04:00
|
|
|
@Define("text")
|
2009-04-06 16:34:33 -04:00
|
|
|
public Object getTextInfo() {
|
|
|
|
return new AssociativeScriptObject(getMediaInfo().snapshot(StreamKind.Text, 0));
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
2009-04-06 16:34:33 -04:00
|
|
|
|
2009-05-02 19:34:04 -04:00
|
|
|
@Define("episode")
|
2009-04-25 03:59:08 -04:00
|
|
|
public Episode getEpisode() {
|
2011-09-18 15:08:03 -04:00
|
|
|
return (Episode) infoObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("movie")
|
|
|
|
public MovieDescriptor getMovie() {
|
|
|
|
return (MovieDescriptor) infoObject;
|
2009-04-25 03:59:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-05-02 19:34:04 -04:00
|
|
|
@Define("file")
|
2009-04-25 03:59:08 -04:00
|
|
|
public File getMediaFile() {
|
|
|
|
return mediaFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-18 15:08:03 -04:00
|
|
|
@Define("pi")
|
|
|
|
public Integer getPart() {
|
2011-09-19 10:21:21 -04:00
|
|
|
return ((MoviePart) infoObject).getPartIndex();
|
2011-09-18 15:08:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Define("pn")
|
|
|
|
public Integer getPartCount() {
|
2011-09-19 10:21:21 -04:00
|
|
|
return ((MoviePart) infoObject).getPartCount();
|
2011-09-18 15:08:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public Object getInfoObject() {
|
|
|
|
return infoObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-29 16:31:08 -04:00
|
|
|
private File getInferredMediaFile() {
|
2009-05-03 13:38:22 -04:00
|
|
|
// make sure media file is defined
|
|
|
|
checkMediaFile();
|
|
|
|
|
2009-05-03 12:28:39 -04:00
|
|
|
if (SUBTITLE_FILES.accept(mediaFile)) {
|
|
|
|
// file is a subtitle
|
|
|
|
String name = FileUtilities.getName(mediaFile);
|
|
|
|
|
|
|
|
// find corresponding movie file
|
2009-05-24 14:38:12 -04:00
|
|
|
for (File movie : mediaFile.getParentFile().listFiles(VIDEO_FILES)) {
|
2009-05-03 12:28:39 -04:00
|
|
|
if (name.startsWith(FileUtilities.getName(movie))) {
|
|
|
|
return movie;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mediaFile;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-07-29 16:31:08 -04:00
|
|
|
private void checkMediaFile() throws RuntimeException {
|
|
|
|
// make sure file is not null, and that it is an existing file
|
|
|
|
if (mediaFile == null || !mediaFile.isFile())
|
2011-09-12 21:44:54 -04:00
|
|
|
throw new RuntimeException("Invalid media file: " + mediaFile);
|
2009-05-03 11:21:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-08-02 07:48:45 -04:00
|
|
|
private synchronized MediaInfo getMediaInfo() {
|
2009-04-04 15:36:12 -04:00
|
|
|
if (mediaInfo == null) {
|
2009-05-03 11:21:04 -04:00
|
|
|
// make sure media file is defined
|
|
|
|
checkMediaFile();
|
|
|
|
|
2009-05-03 12:28:39 -04:00
|
|
|
MediaInfo newMediaInfo = new MediaInfo();
|
2009-04-05 13:43:32 -04:00
|
|
|
|
2009-05-03 13:38:22 -04:00
|
|
|
// use inferred media file (e.g. actual movie file instead of subtitle file)
|
2009-05-03 12:28:39 -04:00
|
|
|
if (!newMediaInfo.open(getInferredMediaFile())) {
|
2011-09-12 21:44:54 -04:00
|
|
|
throw new RuntimeException("Cannot open media file: " + mediaFile);
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
2009-05-03 12:28:39 -04:00
|
|
|
|
|
|
|
mediaInfo = newMediaInfo;
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return mediaInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private String getMediaInfo(StreamKind streamKind, int streamNumber, String... keys) {
|
2009-04-04 16:19:03 -04:00
|
|
|
for (String key : keys) {
|
|
|
|
String value = getMediaInfo().get(streamKind, streamNumber, key);
|
|
|
|
|
2011-09-12 21:44:54 -04:00
|
|
|
if (value.length() > 0)
|
2009-04-04 16:19:03 -04:00
|
|
|
return value;
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2009-04-26 14:57:27 -04:00
|
|
|
|
2009-04-26 09:34:22 -04:00
|
|
|
private String crc32(File file) throws IOException, InterruptedException {
|
2009-04-04 15:36:12 -04:00
|
|
|
// try to get checksum from cache
|
2009-04-26 14:57:27 -04:00
|
|
|
Cache cache = CacheManager.getInstance().getCache("checksum");
|
2011-09-12 21:44:54 -04:00
|
|
|
Element element = cache.get(file);
|
|
|
|
if (element != null)
|
|
|
|
return (String) element.getValue();
|
2009-04-26 14:57:27 -04:00
|
|
|
|
2011-09-12 21:44:54 -04:00
|
|
|
// compute and cache checksum
|
|
|
|
String hash = computeHash(file, HashType.SFV);
|
|
|
|
cache.put(new Element(file, hash));
|
|
|
|
return hash;
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|
2011-09-18 15:08:03 -04:00
|
|
|
|
|
|
|
|
|
|
|
// fetch release group names online and try to update the data once per day
|
2011-09-19 10:21:21 -04:00
|
|
|
private final CachedResource<String> releaseGroups = new CachedResource<String>(getBundle(getClass().getName()).getString("url.release-groups"), 24 * 60 * 60 * 1000) {
|
2011-09-18 15:08:03 -04:00
|
|
|
|
|
|
|
@Override
|
2011-09-19 10:21:21 -04:00
|
|
|
public String process(ByteBuffer data) {
|
|
|
|
return compile("\\s").matcher(Charset.forName("UTF-8").decode(data)).replaceAll("|");
|
2011-09-18 15:08:03 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-04-04 15:36:12 -04:00
|
|
|
}
|