* improved support for airdate

* refactor SxE from String to Integer types
This commit is contained in:
Reinhard Pointner 2010-10-24 12:10:30 +00:00
parent 5db098e95a
commit f53887c7ea
21 changed files with 168 additions and 158 deletions

View File

@ -53,13 +53,13 @@ public class EpisodeBindingBean {
@Define("s")
public String getSeasonNumber() {
public Integer getSeasonNumber() {
return episode.getSeason();
}
@Define("e")
public String getEpisodeNumber() {
public Integer getEpisodeNumber() {
return episode.getEpisode();
}
@ -154,6 +154,16 @@ public class EpisodeBindingBean {
}
@Define("fn")
public String getFileName() {
// make sure media file is defined
checkMediaFile();
// file extension
return FileUtilities.getName(mediaFile);
}
@Define("ext")
public String getExtension() {
// make sure media file is defined

View File

@ -54,6 +54,12 @@ String.metaClass.upperInitial = { replaceAll(/\b[a-z]/, { it.toUpperCase() }) }
String.metaClass.lowerTrail = { replaceAll(/\b(\p{Alpha})(\p{Alpha}+)\b/, { match, initial, trail -> initial + trail.toLowerCase() }) }
/**
* Return substring that matches the given pattern.
*/
String.metaClass.match = { def matcher = delegate =~ it; matcher.find() ? matcher[0] : "" }
/**
* Return substring before the given pattern.
*/

View File

@ -244,7 +244,7 @@ class EpisodeBindingDialog extends JDialog {
public void setEpisode(Episode episode) {
episodeTextField.setText(episode == null ? "" : EpisodeFormat.getInstance().format(episode));
episodeTextField.setText(episode == null ? "" : EpisodeFormat.getDefaultInstance().format(episode));
}
@ -255,7 +255,7 @@ class EpisodeBindingDialog extends JDialog {
public Episode getEpisode() {
try {
return EpisodeFormat.getInstance().parseObject(episodeTextField.getText());
return EpisodeFormat.getDefaultInstance().parseObject(episodeTextField.getText());
} catch (Exception e) {
return null;
}

View File

@ -8,7 +8,8 @@ expr[a1]: n
expr[a2]: s
expr[a3]: e
expr[a4]: t
expr[a5]: episode
expr[a5]: air
expr[a6]: episode
# simple mediainfo expressions
expr[b1]: vc
@ -18,10 +19,10 @@ expr[b4]: vf
expr[b5]: resolution
# file expressions
expr[c1]: file.name
expr[c2]: file.parent
expr[c1]: crc32
expr[c2]: fn
expr[c3]: ext
expr[c4]: crc32
expr[c4]: file
# media info expressions [media]
expr[d1]: media.title

View File

@ -41,7 +41,7 @@ class EpisodeExpressionFormatter implements MatchFormatter {
@Override
public String preview(Match<?, ?> match) {
return EpisodeFormat.getInstance().format(match.getValue());
return EpisodeFormat.getSeasonEpisodeInstance().format(match.getValue());
}

View File

@ -288,10 +288,10 @@ class EpisodeFormatDialog extends JDialog {
// restore episode
try {
episode = EpisodeFormat.getInstance().parseObject(persistentSampleEpisode.getValue());
episode = EpisodeFormat.getDefaultInstance().parseObject(persistentSampleEpisode.getValue());
} catch (Exception e) {
// default sample
episode = new Episode("Dark Angel", "3", "1", "Labyrinth", null, new Date(2009, 6, 1));
episode = new Episode("Dark Angel", 3, 1, "Labyrinth", null, new Date(2009, 6, 1));
}
// restore media file
@ -464,7 +464,7 @@ class EpisodeFormatDialog extends JDialog {
sample = new EpisodeBindingBean(episode, file);
// remember
persistentSampleEpisode.setValue(episode == null ? "" : EpisodeFormat.getInstance().format(sample.getEpisode()));
persistentSampleEpisode.setValue(episode == null ? "" : EpisodeFormat.getDefaultInstance().format(sample.getEpisode()));
persistentSampleFile.setValue(file == null ? "" : sample.getMediaFile().getAbsolutePath());
// reevaluate everything

View File

@ -141,10 +141,10 @@ public class AnidbClient implements EpisodeListProvider {
List<Episode> episodes = new ArrayList<Episode>(25);
for (Node node : selectNodes("//ep", dom)) {
String number = getTextContent("epno", node);
Integer number = getIntegerContent("epno", node);
// ignore special episodes
if (number != null && number.matches("\\d+")) {
if (number != null) {
String title = selectString(".//title[@lang='en']", node);
String airdate = selectString(".//date/@rel", node);

View File

@ -9,12 +9,12 @@ import java.util.Arrays;
public class Episode implements Serializable {
private String seriesName;
private String season;
private String episode;
private Integer season;
private Integer episode;
private String title;
// special number
private String special;
private Integer special;
// episode airdate
private Date airdate;
@ -25,12 +25,12 @@ public class Episode implements Serializable {
}
public Episode(String seriesName, String season, String episode, String title) {
public Episode(String seriesName, Integer season, Integer episode, String title) {
this(seriesName, season, episode, title, null, null);
}
public Episode(String seriesName, String season, String episode, String title, String special, Date airdate) {
public Episode(String seriesName, Integer season, Integer episode, String title, Integer special, Date airdate) {
this.seriesName = seriesName;
this.season = season;
this.episode = episode;
@ -45,53 +45,26 @@ public class Episode implements Serializable {
}
public String getEpisode() {
public Integer getEpisode() {
return episode;
}
public Integer getEpisodeNumber() {
try {
return new Integer(episode);
} catch (NumberFormatException e) {
return null;
}
}
public String getSeason() {
public Integer getSeason() {
return season;
}
public Integer getSeasonNumber() {
try {
return new Integer(season);
} catch (NumberFormatException e) {
return null;
}
}
public String getTitle() {
return title;
}
public String getSpecial() {
public Integer getSpecial() {
return special;
}
public Integer getSpecialNumber() {
try {
return new Integer(special);
} catch (NumberFormatException e) {
return null;
}
}
public Date airdate() {
return airdate;
}
@ -124,7 +97,7 @@ public class Episode implements Serializable {
@Override
public String toString() {
return EpisodeFormat.getInstance().format(this);
return EpisodeFormat.getSeasonEpisodeInstance().format(this);
}
}

View File

@ -6,86 +6,108 @@ 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();
private boolean includeAirdate = true;
private boolean includeSpecial = true;
public static EpisodeFormat getInstance() {
return instance;
public static EpisodeFormat getSeasonEpisodeInstance() {
return new EpisodeFormat(true, false);
}
public static EpisodeFormat getDefaultInstance() {
return new EpisodeFormat(true, true);
}
public EpisodeFormat(boolean includeSpecial, boolean includeAirdate) {
this.includeSpecial = includeSpecial;
this.includeAirdate = includeAirdate;
}
@Override
public StringBuffer format(Object obj, StringBuffer sb, FieldPosition pos) {
// format episode object, e.g. Dark Angel - 3x01 - Labyrinth [2009-06-01]
Episode episode = (Episode) obj;
// episode number is most likely a number but could also be some kind of special identifier (e.g. Special)
String episodeNumber = episode.getEpisode();
// try to format episode number, if possible
try {
episodeNumber = String.format("%02d", Integer.parseInt(episodeNumber));
} catch (NumberFormatException e) {
// ignore
}
String episodeNumber = episode.getEpisode() != null ? String.format("%02d", episode.getEpisode()) : null;
// series name should not be empty or null
sb.append(episode.getSeriesName());
if (episode.getSeason() != null) {
// season and episode
sb.append(" - ").append(episode.getSeason()).append('x').append(episodeNumber);
} else if (episodeNumber != null) {
sb.append(" - ").append(episode.getSeason()).append('x');
if (episode.getEpisode() != null) {
sb.append(String.format("%02d", episode.getEpisode()));
} else if (includeSpecial && episode.getSpecial() != null) {
sb.append("Special " + episode.getSpecial());
}
} else {
// episode, but no season
sb.append(" - ").append(episodeNumber);
}
if (episode.getTitle() != null) {
sb.append(" - ").append(episode.getTitle());
sb.append(" - ").append(episode.getTitle());
if (includeAirdate && episode.airdate() != null) {
sb.append(" [").append(episode.airdate().format("yyyy-MM-dd")).append("]");
}
return sb;
}
private final Pattern sxePattern = Pattern.compile("- (?:(\\d{1,2})x)?(Special )?(\\d{1,3}) -");
private final Pattern airdatePattern = Pattern.compile("\\[(\\d{4}-\\d{1,2}-\\d{1,2})\\]");
@Override
public Episode parseObject(String source, ParsePosition pos) {
String[] section = source.substring(pos.getIndex()).split(" - ", 3);
public Episode parseObject(String s, ParsePosition pos) {
StringBuilder source = new StringBuilder(s);
// series name and episode identifier are required
if (section.length < 2) {
pos.setErrorIndex(0);
return null;
Integer season = null;
Integer episode = null;
Integer special = null;
Date airdate = null;
Matcher m;
if ((m = airdatePattern.matcher(source)).find()) {
airdate = Date.parse(m.group(1), "yyyy-MM-dd");
source.replace(m.start(), m.end(), ""); // remove matched part from text
}
// normalize and check
for (int i = 0; i < section.length; i++) {
section[i] = section[i].trim();
if ((m = sxePattern.matcher(source)).find()) {
season = new Integer(m.group(1));
if (m.group(2) == null)
episode = new Integer(m.group(3));
else
special = new Integer(m.group(3));
if (section[i].isEmpty()) {
pos.setErrorIndex(0);
return null;
}
source.replace(m.start(), m.end(), ""); // remove matched part from text
// assume that all the remaining text is series name and title
String name = source.substring(0, m.start()).trim();
String title = source.substring(m.start()).trim();
// did parse input
pos.setIndex(source.length());
return new Episode(name, season, episode, title, special, airdate);
}
String[] sxe = section[1].split("x", 2);
// series name
String name = section[0];
// season number and episode number
String season = (sxe.length == 2) ? sxe[0] : null;
String episode = (sxe.length == 2) ? sxe[1] : section[1];
// episode title
String title = (section.length == 3) ? section[2] : null;
// did parse input
pos.setIndex(source.length());
return new Episode(name, season, episode, title);
// failed to parse input
pos.setErrorIndex(0);
return null;
}

View File

@ -14,12 +14,8 @@ final class EpisodeListUtilities {
// filter given season from all seasons
for (Episode episode : episodes) {
try {
if (season == Integer.parseInt(episode.getSeason())) {
results.add(episode);
}
} catch (NumberFormatException e) {
// ignore illegal episodes
if (season == episode.getSeason()) {
results.add(episode);
}
}
@ -32,10 +28,8 @@ final class EpisodeListUtilities {
// filter given season from all seasons
for (Episode episode : episodes) {
try {
lastSeason = Math.max(lastSeason, Integer.parseInt(episode.getSeason()));
} catch (NumberFormatException e) {
// ignore illegal episodes
if (episode.getSeason() != null && episode.getSeason() > lastSeason) {
lastSeason = episode.getSeason();
}
}

View File

@ -100,8 +100,8 @@ public class IMDbClient implements EpisodeListProvider {
String title = getTextContent(node);
Scanner numberScanner = new Scanner(node.getPreviousSibling().getTextContent()).useDelimiter("\\D+");
String season = numberScanner.next();
String episode = numberScanner.next();
Integer season = numberScanner.nextInt();
Integer episode = numberScanner.nextInt();
// e.g. 20 May 2003
String airdate = selectString("./following::STRONG", node);

View File

@ -149,8 +149,7 @@ public class TVDotComClient implements EpisodeListProvider {
List<Node> nodes = selectNodes("id('episode_guide_list')//*[@class='info']", dom);
Pattern episodePattern = Pattern.compile("Season.(\\d+).+Episode.(\\d+)");
Pattern specialPattern = Pattern.compile("Special..Season.(\\d+)");
Pattern airdatePattern = Pattern.compile("(\\d{1,2}).(\\d{1,2}).(\\d{4})");
Pattern airdatePattern = Pattern.compile("\\d{1,2}.\\d{1,2}.\\d{4}");
List<Episode> episodes = new ArrayList<Episode>(nodes.size());
@ -158,26 +157,22 @@ public class TVDotComClient implements EpisodeListProvider {
String title = selectString("./H3/A/text()", node);
String meta = selectString("./*[@class='meta']", node).replaceAll("\\p{Space}+", " ");
String season = null;
String episode = null;
Integer season = null;
Integer episode = null;
Date airdate = null;
Matcher matcher;
Matcher m;
// try to match episode information
if ((matcher = episodePattern.matcher(meta)).find()) {
if ((m = 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";
season = new Integer(m.group(1));
episode = new Integer(m.group(2));
}
// try to match airdate information
if ((matcher = airdatePattern.matcher(meta)).find()) {
airdate = Date.parse(matcher.group(), "MM/dd/yyyy"); // e.g. 5/20/2003
if ((m = airdatePattern.matcher(meta)).find()) {
airdate = Date.parse(m.group(), "MM/dd/yyyy"); // e.g. 5/20/2003
}
// add episode if SxE info has been found

View File

@ -85,16 +85,17 @@ public class TVRageClient implements EpisodeListProvider {
// episodes and specials
for (Node node : selectNodes("//episode", dom)) {
String title = getTextContent("title", node);
String episodeNumber = getTextContent("seasonnum", node);
String seasonNumber = getAttribute("no", node.getParentNode());
Integer episodeNumber = getIntegerContent("seasonnum", node);
String seasonIdentifier = getAttribute("no", node.getParentNode());
Integer seasonNumber = seasonIdentifier == null ? null : new Integer(seasonIdentifier);
Date airdate = Date.parse(getTextContent("airdate", node), "yyyy-MM-dd");
// check if we have season and episode number, if not it must be a special episode
if (episodeNumber == null || seasonNumber == null) {
// handle as special episode
seasonNumber = getTextContent("season", node);
int specialNumber = filterBySeason(specials, Integer.parseInt(seasonNumber)).size() + 1;
specials.add(new Episode(seriesName, seasonNumber, "Special " + specialNumber, title, Integer.toString(specialNumber), airdate));
seasonNumber = getIntegerContent("season", node);
int specialNumber = filterBySeason(specials, seasonNumber).size() + 1;
specials.add(new Episode(seriesName, seasonNumber, null, title, specialNumber, airdate));
} else {
// handle as normal episode
episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, title, null, airdate));

View File

@ -133,30 +133,29 @@ public class TheTVDBClient implements EpisodeListProvider {
for (Node node : nodes) {
String episodeName = getTextContent("EpisodeName", node);
String episodeNumber = getTextContent("EpisodeNumber", node);
String seasonNumber = getTextContent("SeasonNumber", node);
Integer episodeNumber = getIntegerContent("EpisodeNumber", node);
Integer seasonNumber = getIntegerContent("SeasonNumber", node);
Date airdate = Date.parse(getTextContent("FirstAired", node), "yyyy-MM-dd");
if (seasonNumber.equals("0")) {
if (seasonNumber == 0) {
// handle as special episode
String airsBefore = getTextContent("airsbefore_season", node);
if (airsBefore != null && airsBefore.matches("\\d+")) {
Integer airsBefore = getIntegerContent("airsbefore_season", node);
if (airsBefore != null) {
seasonNumber = airsBefore;
}
int specialNumber = filterBySeason(specials, Integer.parseInt(seasonNumber)).size() + 1;
specials.add(new Episode(seriesName, seasonNumber, "Special " + specialNumber, episodeName, Integer.toString(specialNumber), airdate));
Integer specialNumber = filterBySeason(specials, seasonNumber).size() + 1;
specials.add(new Episode(seriesName, seasonNumber, null, episodeName, specialNumber, airdate));
} else {
// handle as normal episode
episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, episodeName, null, airdate));
}
if (episodeNumber.equals("1")) {
if (episodeNumber == 1) {
try {
// cache seasonId for each season (always when we are at the first episode)
// because it might be required by getEpisodeListLink
cache.putSeasonId(searchResult.getSeriesId(), Integer.parseInt(seasonNumber), Integer.parseInt(getTextContent("seasonid", node)));
cache.putSeasonId(searchResult.getSeriesId(), seasonNumber, getIntegerContent("seasonid", node));
} catch (NumberFormatException e) {
// season/episode is not a number, just ignore
}

View File

@ -112,6 +112,15 @@ public final class XPathUtilities {
}
public static Integer getIntegerContent(String childName, Node parentNode) {
try {
return new Integer(getTextContent(childName, parentNode));
} catch (NumberFormatException e) {
return null;
}
}
private static XPathExpression getXPath(String xpath) throws XPathExpressionException {
return XPathFactory.newInstance().newXPath().compile(xpath);
}

View File

@ -83,7 +83,7 @@ public class AnidbClientTest {
assertEquals("Monster", first.getSeriesName());
assertEquals("Herr Dr. Tenma", first.getTitle());
assertEquals("1", first.getEpisode());
assertEquals("1", first.getEpisode().toString());
assertEquals(null, first.getSeason());
assertEquals("2004-04-07", first.airdate().toString());
}
@ -99,7 +99,7 @@ public class AnidbClientTest {
assertEquals("Juuni Kokuki", first.getSeriesName());
assertEquals("Shadow of the Moon, The Sea of Shadow - Chapter 1", first.getTitle());
assertEquals("1", first.getEpisode());
assertEquals("1", first.getEpisode().toString());
assertEquals(null, first.getSeason());
assertEquals("2002-04-09", first.airdate().toString());
}

View File

@ -72,8 +72,8 @@ public class IMDbClientTest {
assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
assertEquals("Unaired Pilot", first.getTitle());
assertEquals("0", first.getEpisode());
assertEquals("1", first.getSeason());
assertEquals("0", first.getEpisode().toString());
assertEquals("1", first.getSeason().toString());
assertEquals(null, first.airdate());
Episode last = list.get(144);
@ -96,8 +96,8 @@ public class IMDbClientTest {
assertEquals("Mushishi", first.getSeriesName());
assertEquals("Midori no za", first.getTitle());
assertEquals("1", first.getEpisode());
assertEquals("1", first.getSeason());
assertEquals("1", first.getEpisode().toString());
assertEquals("1", first.getSeason().toString());
}

View File

@ -56,8 +56,8 @@ public class TVDotComClientTest {
assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName());
assertEquals("Chosen", chosen.getTitle());
assertEquals("22", chosen.getEpisode());
assertEquals("7", chosen.getSeason());
assertEquals("22", chosen.getEpisode().toString());
assertEquals("7", chosen.getSeason().toString());
assertEquals("2003-05-20", chosen.airdate().toString());
}
@ -73,8 +73,8 @@ public class TVDotComClientTest {
assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
assertEquals("Welcome to the Hellmouth (1)", first.getTitle());
assertEquals("1", first.getEpisode());
assertEquals("1", first.getSeason());
assertEquals("1", first.getEpisode().toString());
assertEquals("1", first.getSeason().toString());
assertEquals("1997-03-10", first.airdate().toString());
}
@ -90,8 +90,8 @@ public class TVDotComClientTest {
assertEquals("Firefly", fourth.getSeriesName());
assertEquals("Jaynestown", fourth.getTitle());
assertEquals("4", fourth.getEpisode());
assertEquals("1", fourth.getSeason());
assertEquals("4", fourth.getEpisode().toString());
assertEquals("1", fourth.getSeason().toString());
assertEquals("2002-10-18", fourth.airdate().toString());
}
@ -114,8 +114,8 @@ public class TVDotComClientTest {
assertEquals("Lost", episode.getSeriesName());
assertEquals("Exposé", episode.getTitle());
assertEquals("14", episode.getEpisode());
assertEquals("3", episode.getSeason());
assertEquals("14", episode.getEpisode().toString());
assertEquals("3", episode.getSeason().toString());
assertEquals("2007-03-28", episode.airdate().toString());
}

View File

@ -44,8 +44,8 @@ public class TVRageClientTest {
assertEquals("Buffy the Vampire Slayer", chosen.getSeriesName());
assertEquals("Chosen", chosen.getTitle());
assertEquals("22", chosen.getEpisode());
assertEquals("7", chosen.getSeason());
assertEquals("22", chosen.getEpisode().toString());
assertEquals("7", chosen.getSeason().toString());
assertEquals("2003-05-20", chosen.airdate().toString());
}
@ -60,8 +60,8 @@ public class TVRageClientTest {
assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
assertEquals("Unaired Pilot", first.getTitle());
assertEquals("00", first.getEpisode());
assertEquals("0", first.getSeason());
assertEquals("0", first.getEpisode().toString());
assertEquals("0", first.getSeason().toString());
assertEquals(null, first.airdate());
}

View File

@ -64,8 +64,8 @@ public class TheTVDBClientTest {
Episode first = list.get(0);
assertEquals("Buffy the Vampire Slayer", first.getSeriesName());
assertEquals("Welcome to the Hellmouth (1)", first.getTitle());
assertEquals("1", first.getEpisode());
assertEquals("1", first.getSeason());
assertEquals("1", first.getEpisode().toString());
assertEquals("1", first.getSeason().toString());
assertEquals("1997-03-10", first.airdate().toString());
// check special episode
@ -88,8 +88,8 @@ public class TheTVDBClientTest {
assertEquals("Wonderfalls", first.getSeriesName());
assertEquals("Wax Lion", first.getTitle());
assertEquals("1", first.getEpisode());
assertEquals("1", first.getSeason());
assertEquals("1", first.getEpisode().toString());
assertEquals("1", first.getSeason().toString());
assertEquals("2004-03-12", first.airdate().toString());
}

View File

@ -14,6 +14,7 @@ import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import net.sourceforge.filebot.web.Date;
import net.sourceforge.filebot.web.Episode;
import net.sourceforge.tuned.PreferencesMap.SerializableAdapter;
import net.sourceforge.tuned.PreferencesMap.SimpleAdapter;
@ -167,8 +168,7 @@ public class PreferencesMapTest {
@Test
public void serializableAdapter() {
Map<String, Episode> map = PreferencesMap.map(temp, new SerializableAdapter<Episode>());
Episode episode = new Episode("8 Simple Rules", "1", "1", "Pilot", null, null);
Episode episode = new Episode("Dark Angel", 3, 1, "Labyrinth", null, new Date(2009, 6, 1));
map.put("episode", episode);