diff --git a/source/net/sourceforge/filebot/format/EpisodeBindingBean.java b/source/net/sourceforge/filebot/format/EpisodeBindingBean.java index 9d521d03..9092c15c 100644 --- a/source/net/sourceforge/filebot/format/EpisodeBindingBean.java +++ b/source/net/sourceforge/filebot/format/EpisodeBindingBean.java @@ -19,6 +19,7 @@ import net.sf.ehcache.Element; import net.sourceforge.filebot.hash.HashType; import net.sourceforge.filebot.mediainfo.MediaInfo; import net.sourceforge.filebot.mediainfo.MediaInfo.StreamKind; +import net.sourceforge.filebot.web.Date; import net.sourceforge.filebot.web.Episode; import net.sourceforge.tuned.FileUtilities; @@ -69,6 +70,12 @@ public class EpisodeBindingBean { } + @Define("air") + public Date airdate() { + return episode.airdate(); + } + + @Define("vc") public String getVideoCodec() { // e.g. XviD, x264, DivX 5, MPEG-4 Visual, AVC, etc. diff --git a/source/net/sourceforge/filebot/ui/panel/rename/EpisodeFormatDialog.java b/source/net/sourceforge/filebot/ui/panel/rename/EpisodeFormatDialog.java index 50f170c2..09e71f5b 100644 --- a/source/net/sourceforge/filebot/ui/panel/rename/EpisodeFormatDialog.java +++ b/source/net/sourceforge/filebot/ui/panel/rename/EpisodeFormatDialog.java @@ -54,6 +54,7 @@ import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.Settings; import net.sourceforge.filebot.format.EpisodeBindingBean; import net.sourceforge.filebot.format.ExpressionFormat; +import net.sourceforge.filebot.web.Date; import net.sourceforge.filebot.web.Episode; import net.sourceforge.filebot.web.EpisodeFormat; import net.sourceforge.tuned.DefaultThreadFactory; @@ -290,7 +291,7 @@ class EpisodeFormatDialog extends JDialog { episode = EpisodeFormat.getInstance().parseObject(persistentSampleEpisode.getValue()); } catch (Exception e) { // default sample - episode = new Episode("Dark Angel", 3, 1, "Labyrinth"); + episode = new Episode("Dark Angel", "3", "1", "Labyrinth", null, new Date(2009, 6, 1)); } // restore media file diff --git a/source/net/sourceforge/filebot/web/AnidbClient.java b/source/net/sourceforge/filebot/web/AnidbClient.java index 5e0fc1ac..3f2d0cfc 100644 --- a/source/net/sourceforge/filebot/web/AnidbClient.java +++ b/source/net/sourceforge/filebot/web/AnidbClient.java @@ -146,9 +146,10 @@ public class AnidbClient implements EpisodeListProvider { // ignore special episodes if (number != null && number.matches("\\d+")) { String title = selectString(".//title[@lang='en']", node); + String airdate = selectString(".//date/@rel", node); // no seasons for anime - episodes.add(new Episode(animeTitle, null, number, title)); + episodes.add(new Episode(animeTitle, null, number, title, null, Date.parse(airdate, "yyyy-MM-dd"))); } } diff --git a/source/net/sourceforge/filebot/web/Date.java b/source/net/sourceforge/filebot/web/Date.java new file mode 100644 index 00000000..fd29a4cc --- /dev/null +++ b/source/net/sourceforge/filebot/web/Date.java @@ -0,0 +1,77 @@ + +package net.sourceforge.filebot.web; + + +import static java.util.Calendar.*; + +import java.io.Serializable; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.logging.Level; +import java.util.logging.Logger; + + +public class Date implements Serializable { + + private int year; + private int month; + private int day; + + + protected Date() { + // used by serializer + } + + + public Date(int year, int month, int day) { + this.year = year; + this.month = month; + this.day = day; + } + + + public int getYear() { + return year; + } + + + public int getMonth() { + return month; + } + + + public int getDay() { + return day; + } + + + @Override + public String toString() { + return String.format("%04d-%02d-%02d", year, month, day); + } + + + public String format(String pattern) { + return new SimpleDateFormat(pattern).format(new GregorianCalendar(year, month, day).getTime()); + } + + + public static Date parse(String string, String pattern) { + SimpleDateFormat formatter = new SimpleDateFormat(pattern, Locale.ROOT); + formatter.setLenient(false); // enable strict mode (e.g. fail on invalid dates like 0000-00-00) + + try { + Calendar date = new GregorianCalendar(Locale.ROOT); + date.setTime(formatter.parse(string)); + return new Date(date.get(YEAR), date.get(MONTH) + 1, date.get(DAY_OF_MONTH)); + } catch (ParseException e) { + // no result if date is invalid + Logger.getLogger(Date.class.getName()).log(Level.WARNING, e.getMessage()); + return null; + } + } + +} diff --git a/source/net/sourceforge/filebot/web/Episode.java b/source/net/sourceforge/filebot/web/Episode.java index db628208..1edb4d66 100644 --- a/source/net/sourceforge/filebot/web/Episode.java +++ b/source/net/sourceforge/filebot/web/Episode.java @@ -12,30 +12,36 @@ public class Episode implements Serializable { private String season; private String episode; private String title; + + // special number private String special; + // episode airdate + private Date airdate; + protected Episode() { // used by serializer } - public Episode(String seriesName, int season, int episode, String title) { - this(seriesName, String.valueOf(season), String.valueOf(episode), title, null); - } - - public Episode(String seriesName, String season, String episode, String title) { - this(seriesName, season, episode, title, null); + this(seriesName, season, episode, title, null, null); } - public Episode(String seriesName, String season, String episode, String title, String special) { + public Episode(String seriesName, String season, String episode, String title, String special, Date airdate) { this.seriesName = seriesName; this.season = season; this.episode = episode; this.title = title; this.special = special; + this.airdate = airdate; + } + + + public String getSeriesName() { + return seriesName; } @@ -67,6 +73,11 @@ public class Episode implements Serializable { } + public String getTitle() { + return title; + } + + public String getSpecial() { return special; } @@ -81,13 +92,8 @@ public class Episode implements Serializable { } - public String getSeriesName() { - return seriesName; - } - - - public String getTitle() { - return title; + public Date airdate() { + return airdate; } diff --git a/source/net/sourceforge/filebot/web/IMDbClient.java b/source/net/sourceforge/filebot/web/IMDbClient.java index 19b00c68..8a1ffad2 100644 --- a/source/net/sourceforge/filebot/web/IMDbClient.java +++ b/source/net/sourceforge/filebot/web/IMDbClient.java @@ -97,13 +97,16 @@ public class IMDbClient implements EpisodeListProvider { List episodes = new ArrayList(nodes.size()); for (Node node : nodes) { - String title = node.getTextContent().trim(); + String title = getTextContent(node); Scanner numberScanner = new Scanner(node.getPreviousSibling().getTextContent()).useDelimiter("\\D+"); String season = numberScanner.next(); String episode = numberScanner.next(); - episodes.add(new Episode(seriesName, season, episode, title)); + // e.g. 20 May 2003 + String airdate = selectString("./following::STRONG", node); + + episodes.add(new Episode(seriesName, season, episode, title, null, Date.parse(airdate, "dd MMMMM yyyyy"))); } return episodes; diff --git a/source/net/sourceforge/filebot/web/TVDotComClient.java b/source/net/sourceforge/filebot/web/TVDotComClient.java index e92b17e1..54fca3a6 100644 --- a/source/net/sourceforge/filebot/web/TVDotComClient.java +++ b/source/net/sourceforge/filebot/web/TVDotComClient.java @@ -148,8 +148,9 @@ public class TVDotComClient implements EpisodeListProvider { List 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 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})"); List episodes = new ArrayList(nodes.size()); @@ -159,9 +160,11 @@ public class TVDotComClient implements EpisodeListProvider { String season = null; String episode = null; + Date airdate = null; Matcher matcher; + // try to match episode information if ((matcher = episodePattern.matcher(meta)).find()) { // matches episode season = matcher.group(1); @@ -170,12 +173,17 @@ public class TVDotComClient implements EpisodeListProvider { // matches special season = matcher.group(1); episode = "Special"; - } else { - // no episode match - continue; } - episodes.add(new Episode(searchResult.getName(), season, episode, title)); + // try to match airdate information + if ((matcher = airdatePattern.matcher(meta)).find()) { + airdate = Date.parse(matcher.group(), "MM/dd/yyyy"); // e.g. 5/20/2003 + } + + // add episode if SxE info has been found + if (season != null && episode != null) { + episodes.add(new Episode(searchResult.getName(), season, episode, title, null, airdate)); + } } // episodes are listed in reverse order diff --git a/source/net/sourceforge/filebot/web/TVRageClient.java b/source/net/sourceforge/filebot/web/TVRageClient.java index a4abc908..ba3dd46a 100644 --- a/source/net/sourceforge/filebot/web/TVRageClient.java +++ b/source/net/sourceforge/filebot/web/TVRageClient.java @@ -87,16 +87,17 @@ public class TVRageClient implements EpisodeListProvider { String title = getTextContent("title", node); String episodeNumber = getTextContent("seasonnum", node); String seasonNumber = getAttribute("no", node.getParentNode()); + 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))); + specials.add(new Episode(seriesName, seasonNumber, "Special " + specialNumber, title, Integer.toString(specialNumber), airdate)); } else { // handle as normal episode - episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, title)); + episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, title, null, airdate)); } } diff --git a/source/net/sourceforge/filebot/web/TheTVDBClient.java b/source/net/sourceforge/filebot/web/TheTVDBClient.java index d5c66215..9e9504f1 100644 --- a/source/net/sourceforge/filebot/web/TheTVDBClient.java +++ b/source/net/sourceforge/filebot/web/TheTVDBClient.java @@ -135,6 +135,7 @@ public class TheTVDBClient implements EpisodeListProvider { String episodeName = getTextContent("EpisodeName", node); String episodeNumber = getTextContent("EpisodeNumber", node); String seasonNumber = getTextContent("SeasonNumber", node); + Date airdate = Date.parse(getTextContent("FirstAired", node), "yyyy-MM-dd"); if (seasonNumber.equals("0")) { // handle as special episode @@ -145,10 +146,10 @@ public class TheTVDBClient implements EpisodeListProvider { } int specialNumber = filterBySeason(specials, Integer.parseInt(seasonNumber)).size() + 1; - specials.add(new Episode(seriesName, seasonNumber, "Special " + specialNumber, episodeName, Integer.toString(specialNumber))); + specials.add(new Episode(seriesName, seasonNumber, "Special " + specialNumber, episodeName, Integer.toString(specialNumber), airdate)); } else { // handle as normal episode - episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, episodeName)); + episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, episodeName, null, airdate)); } if (episodeNumber.equals("1")) { diff --git a/test/net/sourceforge/filebot/web/AnidbClientTest.java b/test/net/sourceforge/filebot/web/AnidbClientTest.java index 7ec8b96d..08b22bc1 100644 --- a/test/net/sourceforge/filebot/web/AnidbClientTest.java +++ b/test/net/sourceforge/filebot/web/AnidbClientTest.java @@ -85,6 +85,7 @@ public class AnidbClientTest { assertEquals("Herr Dr. Tenma", first.getTitle()); assertEquals("1", first.getEpisode()); assertEquals(null, first.getSeason()); + assertEquals("2004-04-07", first.airdate().toString()); } @@ -100,6 +101,7 @@ public class AnidbClientTest { assertEquals("Shadow of the Moon, The Sea of Shadow - Chapter 1", first.getTitle()); assertEquals("1", first.getEpisode()); assertEquals(null, first.getSeason()); + assertEquals("2002-04-09", first.airdate().toString()); } diff --git a/test/net/sourceforge/filebot/web/IMDbClientTest.java b/test/net/sourceforge/filebot/web/IMDbClientTest.java index ea80e224..83d885d5 100644 --- a/test/net/sourceforge/filebot/web/IMDbClientTest.java +++ b/test/net/sourceforge/filebot/web/IMDbClientTest.java @@ -74,6 +74,7 @@ public class IMDbClientTest { assertEquals("Unaired Pilot", first.getTitle()); assertEquals("0", first.getEpisode()); assertEquals("1", first.getSeason()); + assertEquals(null, first.airdate()); Episode last = list.get(144); @@ -81,6 +82,7 @@ public class IMDbClientTest { assertEquals("Chosen", last.getTitle()); assertEquals("22", last.getEpisode()); assertEquals("7", last.getSeason()); + assertEquals("2003-05-20", last.airdate().toString()); } @@ -92,7 +94,7 @@ public class IMDbClientTest { Episode first = list.get(0); - assertEquals("Mushi-Shi", first.getSeriesName()); + assertEquals("Mushishi", first.getSeriesName()); assertEquals("Midori no za", first.getTitle()); assertEquals("1", first.getEpisode()); assertEquals("1", first.getSeason()); diff --git a/test/net/sourceforge/filebot/web/TVDotComClientTest.java b/test/net/sourceforge/filebot/web/TVDotComClientTest.java index 19f3b7a9..97e4eb5b 100644 --- a/test/net/sourceforge/filebot/web/TVDotComClientTest.java +++ b/test/net/sourceforge/filebot/web/TVDotComClientTest.java @@ -58,6 +58,7 @@ public class TVDotComClientTest { assertEquals("Chosen", chosen.getTitle()); assertEquals("22", chosen.getEpisode()); assertEquals("7", chosen.getSeason()); + assertEquals("2003-05-20", chosen.airdate().toString()); } @@ -74,6 +75,7 @@ public class TVDotComClientTest { assertEquals("Welcome to the Hellmouth (1)", first.getTitle()); assertEquals("1", first.getEpisode()); assertEquals("1", first.getSeason()); + assertEquals("1997-03-10", first.airdate().toString()); } @@ -90,6 +92,7 @@ public class TVDotComClientTest { assertEquals("Jaynestown", fourth.getTitle()); assertEquals("4", fourth.getEpisode()); assertEquals("1", fourth.getSeason()); + assertEquals("2002-10-18", fourth.airdate().toString()); } @@ -113,6 +116,7 @@ public class TVDotComClientTest { assertEquals("Exposé", episode.getTitle()); assertEquals("14", episode.getEpisode()); assertEquals("3", episode.getSeason()); + assertEquals("2007-03-28", episode.airdate().toString()); } diff --git a/test/net/sourceforge/filebot/web/TVRageClientTest.java b/test/net/sourceforge/filebot/web/TVRageClientTest.java index 5da833db..e9628b10 100644 --- a/test/net/sourceforge/filebot/web/TVRageClientTest.java +++ b/test/net/sourceforge/filebot/web/TVRageClientTest.java @@ -6,10 +6,10 @@ import static org.junit.Assert.*; import java.util.List; -import net.sourceforge.filebot.web.TVRageClient.TVRageSearchResult; - import org.junit.Test; +import net.sourceforge.filebot.web.TVRageClient.TVRageSearchResult; + public class TVRageClientTest { @@ -18,7 +18,7 @@ public class TVRageClientTest { */ private static TVRageSearchResult buffySearchResult = new TVRageSearchResult("Buffy the Vampire Slayer", 2930, "http://www.tvrage.com/Buffy_The_Vampire_Slayer"); - + @Test public void search() throws Exception { List results = tvrage.search("Buffy"); @@ -30,9 +30,10 @@ public class TVRageClientTest { assertEquals(buffySearchResult.getLink(), result.getLink()); } + private TVRageClient tvrage = new TVRageClient(); - + @Test public void getEpisodeList() throws Exception { List list = tvrage.getEpisodeList(buffySearchResult, 7); @@ -45,6 +46,7 @@ public class TVRageClientTest { assertEquals("Chosen", chosen.getTitle()); assertEquals("22", chosen.getEpisode()); assertEquals("7", chosen.getSeason()); + assertEquals("2003-05-20", chosen.airdate().toString()); } @@ -60,6 +62,7 @@ public class TVRageClientTest { assertEquals("Unaired Pilot", first.getTitle()); assertEquals("00", first.getEpisode()); assertEquals("0", first.getSeason()); + assertEquals(null, first.airdate()); } diff --git a/test/net/sourceforge/filebot/web/TheTVDBClientTest.java b/test/net/sourceforge/filebot/web/TheTVDBClientTest.java index 08be7243..7afc4463 100644 --- a/test/net/sourceforge/filebot/web/TheTVDBClientTest.java +++ b/test/net/sourceforge/filebot/web/TheTVDBClientTest.java @@ -66,6 +66,7 @@ public class TheTVDBClientTest { assertEquals("Welcome to the Hellmouth (1)", first.getTitle()); assertEquals("1", first.getEpisode()); assertEquals("1", first.getSeason()); + assertEquals("1997-03-10", first.airdate().toString()); // check special episode Episode last = list.get(list.size() - 1); @@ -73,6 +74,7 @@ public class TheTVDBClientTest { assertEquals("Unaired Pilot", last.getTitle()); assertEquals("Special 1", last.getEpisode()); assertEquals("1", last.getSeason()); + assertEquals(null, last.airdate()); } @@ -82,12 +84,13 @@ public class TheTVDBClientTest { assertEquals(13, list.size()); - Episode chosen = list.get(0); + Episode first = list.get(0); - assertEquals("Wonderfalls", chosen.getSeriesName()); - assertEquals("Wax Lion", chosen.getTitle()); - assertEquals("1", chosen.getEpisode()); - assertEquals("1", chosen.getSeason()); + assertEquals("Wonderfalls", first.getSeriesName()); + assertEquals("Wax Lion", first.getTitle()); + assertEquals("1", first.getEpisode()); + assertEquals("1", first.getSeason()); + assertEquals("2004-03-12", first.airdate().toString()); } diff --git a/test/net/sourceforge/tuned/PreferencesMapTest.java b/test/net/sourceforge/tuned/PreferencesMapTest.java index 23d6303f..4ecf7428 100644 --- a/test/net/sourceforge/tuned/PreferencesMapTest.java +++ b/test/net/sourceforge/tuned/PreferencesMapTest.java @@ -168,7 +168,7 @@ public class PreferencesMapTest { public void serializableAdapter() { Map map = PreferencesMap.map(temp, new SerializableAdapter()); - Episode episode = new Episode("8 Simple Rules", 1, 1, "Pilot"); + Episode episode = new Episode("8 Simple Rules", "1", "1", "Pilot", null, null); map.put("episode", episode);