+ EpressionFormat: format Double objects with integer values as integers

+ Matching: allow partial match with season number OR episode number
+ AutoFetchEpisodeListMatcher: abort if one or more shows cannot be found

* support specials in TVRage, TVDotCom and TheTVDB Client
* account for IMDB search-to-info-page redirect
This commit is contained in:
Reinhard Pointner 2009-05-03 15:21:04 +00:00
parent 416384901b
commit 1ed4d2b495
28 changed files with 320 additions and 194 deletions

View File

@ -57,13 +57,13 @@ public class EpisodeFormatBindingBean {
@Define("s") @Define("s")
public String getSeasonNumber() { public String getSeasonNumber() {
return episode.getSeasonNumber(); return episode.getSeason();
} }
@Define("e") @Define("e")
public String getEpisodeNumber() { public String getEpisodeNumber() {
return episode.getEpisodeNumber(); return episode.getEpisode();
} }
@ -123,29 +123,31 @@ public class EpisodeFormatBindingBean {
@Define("crc32") @Define("crc32")
public String getCRC32() throws IOException, InterruptedException { public String getCRC32() throws IOException, InterruptedException {
if (mediaFile != null) { // make sure media file is defined
// try to get checksum from file name checkMediaFile();
String checksum = FileBotUtilities.getEmbeddedChecksum(mediaFile.getName());
if (checksum != null) // try to get checksum from file name
return checksum; String checksum = FileBotUtilities.getEmbeddedChecksum(mediaFile.getName());
// try to get checksum from sfv file if (checksum != null)
checksum = getChecksumFromSfvFile(mediaFile); return checksum;
if (checksum != null) // try to get checksum from sfv file
return checksum; checksum = getChecksumFromSfvFile(mediaFile);
// calculate checksum from file if (checksum != null)
return crc32(mediaFile); return checksum;
}
return null; // calculate checksum from file
return crc32(mediaFile);
} }
@Define("ext") @Define("ext")
public String getContainerExtension() { public String getContainerExtension() {
// make sure media file is defined
checkMediaFile();
// file extension // file extension
return FileUtilities.getExtension(mediaFile); return FileUtilities.getExtension(mediaFile);
} }
@ -193,12 +195,21 @@ public class EpisodeFormatBindingBean {
} }
private synchronized MediaInfo getMediaInfo() { private void checkMediaFile() {
if (mediaFile == null) { // make sure file is not null
throw new NullPointerException("Media file is null"); if (mediaFile == null)
} throw new NullPointerException("Media file is not defined");
// file may not exist at this point but if an existing file is required,
// an exception will be thrown later anyway
}
private synchronized MediaInfo getMediaInfo() {
if (mediaInfo == null) { if (mediaInfo == null) {
// make sure media file is defined
checkMediaFile();
mediaInfo = new MediaInfo(); mediaInfo = new MediaInfo();
if (!mediaInfo.open(mediaFile)) { if (!mediaInfo.open(mediaFile)) {

View File

@ -1,4 +1,6 @@
/**
* Convenience method to pad strings or numbers with given characters ('0' by default)
*/
String.prototype.pad = Number.prototype.pad = function(length, padding) { String.prototype.pad = Number.prototype.pad = function(length, padding) {
var s = this.toString(); var s = this.toString();
@ -13,6 +15,9 @@ String.prototype.pad = Number.prototype.pad = function(length, padding) {
} }
/**
* Convenience method to replace space characters with a given characters
*/
String.prototype.space = function(replacement) { String.prototype.space = function(replacement) {
return this.replace(/\s/g, replacement); return this.replace(/\s/g, replacement);
} }

View File

@ -18,6 +18,7 @@ import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain; import java.security.ProtectionDomain;
import java.text.FieldPosition; import java.text.FieldPosition;
import java.text.Format; import java.text.Format;
import java.text.NumberFormat;
import java.text.ParsePosition; import java.text.ParsePosition;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -125,6 +126,11 @@ public class ExpressionFormat extends Format {
Object value = ((CompiledScript) snipped).eval(context); Object value = ((CompiledScript) snipped).eval(context);
if (value != null) { if (value != null) {
if (value instanceof Double && value.equals(Math.floor((Double) value))) {
// value is really an integer, not a decimal (number literals -1, 0 and 1 are interpreted as decimal by rhino)
value = NumberFormat.getIntegerInstance().format(value);
}
sb.append(value); sb.append(value);
} }
} catch (ScriptException e) { } catch (ScriptException e) {

View File

@ -64,6 +64,8 @@ public class SeasonEpisodeMatcher {
public static class SxE { public static class SxE {
public static final int UNDEFINED = -1;
public final int season; public final int season;
public final int episode; public final int episode;
@ -81,7 +83,11 @@ public class SeasonEpisodeMatcher {
protected int parse(String number) { protected int parse(String number) {
return number == null || number.isEmpty() ? 0 : Integer.parseInt(number); try {
return Integer.parseInt(number);
} catch (Exception e) {
return UNDEFINED;
}
} }

View File

@ -27,12 +27,12 @@ public class SeasonEpisodeSimilarityMetric implements SimilarityMetric {
for (SxE sxe1 : sxeVector1) { for (SxE sxe1 : sxeVector1) {
for (SxE sxe2 : sxeVector2) { for (SxE sxe2 : sxeVector2) {
if (sxe1.episode == sxe2.episode) { if (sxe1.episode == sxe2.episode && sxe1.season == sxe2.season) {
if (sxe1.season == sxe2.season) { // vectors have at least one perfect episode match in common
// vectors have at least one perfect episode match in common return 1;
return 1; }
}
if (sxe1.episode == sxe2.episode || sxe1.season == sxe2.season) {
// at least we have a partial match // at least we have a partial match
similarity = 0.5f; similarity = 0.5f;
} }

View File

@ -54,7 +54,7 @@ import net.sourceforge.filebot.Settings;
import net.sourceforge.filebot.format.EpisodeFormatBindingBean; import net.sourceforge.filebot.format.EpisodeFormatBindingBean;
import net.sourceforge.filebot.format.ExpressionFormat; import net.sourceforge.filebot.format.ExpressionFormat;
import net.sourceforge.filebot.web.Episode; import net.sourceforge.filebot.web.Episode;
import net.sourceforge.filebot.web.Episode.EpisodeFormat; import net.sourceforge.filebot.web.EpisodeFormat;
import net.sourceforge.tuned.DefaultThreadFactory; import net.sourceforge.tuned.DefaultThreadFactory;
import net.sourceforge.tuned.ExceptionUtilities; import net.sourceforge.tuned.ExceptionUtilities;
import net.sourceforge.tuned.ui.GradientStyle; import net.sourceforge.tuned.ui.GradientStyle;
@ -392,10 +392,13 @@ public class EpisodeFormatDialog extends JDialog {
try { try {
preview.setText(get()); preview.setText(get());
// check internal script exception and empty output // check internal script exception
if (format.scriptException() != null) { if (format.scriptException() != null) {
throw format.scriptException(); throw format.scriptException();
} else if (get().trim().isEmpty()) { }
// check empty output
if (get().trim().isEmpty()) {
throw new RuntimeException("Formatted value is empty"); throw new RuntimeException("Formatted value is empty");
} }
@ -406,7 +409,7 @@ public class EpisodeFormatDialog extends JDialog {
status.setIcon(ResourceManager.getIcon("status.warning")); status.setIcon(ResourceManager.getIcon("status.warning"));
status.setVisible(true); status.setVisible(true);
} finally { } finally {
preview.setVisible(true); preview.setVisible(preview.getText().trim().length() > 0);
editor.setForeground(defaultColor); editor.setForeground(defaultColor);
progressIndicatorTimer.stop(); progressIndicatorTimer.stop();

View File

@ -81,12 +81,14 @@ class AutoFetchEpisodeListMatcher extends SwingWorker<List<Match<File, Episode>>
public Collection<Episode> call() throws Exception { public Collection<Episode> call() throws Exception {
Collection<SearchResult> results = provider.search(seriesName); Collection<SearchResult> results = provider.search(seriesName);
if (results.size() > 0) { if (results.isEmpty()) {
SearchResult selectedSearchResult = selectSearchResult(seriesName, results); throw new RuntimeException(String.format("'%s' has not been found.", seriesName));
}
if (selectedSearchResult != null) { SearchResult selectedSearchResult = selectSearchResult(seriesName, results);
return provider.getEpisodeList(selectedSearchResult);
} if (selectedSearchResult != null) {
return provider.getEpisodeList(selectedSearchResult);
} }
return Collections.emptyList(); return Collections.emptyList();

View File

@ -10,7 +10,7 @@ import net.sourceforge.filebot.format.EpisodeFormatBindingBean;
import net.sourceforge.filebot.format.ExpressionFormat; import net.sourceforge.filebot.format.ExpressionFormat;
import net.sourceforge.filebot.similarity.Match; import net.sourceforge.filebot.similarity.Match;
import net.sourceforge.filebot.web.Episode; import net.sourceforge.filebot.web.Episode;
import net.sourceforge.filebot.web.Episode.EpisodeFormat; import net.sourceforge.filebot.web.EpisodeFormat;
public class EpisodeExpressionFormatter extends ExpressionFormat implements MatchFormatter { public class EpisodeExpressionFormatter extends ExpressionFormat implements MatchFormatter {

View File

@ -79,13 +79,8 @@ class MatchAction extends AbstractAction {
if (o instanceof Episode) { if (o instanceof Episode) {
Episode episode = (Episode) o; Episode episode = (Episode) o;
try { // create SxE from episode
// create SxE from episode return Collections.singleton(new SxE(episode.getSeason(), episode.getEpisode()));
return Collections.singleton(new SxE(episode.getSeasonNumber(), episode.getEpisodeNumber()));
} catch (NumberFormatException e) {
// some kind of special episode, no SxE
return null;
}
} }
return super.parse(o); return super.parse(o);

View File

@ -180,12 +180,12 @@ public class RenamePanel extends JComponent {
protected ActionPopup createSettingsPopup() { protected ActionPopup createSettingsPopup() {
ActionPopup actionPopup = new ActionPopup("Settings", ResourceManager.getIcon("action.rename.small")); ActionPopup actionPopup = new ActionPopup("Rename Settings", ResourceManager.getIcon("action.rename.small"));
actionPopup.addDescription(new JLabel("Extension:")); actionPopup.addDescription(new JLabel("Extension:"));
actionPopup.add(new PreserveExtensionAction(true, "Preserve", ResourceManager.getIcon("action.extension.preserve"))); actionPopup.add(new PreserveExtensionAction(true, "Preserve", ResourceManager.getIcon("action.extension.preserve")));
actionPopup.add(new PreserveExtensionAction(false, "Include", ResourceManager.getIcon("action.extension.include"))); actionPopup.add(new PreserveExtensionAction(false, "Override", ResourceManager.getIcon("action.extension.override")));
return actionPopup; return actionPopup;
} }

View File

@ -113,7 +113,7 @@ public class AnidbClient implements EpisodeListProvider {
// if number does not match, episode is probably some kind of special (S1, S2, ...) // if number does not match, episode is probably some kind of special (S1, S2, ...)
if (number.matches("\\d+")) { if (number.matches("\\d+")) {
// no seasons for anime // no seasons for anime
episodes.add(new Episode(searchResult.getName(), number, title)); episodes.add(new Episode(searchResult.getName(), null, number, title));
} }
} }

View File

@ -3,42 +3,54 @@ package net.sourceforge.filebot.web;
import java.io.Serializable; import java.io.Serializable;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Episode implements Serializable { public class Episode implements Serializable {
private String seriesName; private final String seriesName;
private String seasonNumber; private final String season;
private String episodeNumber; private final String episode;
private String title; private final String title;
public Episode(String seriesName, String seasonNumber, String episodeNumber, String title) { public Episode(String seriesName, Integer season, Integer episode, String title) {
this(seriesName, season.toString(), episode.toString(), title);
}
public Episode(String seriesName, String season, String episode, String title) {
this.seriesName = seriesName; this.seriesName = seriesName;
this.seasonNumber = seasonNumber; this.season = season;
this.episodeNumber = episodeNumber; this.episode = episode;
this.title = title; this.title = title;
} }
public Episode(String seriesName, String episodeNumber, String title) { public String getEpisode() {
this(seriesName, null, episodeNumber, title); return episode;
} }
public String getEpisodeNumber() { public Integer getEpisodeNumber() {
return episodeNumber; try {
return Integer.valueOf(episode);
} catch (NumberFormatException e) {
return null;
}
} }
public String getSeasonNumber() { public String getSeason() {
return seasonNumber; return season;
}
public Integer getSeasonNumber() {
try {
return Integer.valueOf(season);
} catch (NumberFormatException e) {
return null;
}
} }
@ -57,66 +69,4 @@ public class Episode implements Serializable {
return EpisodeFormat.getInstance().format(this); return EpisodeFormat.getInstance().format(this);
} }
public static class EpisodeFormat extends Format {
private static final EpisodeFormat instance = new EpisodeFormat();
public static EpisodeFormat getInstance() {
return instance;
}
@Override
public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) {
Episode episode = (Episode) obj;
sb.append(episode.getSeriesName()).append(" - ");
if (episode.getSeasonNumber() != null) {
sb.append(episode.getSeasonNumber()).append('x');
}
try {
// try to format episode number
sb.append(String.format("%02d", Integer.parseInt(episode.getEpisodeNumber())));
} catch (NumberFormatException e) {
// use episode "number" as is
sb.append(episode.getEpisodeNumber());
}
return sb.append(" - ").append(episode.getTitle());
}
@Override
public Episode parseObject(String source, ParsePosition pos) {
Pattern pattern = Pattern.compile("(.*) - (?:(\\w+?)x)?(\\w+)? - (.*)");
Matcher matcher = pattern.matcher(source).region(pos.getIndex(), source.length());
if (!matcher.matches()) {
pos.setErrorIndex(matcher.regionStart());
return null;
}
// episode number must not be null
if (matcher.group(3) == null) {
pos.setErrorIndex(matcher.start(3));
return null;
}
pos.setIndex(matcher.end());
return new Episode(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4));
}
@Override
public Episode parseObject(String source) throws ParseException {
return (Episode) super.parseObject(source);
}
}
} }

View File

@ -0,0 +1,69 @@
package net.sourceforge.filebot.web;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EpisodeFormat extends Format {
private static final EpisodeFormat instance = new EpisodeFormat();
public static EpisodeFormat getInstance() {
return instance;
}
@Override
public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) {
Episode episode = (Episode) obj;
sb.append(episode.getSeriesName()).append(" - ");
if (episode.getSeason() != null) {
sb.append(episode.getSeason()).append('x');
}
Integer episodeNumber = episode.getEpisodeNumber();
// try to format episode number, or use episode "number" string as is
sb.append(episodeNumber != null ? String.format("%02d", episodeNumber) : episode.getEpisode());
return sb.append(" - ").append(episode.getTitle());
}
@Override
public Episode parseObject(String source, ParsePosition pos) {
Pattern pattern = Pattern.compile("(.*) - (?:(\\w+?)x)?(\\w+)? - (.*)");
Matcher matcher = pattern.matcher(source).region(pos.getIndex(), source.length());
if (!matcher.matches()) {
pos.setErrorIndex(matcher.regionStart());
return null;
}
// episode number must not be null
if (matcher.group(3) == null) {
pos.setErrorIndex(matcher.start(3));
return null;
}
pos.setIndex(matcher.end());
return new Episode(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4));
}
@Override
public Episode parseObject(String source) throws ParseException {
return (Episode) super.parseObject(source);
}
}

View File

@ -15,7 +15,7 @@ public final class EpisodeListUtilities {
// filter given season from all seasons // filter given season from all seasons
for (Episode episode : episodes) { for (Episode episode : episodes) {
try { try {
if (season == Integer.parseInt(episode.getSeasonNumber())) { if (season == Integer.parseInt(episode.getSeason())) {
results.add(episode); results.add(episode);
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
@ -33,7 +33,7 @@ public final class EpisodeListUtilities {
// filter given season from all seasons // filter given season from all seasons
for (Episode episode : episodes) { for (Episode episode : episodes) {
try { try {
lastSeason = Math.max(lastSeason, Integer.parseInt(episode.getSeasonNumber())); lastSeason = Math.max(lastSeason, Integer.parseInt(episode.getSeason()));
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// ignore illegal episodes // ignore illegal episodes
} }

View File

@ -18,6 +18,8 @@ import java.net.URLEncoder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Scanner; import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.Icon; import javax.swing.Icon;
@ -68,9 +70,16 @@ public class IMDbClient implements EpisodeListProvider {
String href = getAttribute("href", node); String href = getAttribute("href", node);
String nameAndYear = String.format("%s %s", name, year).trim(); String nameAndYear = String.format("%s %s", name, year).trim();
int imdbId = new Scanner(href).useDelimiter("\\D+").nextInt();
results.add(new MovieDescriptor(nameAndYear, imdbId)); results.add(new MovieDescriptor(nameAndYear, getImdbId(href)));
}
// we might have been redirected to the movie page
if (results.isEmpty()) {
String name = removeQuotationMarks(selectString("//H1/text()", dom));
String url = selectString("//LINK[@rel='canonical']/@href", dom);
results.add(new MovieDescriptor(name, getImdbId(url)));
} }
return results; return results;
@ -129,6 +138,30 @@ public class IMDbClient implements EpisodeListProvider {
} }
protected int getImdbId(String link) {
try {
// try to extract path
link = new URI(link).getPath();
} catch (URISyntaxException e) {
// cannot extract path component, just move on
}
Matcher matcher = Pattern.compile("tt(\\d{7})").matcher(link);
String imdbId = null;
// find last match
while (matcher.find()) {
imdbId = matcher.group(1);
}
if (imdbId == null)
throw new IllegalArgumentException(String.format("Cannot find imdb id: %s", link));
return Integer.parseInt(imdbId);
}
@Override @Override
public URI getEpisodeListLink(SearchResult searchResult) { public URI getEpisodeListLink(SearchResult searchResult) {
return getEpisodeListLink(searchResult, 0); return getEpisodeListLink(searchResult, 0);

View File

@ -144,23 +144,34 @@ public class TVDotComClient implements EpisodeListProvider {
List<Node> nodes = selectNodes("id('episode_guide_list')//*[@class='info']", dom); List<Node> nodes = selectNodes("id('episode_guide_list')//*[@class='info']", dom);
Pattern seasonEpisodePattern = Pattern.compile("Season (\\d+), Episode (\\d+)"); Pattern episodePattern = Pattern.compile("Season (\\d+). Episode (\\d+)");
Pattern specialPattern = Pattern.compile("Special. Season (\\d+)");
List<Episode> episodes = new ArrayList<Episode>(nodes.size()); List<Episode> episodes = new ArrayList<Episode>(nodes.size());
for (Node node : nodes) { for (Node node : nodes) {
String meta = selectString("./*[@class='meta']", node); String title = selectString("./H3/A/text()", node);
String meta = selectString("./*[@class='meta']", node).replaceAll("\\p{Space}+", " ");
// normalize space and then match season and episode numbers String season = null;
Matcher matcher = seasonEpisodePattern.matcher(meta.replaceAll("\\p{Space}+", " ")); String episode = null;
if (matcher.find()) { Matcher matcher;
String title = selectString("./H3/A/text()", node);
String season = matcher.group(1);
String episode = matcher.group(2);
episodes.add(new Episode(searchResult.getName(), season, episode, title)); if ((matcher = episodePattern.matcher(meta)).find()) {
// matches episode
season = matcher.group(1);
episode = matcher.group(2);
} else if ((matcher = specialPattern.matcher(meta)).find()) {
// matches special
season = matcher.group(1);
episode = "Special";
} else {
// no episode match
continue;
} }
episodes.add(new Episode(searchResult.getName(), season, episode, title));
} }
return episodes; return episodes;

View File

@ -5,6 +5,7 @@ package net.sourceforge.filebot.web;
import static net.sourceforge.filebot.web.EpisodeListUtilities.filterBySeason; import static net.sourceforge.filebot.web.EpisodeListUtilities.filterBySeason;
import static net.sourceforge.filebot.web.EpisodeListUtilities.getLastSeason; import static net.sourceforge.filebot.web.EpisodeListUtilities.getLastSeason;
import static net.sourceforge.filebot.web.WebRequest.getDocument; import static net.sourceforge.filebot.web.WebRequest.getDocument;
import static net.sourceforge.tuned.XPathUtilities.getAttribute;
import static net.sourceforge.tuned.XPathUtilities.getTextContent; import static net.sourceforge.tuned.XPathUtilities.getTextContent;
import static net.sourceforge.tuned.XPathUtilities.selectNodes; import static net.sourceforge.tuned.XPathUtilities.selectNodes;
import static net.sourceforge.tuned.XPathUtilities.selectString; import static net.sourceforge.tuned.XPathUtilities.selectString;
@ -94,14 +95,20 @@ public class TVRageClient implements EpisodeListProvider {
Document dom = getDocument(episodeListUrl); Document dom = getDocument(episodeListUrl);
String seriesName = selectString("Show/name", dom); String seriesName = selectString("Show/name", dom);
List<Node> nodes = selectNodes("Show/Episodelist/Season/episode", dom);
List<Episode> episodes = new ArrayList<Episode>(nodes.size()); List<Episode> episodes = new ArrayList<Episode>(25);
for (Node node : nodes) { // episodes and specials
for (Node node : selectNodes("//episode", dom)) {
String title = getTextContent("title", node); String title = getTextContent("title", node);
String episodeNumber = getTextContent("seasonnum", node); String episodeNumber = getTextContent("seasonnum", node);
String seasonNumber = node.getParentNode().getAttributes().getNamedItem("no").getTextContent(); String seasonNumber = getAttribute("no", node.getParentNode());
// check if we have season and episode number, if not it must be a special episode
if (episodeNumber == null || seasonNumber == null) {
episodeNumber = "Special";
seasonNumber = getTextContent("season", node);
}
episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, title)); episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, title));
} }

View File

@ -144,6 +144,13 @@ public class TheTVDBClient implements EpisodeListProvider {
String episodeNumber = getTextContent("EpisodeNumber", node); String episodeNumber = getTextContent("EpisodeNumber", node);
String seasonNumber = getTextContent("SeasonNumber", node); String seasonNumber = getTextContent("SeasonNumber", node);
if (seasonNumber.equals("0")) {
String airsBefore = getTextContent("airsbefore_season", node);
seasonNumber = airsBefore.isEmpty() ? null : airsBefore;
episodeNumber = "Special";
}
episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, episodeName)); episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, episodeName));
if (episodeNumber.equals("1")) { if (episodeNumber.equals("1")) {

View File

@ -77,7 +77,12 @@ public final class XPathUtilities {
public static String getAttribute(String attribute, Node node) { public static String getAttribute(String attribute, Node node) {
return node.getAttributes().getNamedItem(attribute).getNodeValue().trim(); Node attributeNode = node.getAttributes().getNamedItem(attribute);
if (attributeNode != null)
return attributeNode.getNodeValue().trim();
return null;
} }

View File

@ -15,7 +15,7 @@ public class ArgumentBeanTest {
@Test @Test
public void clear() throws Exception { public void clear() throws Exception {
ArgumentBean bean = parse("-clear", "--analyze", "One Piece", "Naruto"); ArgumentBean bean = parse("-clear", "--sfv", "One Piece", "Naruto");
assertTrue(bean.clear()); assertTrue(bean.clear());
assertFalse(bean.help()); assertFalse(bean.help());

View File

@ -2,7 +2,9 @@
package net.sourceforge.filebot.similarity; package net.sourceforge.filebot.similarity;
import static net.sourceforge.filebot.similarity.SeasonEpisodeMatcher.SxE.UNDEFINED;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import net.sourceforge.filebot.similarity.SeasonEpisodeMatcher.SxE;
import org.junit.Test; import org.junit.Test;
@ -15,52 +17,52 @@ public class SeasonEpisodeMatcherTest {
@Test @Test
public void patternPrecedence() { public void patternPrecedence() {
// S01E01 pattern has highest precedence // S01E01 pattern has highest precedence
assertEquals("1x03", matcher.match("Test.101.1x02.S01E03").get(0).toString()); assertEquals(new SxE(1, 3), matcher.match("Test.101.1x02.S01E03").get(0));
// multiple values // multiple values
assertEquals("1x02", matcher.match("Test.42.s01e01.s01e02.300").get(1).toString()); assertEquals(new SxE(1, 2), matcher.match("Test.42.s01e01.s01e02.300").get(1));
} }
@Test @Test
public void pattern_1x01() { public void pattern_1x01() {
assertEquals("1x01", matcher.match("1x01").get(0).toString()); assertEquals(new SxE(1, 1), matcher.match("1x01").get(0));
// test multiple matches // test multiple matches
assertEquals("1x02", matcher.match("Test - 1x01 and 1.02 - Multiple MatchCollection").get(1).toString()); assertEquals(new SxE(1, 2), matcher.match("Test - 1x01 and 1.02 - Multiple MatchCollection").get(1));
// test high values // test high values
assertEquals("12x345", matcher.match("Test - 12x345 - High Values").get(0).toString()); assertEquals(new SxE(12, 345), matcher.match("Test - 12x345 - High Values").get(0));
// test lookahead and lookbehind // test lookahead and lookbehind
assertEquals("1x03", matcher.match("Test_-_103_[1280x720]").get(0).toString()); assertEquals(new SxE(1, 3), matcher.match("Test_-_103_[1280x720]").get(0));
} }
@Test @Test
public void pattern_S01E01() { public void pattern_S01E01() {
assertEquals("1x01", matcher.match("S01E01").get(0).toString()); assertEquals(new SxE(1, 1), matcher.match("S01E01").get(0));
// test multiple matches // test multiple matches
assertEquals("1x02", matcher.match("S01E01 and S01E02 - Multiple MatchCollection").get(1).toString()); assertEquals(new SxE(1, 2), matcher.match("S01E01 and S01E02 - Multiple MatchCollection").get(1));
// test separated values // test separated values
assertEquals("1x03", matcher.match("[s01]_[e03]").get(0).toString()); assertEquals(new SxE(1, 3), matcher.match("[s01]_[e03]").get(0));
// test high values // test high values
assertEquals("12x345", matcher.match("Test - S12E345 - High Values").get(0).toString()); assertEquals(new SxE(12, 345), matcher.match("Test - S12E345 - High Values").get(0));
} }
@Test @Test
public void pattern_101() { public void pattern_101() {
assertEquals("1x01", matcher.match("Test.101").get(0).toString()); assertEquals(new SxE(1, 1), matcher.match("Test.101").get(0));
// test 2-digit number // test 2-digit number
assertEquals("0x02", matcher.match("02").get(0).toString()); assertEquals(new SxE(UNDEFINED, 2), matcher.match("02").get(0));
// test high values // test high values
assertEquals("10x01", matcher.match("[Test]_1001_High_Values").get(0).toString()); assertEquals(new SxE(10, 1), matcher.match("[Test]_1001_High_Values").get(0));
// first two digits <= 29 // first two digits <= 29
assertEquals(null, matcher.match("The 4400")); assertEquals(null, matcher.match("The 4400"));

View File

@ -20,8 +20,8 @@ public class SeasonEpisodeSimilarityMetricTest {
// multiple pattern matches, single episode match // multiple pattern matches, single episode match
assertEquals(1.0, metric.getSimilarity("1x02a", "101 102 103"), 0); assertEquals(1.0, metric.getSimilarity("1x02a", "101 102 103"), 0);
// multiple pattern matches, no episode match // multiple pattern matches, only partial match (season)
assertEquals(0.0, metric.getSimilarity("1x03b", "104 105 106"), 0); assertEquals(0.5, metric.getSimilarity("1x03b", "104 105 106"), 0);
// no pattern match, no episode match // no pattern match, no episode match
assertEquals(0.0, metric.getSimilarity("abc", "xyz"), 0); assertEquals(0.0, metric.getSimilarity("abc", "xyz"), 0);

View File

@ -67,8 +67,8 @@ public class AnidbClientTest {
assertEquals("Monster", first.getSeriesName()); assertEquals("Monster", first.getSeriesName());
assertEquals("Herr Dr. Tenma", first.getTitle()); assertEquals("Herr Dr. Tenma", first.getTitle());
assertEquals("1", first.getEpisodeNumber()); assertEquals("1", first.getEpisode());
assertEquals(null, first.getSeasonNumber()); assertEquals(null, first.getSeason());
} }
@ -82,8 +82,8 @@ public class AnidbClientTest {
assertEquals("Juuni Kokuki", first.getSeriesName()); assertEquals("Juuni Kokuki", first.getSeriesName());
assertEquals("Shadow of the Moon, The Sea of Shadow - Chapter 1", first.getTitle()); assertEquals("Shadow of the Moon, The Sea of Shadow - Chapter 1", first.getTitle());
assertEquals("1", first.getEpisodeNumber()); assertEquals("1", first.getEpisode());
assertEquals(null, first.getSeasonNumber()); assertEquals(null, first.getSeason());
} }

View File

@ -27,6 +27,20 @@ public class IMDbClientTest {
} }
@Test
public void searchResultPageRedirect() throws Exception {
List<SearchResult> results = imdb.search("my name is earl");
// exactly one search result
assertEquals(1, results.size(), 0);
MovieDescriptor movie = (MovieDescriptor) results.get(0);
assertEquals("My Name Is Earl", movie.getName());
assertEquals(460091, movie.getImdbId(), 0);
}
@Test @Test
public void getEpisodeList() throws Exception { public void getEpisodeList() throws Exception {
List<Episode> list = imdb.getEpisodeList(new MovieDescriptor("Buffy", 118276)); List<Episode> list = imdb.getEpisodeList(new MovieDescriptor("Buffy", 118276));
@ -37,15 +51,15 @@ public class IMDbClientTest {
assertEquals("Buffy the Vampire Slayer", first.getSeriesName()); assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
assertEquals("Unaired Pilot", first.getTitle()); assertEquals("Unaired Pilot", first.getTitle());
assertEquals("0", first.getEpisodeNumber()); assertEquals("0", first.getEpisode());
assertEquals("1", first.getSeasonNumber()); assertEquals("1", first.getSeason());
Episode last = list.get(144); Episode last = list.get(144);
assertEquals("Buffy the Vampire Slayer", last.getSeriesName()); assertEquals("Buffy the Vampire Slayer", last.getSeriesName());
assertEquals("Chosen", last.getTitle()); assertEquals("Chosen", last.getTitle());
assertEquals("22", last.getEpisodeNumber()); assertEquals("22", last.getEpisode());
assertEquals("7", last.getSeasonNumber()); assertEquals("7", last.getSeason());
} }
@ -59,8 +73,8 @@ public class IMDbClientTest {
assertEquals("Mushishi", first.getSeriesName()); assertEquals("Mushishi", first.getSeriesName());
assertEquals("Midori no za", first.getTitle()); assertEquals("Midori no za", first.getTitle());
assertEquals("1", first.getEpisodeNumber()); assertEquals("1", first.getEpisode());
assertEquals("1", first.getSeasonNumber()); assertEquals("1", first.getSeason());
} }

View File

@ -49,8 +49,8 @@ public class TVDotComClientTest {
assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName()); assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName());
assertEquals("Chosen", chosen.getTitle()); assertEquals("Chosen", chosen.getTitle());
assertEquals("22", chosen.getEpisodeNumber()); assertEquals("22", chosen.getEpisode());
assertEquals("7", chosen.getSeasonNumber()); assertEquals("7", chosen.getSeason());
} }
@ -65,8 +65,8 @@ public class TVDotComClientTest {
assertEquals("Buffy the Vampire Slayer", first.getSeriesName()); assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
assertEquals("Welcome to the Hellmouth (1)", first.getTitle()); assertEquals("Welcome to the Hellmouth (1)", first.getTitle());
assertEquals("1", first.getEpisodeNumber()); assertEquals("1", first.getEpisode());
assertEquals("1", first.getSeasonNumber()); assertEquals("1", first.getSeason());
} }
@ -81,8 +81,8 @@ public class TVDotComClientTest {
assertEquals("Firefly", fourth.getSeriesName()); assertEquals("Firefly", fourth.getSeriesName());
assertEquals("Jaynestown", fourth.getTitle()); assertEquals("Jaynestown", fourth.getTitle());
assertEquals("4", fourth.getEpisodeNumber()); assertEquals("4", fourth.getEpisode());
assertEquals("1", fourth.getSeasonNumber()); assertEquals("1", fourth.getSeason());
} }
@ -104,8 +104,8 @@ public class TVDotComClientTest {
assertEquals("Lost", episode.getSeriesName()); assertEquals("Lost", episode.getSeriesName());
assertEquals("Exposé", episode.getTitle()); assertEquals("Exposé", episode.getTitle());
assertEquals("14", episode.getEpisodeNumber()); assertEquals("14", episode.getEpisode());
assertEquals("3", episode.getSeasonNumber()); assertEquals("3", episode.getSeason());
} }

View File

@ -43,8 +43,8 @@ public class TVRageClientTest {
assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName()); assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName());
assertEquals("Chosen", chosen.getTitle()); assertEquals("Chosen", chosen.getTitle());
assertEquals("22", chosen.getEpisodeNumber()); assertEquals("22", chosen.getEpisode());
assertEquals("7", chosen.getSeasonNumber()); assertEquals("7", chosen.getSeason());
} }
@ -58,8 +58,8 @@ public class TVRageClientTest {
assertEquals("Buffy the Vampire Slayer", first.getSeriesName()); assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
assertEquals("Unaired Pilot", first.getTitle()); assertEquals("Unaired Pilot", first.getTitle());
assertEquals("00", first.getEpisodeNumber()); assertEquals("00", first.getEpisode());
assertEquals("0", first.getSeasonNumber()); assertEquals("0", first.getSeason());
} }

View File

@ -72,8 +72,8 @@ public class TheTVDBClientTest {
assertEquals("Buffy the Vampire Slayer", first.getSeriesName()); assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
assertEquals("Unaired Pilot", first.getTitle()); assertEquals("Unaired Pilot", first.getTitle());
assertEquals("1", first.getEpisodeNumber()); assertEquals("Special", first.getEpisode());
assertEquals("0", first.getSeasonNumber()); assertEquals("1", first.getSeason());
} }
@ -87,8 +87,8 @@ public class TheTVDBClientTest {
assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName()); assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName());
assertEquals("Chosen", chosen.getTitle()); assertEquals("Chosen", chosen.getTitle());
assertEquals("22", chosen.getEpisodeNumber()); assertEquals("22", chosen.getEpisode());
assertEquals("7", chosen.getSeasonNumber()); assertEquals("7", chosen.getSeason());
} }