+ 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:
parent
416384901b
commit
1ed4d2b495
|
@ -57,13 +57,13 @@ public class EpisodeFormatBindingBean {
|
|||
|
||||
@Define("s")
|
||||
public String getSeasonNumber() {
|
||||
return episode.getSeasonNumber();
|
||||
return episode.getSeason();
|
||||
}
|
||||
|
||||
|
||||
@Define("e")
|
||||
public String getEpisodeNumber() {
|
||||
return episode.getEpisodeNumber();
|
||||
return episode.getEpisode();
|
||||
}
|
||||
|
||||
|
||||
|
@ -123,7 +123,9 @@ public class EpisodeFormatBindingBean {
|
|||
|
||||
@Define("crc32")
|
||||
public String getCRC32() throws IOException, InterruptedException {
|
||||
if (mediaFile != null) {
|
||||
// make sure media file is defined
|
||||
checkMediaFile();
|
||||
|
||||
// try to get checksum from file name
|
||||
String checksum = FileBotUtilities.getEmbeddedChecksum(mediaFile.getName());
|
||||
|
||||
|
@ -140,12 +142,12 @@ public class EpisodeFormatBindingBean {
|
|||
return crc32(mediaFile);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Define("ext")
|
||||
public String getContainerExtension() {
|
||||
// make sure media file is defined
|
||||
checkMediaFile();
|
||||
|
||||
// file extension
|
||||
return FileUtilities.getExtension(mediaFile);
|
||||
}
|
||||
|
@ -193,12 +195,21 @@ public class EpisodeFormatBindingBean {
|
|||
}
|
||||
|
||||
|
||||
private synchronized MediaInfo getMediaInfo() {
|
||||
if (mediaFile == null) {
|
||||
throw new NullPointerException("Media file is null");
|
||||
private void checkMediaFile() {
|
||||
// make sure file is not 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) {
|
||||
// make sure media file is defined
|
||||
checkMediaFile();
|
||||
|
||||
mediaInfo = new MediaInfo();
|
||||
|
||||
if (!mediaInfo.open(mediaFile)) {
|
||||
|
|
|
@ -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) {
|
||||
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) {
|
||||
return this.replace(/\s/g, replacement);
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.security.PrivilegedExceptionAction;
|
|||
import java.security.ProtectionDomain;
|
||||
import java.text.FieldPosition;
|
||||
import java.text.Format;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParsePosition;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -125,6 +126,11 @@ public class ExpressionFormat extends Format {
|
|||
Object value = ((CompiledScript) snipped).eval(context);
|
||||
|
||||
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);
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
|
|
Before Width: | Height: | Size: 748 B After Width: | Height: | Size: 748 B |
|
@ -64,6 +64,8 @@ public class SeasonEpisodeMatcher {
|
|||
|
||||
public static class SxE {
|
||||
|
||||
public static final int UNDEFINED = -1;
|
||||
|
||||
public final int season;
|
||||
public final int episode;
|
||||
|
||||
|
@ -81,7 +83,11 @@ public class SeasonEpisodeMatcher {
|
|||
|
||||
|
||||
protected int parse(String number) {
|
||||
return number == null || number.isEmpty() ? 0 : Integer.parseInt(number);
|
||||
try {
|
||||
return Integer.parseInt(number);
|
||||
} catch (Exception e) {
|
||||
return UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,12 +27,12 @@ public class SeasonEpisodeSimilarityMetric implements SimilarityMetric {
|
|||
|
||||
for (SxE sxe1 : sxeVector1) {
|
||||
for (SxE sxe2 : sxeVector2) {
|
||||
if (sxe1.episode == sxe2.episode) {
|
||||
if (sxe1.season == sxe2.season) {
|
||||
if (sxe1.episode == sxe2.episode && sxe1.season == sxe2.season) {
|
||||
// vectors have at least one perfect episode match in common
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sxe1.episode == sxe2.episode || sxe1.season == sxe2.season) {
|
||||
// at least we have a partial match
|
||||
similarity = 0.5f;
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ import net.sourceforge.filebot.Settings;
|
|||
import net.sourceforge.filebot.format.EpisodeFormatBindingBean;
|
||||
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||
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.ExceptionUtilities;
|
||||
import net.sourceforge.tuned.ui.GradientStyle;
|
||||
|
@ -392,10 +392,13 @@ public class EpisodeFormatDialog extends JDialog {
|
|||
try {
|
||||
preview.setText(get());
|
||||
|
||||
// check internal script exception and empty output
|
||||
// check internal script exception
|
||||
if (format.scriptException() != null) {
|
||||
throw format.scriptException();
|
||||
} else if (get().trim().isEmpty()) {
|
||||
}
|
||||
|
||||
// check empty output
|
||||
if (get().trim().isEmpty()) {
|
||||
throw new RuntimeException("Formatted value is empty");
|
||||
}
|
||||
|
||||
|
@ -406,7 +409,7 @@ public class EpisodeFormatDialog extends JDialog {
|
|||
status.setIcon(ResourceManager.getIcon("status.warning"));
|
||||
status.setVisible(true);
|
||||
} finally {
|
||||
preview.setVisible(true);
|
||||
preview.setVisible(preview.getText().trim().length() > 0);
|
||||
editor.setForeground(defaultColor);
|
||||
|
||||
progressIndicatorTimer.stop();
|
||||
|
|
|
@ -81,13 +81,15 @@ class AutoFetchEpisodeListMatcher extends SwingWorker<List<Match<File, Episode>>
|
|||
public Collection<Episode> call() throws Exception {
|
||||
Collection<SearchResult> results = provider.search(seriesName);
|
||||
|
||||
if (results.size() > 0) {
|
||||
if (results.isEmpty()) {
|
||||
throw new RuntimeException(String.format("'%s' has not been found.", seriesName));
|
||||
}
|
||||
|
||||
SearchResult selectedSearchResult = selectSearchResult(seriesName, results);
|
||||
|
||||
if (selectedSearchResult != null) {
|
||||
return provider.getEpisodeList(selectedSearchResult);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import net.sourceforge.filebot.format.EpisodeFormatBindingBean;
|
|||
import net.sourceforge.filebot.format.ExpressionFormat;
|
||||
import net.sourceforge.filebot.similarity.Match;
|
||||
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 {
|
||||
|
|
|
@ -79,13 +79,8 @@ class MatchAction extends AbstractAction {
|
|||
if (o instanceof Episode) {
|
||||
Episode episode = (Episode) o;
|
||||
|
||||
try {
|
||||
// create SxE from episode
|
||||
return Collections.singleton(new SxE(episode.getSeasonNumber(), episode.getEpisodeNumber()));
|
||||
} catch (NumberFormatException e) {
|
||||
// some kind of special episode, no SxE
|
||||
return null;
|
||||
}
|
||||
return Collections.singleton(new SxE(episode.getSeason(), episode.getEpisode()));
|
||||
}
|
||||
|
||||
return super.parse(o);
|
||||
|
|
|
@ -180,12 +180,12 @@ public class RenamePanel extends JComponent {
|
|||
|
||||
|
||||
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.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;
|
||||
}
|
||||
|
|
|
@ -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.matches("\\d+")) {
|
||||
// no seasons for anime
|
||||
episodes.add(new Episode(searchResult.getName(), number, title));
|
||||
episodes.add(new Episode(searchResult.getName(), null, number, title));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,42 +3,54 @@ package net.sourceforge.filebot.web;
|
|||
|
||||
|
||||
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 {
|
||||
|
||||
private String seriesName;
|
||||
private String seasonNumber;
|
||||
private String episodeNumber;
|
||||
private String title;
|
||||
private final String seriesName;
|
||||
private final String season;
|
||||
private final String episode;
|
||||
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.seasonNumber = seasonNumber;
|
||||
this.episodeNumber = episodeNumber;
|
||||
this.season = season;
|
||||
this.episode = episode;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
|
||||
public Episode(String seriesName, String episodeNumber, String title) {
|
||||
this(seriesName, null, episodeNumber, title);
|
||||
public String getEpisode() {
|
||||
return episode;
|
||||
}
|
||||
|
||||
|
||||
public String getEpisodeNumber() {
|
||||
return episodeNumber;
|
||||
public Integer getEpisodeNumber() {
|
||||
try {
|
||||
return Integer.valueOf(episode);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getSeasonNumber() {
|
||||
return seasonNumber;
|
||||
public String getSeason() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,7 @@ public final class EpisodeListUtilities {
|
|||
// filter given season from all seasons
|
||||
for (Episode episode : episodes) {
|
||||
try {
|
||||
if (season == Integer.parseInt(episode.getSeasonNumber())) {
|
||||
if (season == Integer.parseInt(episode.getSeason())) {
|
||||
results.add(episode);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
|
@ -33,7 +33,7 @@ public final class EpisodeListUtilities {
|
|||
// filter given season from all seasons
|
||||
for (Episode episode : episodes) {
|
||||
try {
|
||||
lastSeason = Math.max(lastSeason, Integer.parseInt(episode.getSeasonNumber()));
|
||||
lastSeason = Math.max(lastSeason, Integer.parseInt(episode.getSeason()));
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore illegal episodes
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ import java.net.URLEncoder;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.swing.Icon;
|
||||
|
||||
|
@ -68,9 +70,16 @@ public class IMDbClient implements EpisodeListProvider {
|
|||
String href = getAttribute("href", node);
|
||||
|
||||
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;
|
||||
|
@ -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
|
||||
public URI getEpisodeListLink(SearchResult searchResult) {
|
||||
return getEpisodeListLink(searchResult, 0);
|
||||
|
|
|
@ -144,24 +144,35 @@ public class TVDotComClient implements EpisodeListProvider {
|
|||
|
||||
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());
|
||||
|
||||
for (Node node : nodes) {
|
||||
String meta = selectString("./*[@class='meta']", node);
|
||||
|
||||
// normalize space and then match season and episode numbers
|
||||
Matcher matcher = seasonEpisodePattern.matcher(meta.replaceAll("\\p{Space}+", " "));
|
||||
|
||||
if (matcher.find()) {
|
||||
String title = selectString("./H3/A/text()", node);
|
||||
String season = matcher.group(1);
|
||||
String episode = matcher.group(2);
|
||||
String meta = selectString("./*[@class='meta']", node).replaceAll("\\p{Space}+", " ");
|
||||
|
||||
String season = null;
|
||||
String episode = null;
|
||||
|
||||
Matcher matcher;
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ package net.sourceforge.filebot.web;
|
|||
import static net.sourceforge.filebot.web.EpisodeListUtilities.filterBySeason;
|
||||
import static net.sourceforge.filebot.web.EpisodeListUtilities.getLastSeason;
|
||||
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.selectNodes;
|
||||
import static net.sourceforge.tuned.XPathUtilities.selectString;
|
||||
|
@ -94,14 +95,20 @@ public class TVRageClient implements EpisodeListProvider {
|
|||
Document dom = getDocument(episodeListUrl);
|
||||
|
||||
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 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));
|
||||
}
|
||||
|
|
|
@ -144,6 +144,13 @@ public class TheTVDBClient implements EpisodeListProvider {
|
|||
String episodeNumber = getTextContent("EpisodeNumber", 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));
|
||||
|
||||
if (episodeNumber.equals("1")) {
|
||||
|
|
|
@ -77,7 +77,12 @@ public final class XPathUtilities {
|
|||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ public class ArgumentBeanTest {
|
|||
|
||||
@Test
|
||||
public void clear() throws Exception {
|
||||
ArgumentBean bean = parse("-clear", "--analyze", "One Piece", "Naruto");
|
||||
ArgumentBean bean = parse("-clear", "--sfv", "One Piece", "Naruto");
|
||||
|
||||
assertTrue(bean.clear());
|
||||
assertFalse(bean.help());
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
package net.sourceforge.filebot.similarity;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.similarity.SeasonEpisodeMatcher.SxE.UNDEFINED;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import net.sourceforge.filebot.similarity.SeasonEpisodeMatcher.SxE;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -15,52 +17,52 @@ public class SeasonEpisodeMatcherTest {
|
|||
@Test
|
||||
public void patternPrecedence() {
|
||||
// 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
assertEquals("1x03", matcher.match("Test_-_103_[1280x720]").get(0).toString());
|
||||
assertEquals(new SxE(1, 3), matcher.match("Test_-_103_[1280x720]").get(0));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
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
|
||||
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
|
||||
assertEquals("1x03", matcher.match("[s01]_[e03]").get(0).toString());
|
||||
assertEquals(new SxE(1, 3), matcher.match("[s01]_[e03]").get(0));
|
||||
|
||||
// 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
|
||||
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
|
||||
assertEquals("0x02", matcher.match("02").get(0).toString());
|
||||
assertEquals(new SxE(UNDEFINED, 2), matcher.match("02").get(0));
|
||||
|
||||
// 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
|
||||
assertEquals(null, matcher.match("The 4400"));
|
||||
|
|
|
@ -20,8 +20,8 @@ public class SeasonEpisodeSimilarityMetricTest {
|
|||
// multiple pattern matches, single episode match
|
||||
assertEquals(1.0, metric.getSimilarity("1x02a", "101 102 103"), 0);
|
||||
|
||||
// multiple pattern matches, no episode match
|
||||
assertEquals(0.0, metric.getSimilarity("1x03b", "104 105 106"), 0);
|
||||
// multiple pattern matches, only partial match (season)
|
||||
assertEquals(0.5, metric.getSimilarity("1x03b", "104 105 106"), 0);
|
||||
|
||||
// no pattern match, no episode match
|
||||
assertEquals(0.0, metric.getSimilarity("abc", "xyz"), 0);
|
||||
|
|
|
@ -67,8 +67,8 @@ public class AnidbClientTest {
|
|||
|
||||
assertEquals("Monster", first.getSeriesName());
|
||||
assertEquals("Herr Dr. Tenma", first.getTitle());
|
||||
assertEquals("1", first.getEpisodeNumber());
|
||||
assertEquals(null, first.getSeasonNumber());
|
||||
assertEquals("1", first.getEpisode());
|
||||
assertEquals(null, first.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
@ -82,8 +82,8 @@ public class AnidbClientTest {
|
|||
|
||||
assertEquals("Juuni Kokuki", first.getSeriesName());
|
||||
assertEquals("Shadow of the Moon, The Sea of Shadow - Chapter 1", first.getTitle());
|
||||
assertEquals("1", first.getEpisodeNumber());
|
||||
assertEquals(null, first.getSeasonNumber());
|
||||
assertEquals("1", first.getEpisode());
|
||||
assertEquals(null, first.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
public void getEpisodeList() throws Exception {
|
||||
List<Episode> list = imdb.getEpisodeList(new MovieDescriptor("Buffy", 118276));
|
||||
|
@ -37,15 +51,15 @@ public class IMDbClientTest {
|
|||
|
||||
assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
|
||||
assertEquals("Unaired Pilot", first.getTitle());
|
||||
assertEquals("0", first.getEpisodeNumber());
|
||||
assertEquals("1", first.getSeasonNumber());
|
||||
assertEquals("0", first.getEpisode());
|
||||
assertEquals("1", first.getSeason());
|
||||
|
||||
Episode last = list.get(144);
|
||||
|
||||
assertEquals("Buffy the Vampire Slayer", last.getSeriesName());
|
||||
assertEquals("Chosen", last.getTitle());
|
||||
assertEquals("22", last.getEpisodeNumber());
|
||||
assertEquals("7", last.getSeasonNumber());
|
||||
assertEquals("22", last.getEpisode());
|
||||
assertEquals("7", last.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
@ -59,8 +73,8 @@ public class IMDbClientTest {
|
|||
|
||||
assertEquals("Mushishi", first.getSeriesName());
|
||||
assertEquals("Midori no za", first.getTitle());
|
||||
assertEquals("1", first.getEpisodeNumber());
|
||||
assertEquals("1", first.getSeasonNumber());
|
||||
assertEquals("1", first.getEpisode());
|
||||
assertEquals("1", first.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -49,8 +49,8 @@ public class TVDotComClientTest {
|
|||
|
||||
assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName());
|
||||
assertEquals("Chosen", chosen.getTitle());
|
||||
assertEquals("22", chosen.getEpisodeNumber());
|
||||
assertEquals("7", chosen.getSeasonNumber());
|
||||
assertEquals("22", chosen.getEpisode());
|
||||
assertEquals("7", chosen.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
@ -65,8 +65,8 @@ public class TVDotComClientTest {
|
|||
|
||||
assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
|
||||
assertEquals("Welcome to the Hellmouth (1)", first.getTitle());
|
||||
assertEquals("1", first.getEpisodeNumber());
|
||||
assertEquals("1", first.getSeasonNumber());
|
||||
assertEquals("1", first.getEpisode());
|
||||
assertEquals("1", first.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,8 +81,8 @@ public class TVDotComClientTest {
|
|||
|
||||
assertEquals("Firefly", fourth.getSeriesName());
|
||||
assertEquals("Jaynestown", fourth.getTitle());
|
||||
assertEquals("4", fourth.getEpisodeNumber());
|
||||
assertEquals("1", fourth.getSeasonNumber());
|
||||
assertEquals("4", fourth.getEpisode());
|
||||
assertEquals("1", fourth.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
@ -104,8 +104,8 @@ public class TVDotComClientTest {
|
|||
|
||||
assertEquals("Lost", episode.getSeriesName());
|
||||
assertEquals("Exposé", episode.getTitle());
|
||||
assertEquals("14", episode.getEpisodeNumber());
|
||||
assertEquals("3", episode.getSeasonNumber());
|
||||
assertEquals("14", episode.getEpisode());
|
||||
assertEquals("3", episode.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -43,8 +43,8 @@ public class TVRageClientTest {
|
|||
|
||||
assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName());
|
||||
assertEquals("Chosen", chosen.getTitle());
|
||||
assertEquals("22", chosen.getEpisodeNumber());
|
||||
assertEquals("7", chosen.getSeasonNumber());
|
||||
assertEquals("22", chosen.getEpisode());
|
||||
assertEquals("7", chosen.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
@ -58,8 +58,8 @@ public class TVRageClientTest {
|
|||
|
||||
assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
|
||||
assertEquals("Unaired Pilot", first.getTitle());
|
||||
assertEquals("00", first.getEpisodeNumber());
|
||||
assertEquals("0", first.getSeasonNumber());
|
||||
assertEquals("00", first.getEpisode());
|
||||
assertEquals("0", first.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -72,8 +72,8 @@ public class TheTVDBClientTest {
|
|||
|
||||
assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
|
||||
assertEquals("Unaired Pilot", first.getTitle());
|
||||
assertEquals("1", first.getEpisodeNumber());
|
||||
assertEquals("0", first.getSeasonNumber());
|
||||
assertEquals("Special", first.getEpisode());
|
||||
assertEquals("1", first.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
@ -87,8 +87,8 @@ public class TheTVDBClientTest {
|
|||
|
||||
assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName());
|
||||
assertEquals("Chosen", chosen.getTitle());
|
||||
assertEquals("22", chosen.getEpisodeNumber());
|
||||
assertEquals("7", chosen.getSeasonNumber());
|
||||
assertEquals("22", chosen.getEpisode());
|
||||
assertEquals("7", chosen.getSeason());
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue