* add support for absolute episode numbers alongside SxE
-> add absolute episode number support to TVRage and TheTVDB -> check against absolute episode number when matching files and episodes -> add naming scheme binding for absolute episode number
This commit is contained in:
parent
78d2a3c44a
commit
13a1b3fa6a
|
@ -70,12 +70,24 @@ public class EpisodeBindingBean {
|
|||
}
|
||||
|
||||
|
||||
@Define("air")
|
||||
@Define("airdate")
|
||||
public Date airdate() {
|
||||
return episode.airdate();
|
||||
}
|
||||
|
||||
|
||||
@Define("absolute")
|
||||
public Integer getAbsoluteEpisodeNumber() {
|
||||
return episode.getAbsolute();
|
||||
}
|
||||
|
||||
|
||||
@Define("special")
|
||||
public Integer getSpecialNumber() {
|
||||
return episode.getSpecial();
|
||||
}
|
||||
|
||||
|
||||
@Define("vc")
|
||||
public String getVideoCodec() {
|
||||
// e.g. XviD, x264, DivX 5, MPEG-4 Visual, AVC, etc.
|
||||
|
|
|
@ -35,10 +35,10 @@ public class SeasonEpisodeMatcher {
|
|||
SxE seasonEpisode = new SxE(match.group(1), match.group(2));
|
||||
|
||||
// interpret match as episode number only
|
||||
SxE episodeOnly = new SxE(null, match.group(1) + match.group(2));
|
||||
SxE absoluteEpisode = new SxE(null, match.group(1) + match.group(2));
|
||||
|
||||
// return both matches, unless they are one and the same
|
||||
return seasonEpisode.equals(episodeOnly) ? Collections.singleton(episodeOnly) : Arrays.asList(seasonEpisode, episodeOnly);
|
||||
return seasonEpisode.equals(absoluteEpisode) ? Collections.singleton(absoluteEpisode) : Arrays.asList(seasonEpisode, absoluteEpisode);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -27,12 +27,12 @@ public class SeasonEpisodeMetric implements SimilarityMetric {
|
|||
|
||||
for (SxE sxe1 : sxeVector1) {
|
||||
for (SxE sxe2 : sxeVector2) {
|
||||
if (sxe1.episode == sxe2.episode && sxe1.season == sxe2.season) {
|
||||
if (sxe1.season == sxe2.season && sxe1.episode == sxe2.episode) {
|
||||
// vectors have at least one perfect episode match in common
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sxe1.episode == sxe2.episode || sxe1.season == sxe2.season) {
|
||||
if (sxe1.season == sxe2.season || sxe1.episode == sxe2.episode) {
|
||||
// at least we have a partial match
|
||||
similarity = 0.5f;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@ expr[a1]: n
|
|||
expr[a2]: s
|
||||
expr[a3]: e
|
||||
expr[a4]: t
|
||||
expr[a5]: air
|
||||
expr[a6]: episode
|
||||
expr[a5]: airdate
|
||||
expr[a6]: absolute
|
||||
expr[a7]: special
|
||||
expr[a8]: episode
|
||||
|
||||
# simple mediainfo expressions
|
||||
expr[b1]: vc
|
||||
|
|
|
@ -291,7 +291,7 @@ class EpisodeFormatDialog extends JDialog {
|
|||
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", 42, null, new Date(2009, 6, 1));
|
||||
}
|
||||
|
||||
// restore media file
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
package net.sourceforge.filebot.ui.panel.rename;
|
||||
|
||||
|
||||
import static java.util.Collections.*;
|
||||
import static net.sourceforge.filebot.hash.VerificationUtilities.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.sourceforge.filebot.similarity.DateMetric;
|
||||
import net.sourceforge.filebot.similarity.FileSizeMetric;
|
||||
|
@ -27,7 +28,7 @@ public enum MatchSimilarityMetric implements SimilarityMetric {
|
|||
|
||||
@Override
|
||||
public float getSimilarity(Object o1, Object o2) {
|
||||
// order of arguments is logically irrelevant, but we might be able to save us a call to File.length() this way
|
||||
// order of arguments is logically irrelevant, but we might be able to save us a call to File.length() which is quite costly
|
||||
return o1 instanceof File ? super.getSimilarity(o2, o1) : super.getSimilarity(o1, o2);
|
||||
}
|
||||
|
||||
|
@ -47,7 +48,13 @@ public enum MatchSimilarityMetric implements SimilarityMetric {
|
|||
|
||||
@Override
|
||||
public float getSimilarity(Object o1, Object o2) {
|
||||
return Math.max(SeasonEpisode.getSimilarity(o1, o2), AirDate.getSimilarity(o1, o2));
|
||||
float sxeSimilarity = SeasonEpisode.getSimilarity(o1, o2);
|
||||
|
||||
// break if SxE is a perfect match already
|
||||
if (sxeSimilarity >= 1)
|
||||
return sxeSimilarity;
|
||||
|
||||
return Math.max(sxeSimilarity, AirDate.getSimilarity(o1, o2));
|
||||
}
|
||||
|
||||
}),
|
||||
|
@ -60,8 +67,11 @@ public enum MatchSimilarityMetric implements SimilarityMetric {
|
|||
if (object instanceof Episode) {
|
||||
Episode episode = (Episode) object;
|
||||
|
||||
// create SxE from episode
|
||||
return singleton(new SxE(episode.getSeason(), episode.getEpisode()));
|
||||
// get SxE from episode, both SxE for season/episode numbering and SxE for absolute episode numbering
|
||||
SxE seasonEpisode = new SxE(episode.getSeason(), episode.getEpisode());
|
||||
SxE absoluteEpisode = new SxE(null, episode.getAbsolute());
|
||||
|
||||
return seasonEpisode.equals(absoluteEpisode) ? Collections.singleton(absoluteEpisode) : Arrays.asList(seasonEpisode, absoluteEpisode);
|
||||
}
|
||||
|
||||
return super.parse(object);
|
||||
|
|
|
@ -149,7 +149,7 @@ public class AnidbClient implements EpisodeListProvider {
|
|||
String airdate = selectString(".//date/@rel", node);
|
||||
|
||||
// no seasons for anime
|
||||
episodes.add(new Episode(animeTitle, null, number, title, null, Date.parse(airdate, "yyyy-MM-dd")));
|
||||
episodes.add(new Episode(animeTitle, null, number, title, number, null, Date.parse(airdate, "yyyy-MM-dd")));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ public class Episode implements Serializable {
|
|||
private Integer episode;
|
||||
private String title;
|
||||
|
||||
// absolute episode number
|
||||
private Integer absolute;
|
||||
|
||||
// special number
|
||||
private Integer special;
|
||||
|
||||
|
@ -26,15 +29,16 @@ public class Episode implements Serializable {
|
|||
|
||||
|
||||
public Episode(String seriesName, Integer season, Integer episode, String title) {
|
||||
this(seriesName, season, episode, title, null, null);
|
||||
this(seriesName, season, episode, title, null, null, null);
|
||||
}
|
||||
|
||||
|
||||
public Episode(String seriesName, Integer season, Integer episode, String title, Integer special, Date airdate) {
|
||||
public Episode(String seriesName, Integer season, Integer episode, String title, Integer absolute, Integer special, Date airdate) {
|
||||
this.seriesName = seriesName;
|
||||
this.season = season;
|
||||
this.episode = episode;
|
||||
this.title = title;
|
||||
this.absolute = absolute;
|
||||
this.special = special;
|
||||
this.airdate = airdate;
|
||||
}
|
||||
|
@ -60,6 +64,11 @@ public class Episode implements Serializable {
|
|||
}
|
||||
|
||||
|
||||
public Integer getAbsolute() {
|
||||
return absolute;
|
||||
}
|
||||
|
||||
|
||||
public Integer getSpecial() {
|
||||
return special;
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ public class EpisodeFormat extends Format {
|
|||
}
|
||||
|
||||
if ((m = sxePattern.matcher(source)).find()) {
|
||||
season = new Integer(m.group(1));
|
||||
season = (m.group(1) == null) ? null : new Integer(m.group(1));
|
||||
if (m.group(2) == null)
|
||||
episode = new Integer(m.group(3));
|
||||
else
|
||||
|
@ -102,7 +102,7 @@ public class EpisodeFormat extends Format {
|
|||
|
||||
// did parse input
|
||||
pos.setIndex(source.length());
|
||||
return new Episode(name, season, episode, title, special, airdate);
|
||||
return new Episode(name, season, episode, title, season == null ? episode : null, special, airdate);
|
||||
}
|
||||
|
||||
// failed to parse input
|
||||
|
|
|
@ -106,7 +106,7 @@ public class IMDbClient implements EpisodeListProvider {
|
|||
// 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")));
|
||||
episodes.add(new Episode(seriesName, season, episode, title, null, null, Date.parse(airdate, "dd MMMMM yyyyy")));
|
||||
}
|
||||
|
||||
return episodes;
|
||||
|
|
|
@ -177,7 +177,7 @@ public class TVDotComClient implements EpisodeListProvider {
|
|||
|
||||
// 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.add(new Episode(searchResult.getName(), season, episode, title, null, null, airdate));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,6 +86,7 @@ public class TVRageClient implements EpisodeListProvider {
|
|||
for (Node node : selectNodes("//episode", dom)) {
|
||||
String title = getTextContent("title", node);
|
||||
Integer episodeNumber = getIntegerContent("seasonnum", node);
|
||||
Integer absoluteNumber = getIntegerContent("epnum", 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");
|
||||
|
@ -95,10 +96,10 @@ public class TVRageClient implements EpisodeListProvider {
|
|||
// handle as special episode
|
||||
seasonNumber = getIntegerContent("season", node);
|
||||
int specialNumber = filterBySeason(specials, seasonNumber).size() + 1;
|
||||
specials.add(new Episode(seriesName, seasonNumber, null, title, specialNumber, airdate));
|
||||
specials.add(new Episode(seriesName, seasonNumber, null, title, null, specialNumber, airdate));
|
||||
} else {
|
||||
// handle as normal episode
|
||||
episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, title, null, airdate));
|
||||
episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, title, absoluteNumber, null, airdate));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,13 @@ public class TheTVDBClient implements EpisodeListProvider {
|
|||
|
||||
|
||||
public List<SearchResult> search(String query, Locale language) throws Exception {
|
||||
// check if the exact series name is already cached
|
||||
Integer cachedResult = cache.getSeriesId(query, language);
|
||||
|
||||
if (cachedResult != null)
|
||||
return Arrays.asList(new SearchResult[] { new TheTVDBSearchResult(query, cachedResult) });
|
||||
|
||||
// perform online search
|
||||
URL url = getResource(null, "/api/GetSeries.php?seriesname=" + URLEncoder.encode(query, "UTF-8") + "&language=" + language.getLanguage());
|
||||
Document dom = getDocument(url);
|
||||
|
||||
|
@ -86,10 +93,11 @@ public class TheTVDBClient implements EpisodeListProvider {
|
|||
List<SearchResult> searchResults = new ArrayList<SearchResult>(nodes.size());
|
||||
|
||||
for (Node node : nodes) {
|
||||
int seriesId = Integer.parseInt(getTextContent("seriesid", node));
|
||||
int seriesId = getIntegerContent("seriesid", node);
|
||||
String seriesName = getTextContent("SeriesName", node);
|
||||
|
||||
searchResults.add(new TheTVDBSearchResult(seriesName, seriesId));
|
||||
cache.putSeriesId(seriesName, language, seriesId);
|
||||
}
|
||||
|
||||
return searchResults;
|
||||
|
@ -134,6 +142,7 @@ public class TheTVDBClient implements EpisodeListProvider {
|
|||
for (Node node : nodes) {
|
||||
String episodeName = getTextContent("EpisodeName", node);
|
||||
Integer episodeNumber = getIntegerContent("EpisodeNumber", node);
|
||||
Integer absoluteNumber = getIntegerContent("absolute_number", node);
|
||||
Integer seasonNumber = getIntegerContent("SeasonNumber", node);
|
||||
Date airdate = Date.parse(getTextContent("FirstAired", node), "yyyy-MM-dd");
|
||||
|
||||
|
@ -145,10 +154,10 @@ public class TheTVDBClient implements EpisodeListProvider {
|
|||
}
|
||||
|
||||
Integer specialNumber = filterBySeason(specials, seasonNumber).size() + 1;
|
||||
specials.add(new Episode(seriesName, seasonNumber, null, episodeName, specialNumber, airdate));
|
||||
specials.add(new Episode(seriesName, seasonNumber, null, episodeName, null, specialNumber, airdate));
|
||||
} else {
|
||||
// handle as normal episode
|
||||
episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, episodeName, null, airdate));
|
||||
episodes.add(new Episode(seriesName, seasonNumber, episodeNumber, episodeName, absoluteNumber, null, airdate));
|
||||
}
|
||||
|
||||
if (episodeNumber == 1) {
|
||||
|
@ -338,13 +347,28 @@ public class TheTVDBClient implements EpisodeListProvider {
|
|||
}
|
||||
|
||||
|
||||
public void putSeriesId(String seriesName, Locale language, int seriesId) {
|
||||
cache.put(new Element(key(host, "SeriesId", seriesName, language.getLanguage()), seriesId));
|
||||
}
|
||||
|
||||
|
||||
public Integer getSeriesId(String seriesName, Locale language) {
|
||||
Element element = cache.get(key(host, "SeriesId", seriesName, language.getLanguage()));
|
||||
|
||||
if (element != null)
|
||||
return (Integer) element.getValue();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void putSeasonId(int seriesId, int seasonNumber, int seasonId) {
|
||||
cache.put(new Element(key(host, seriesId, seasonNumber, "SeasonId"), seasonId));
|
||||
cache.put(new Element(key(host, "SeasonId", seriesId, seasonNumber), seasonId));
|
||||
}
|
||||
|
||||
|
||||
public Integer getSeasonId(int seriesId, int seasonNumber) {
|
||||
Element element = cache.get(key(host, seriesId, seasonNumber, "SeasonId"));
|
||||
Element element = cache.get(key(host, "SeasonId", seriesId, seasonNumber));
|
||||
|
||||
if (element != null)
|
||||
return (Integer) element.getValue();
|
||||
|
@ -354,13 +378,13 @@ public class TheTVDBClient implements EpisodeListProvider {
|
|||
|
||||
|
||||
public void putEpisodeList(int seriesId, Locale language, List<Episode> episodes) {
|
||||
cache.put(new Element(key(host, seriesId, language.getLanguage(), "EpisodeList"), episodes));
|
||||
cache.put(new Element(key(host, "EpisodeList", seriesId, language.getLanguage()), episodes));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Episode> getEpisodeList(int seriesId, Locale language) {
|
||||
Element element = cache.get(key(host, seriesId, language.getLanguage(), "EpisodeList"));
|
||||
Element element = cache.get(key(host, "EpisodeList", seriesId, language.getLanguage()));
|
||||
|
||||
if (element != null)
|
||||
return (List<Episode>) element.getValue();
|
||||
|
|
|
@ -84,6 +84,7 @@ public class AnidbClientTest {
|
|||
assertEquals("Monster", first.getSeriesName());
|
||||
assertEquals("Herr Dr. Tenma", first.getTitle());
|
||||
assertEquals("1", first.getEpisode().toString());
|
||||
assertEquals("1", first.getAbsolute().toString());
|
||||
assertEquals(null, first.getSeason());
|
||||
assertEquals("2004-04-07", first.airdate().toString());
|
||||
}
|
||||
|
@ -97,9 +98,10 @@ public class AnidbClientTest {
|
|||
|
||||
Episode first = list.get(0);
|
||||
|
||||
assertEquals("Juuni Kokuki", first.getSeriesName());
|
||||
assertEquals("Juuni Kokki", first.getSeriesName());
|
||||
assertEquals("Shadow of the Moon, The Sea of Shadow - Chapter 1", first.getTitle());
|
||||
assertEquals("1", first.getEpisode().toString());
|
||||
assertEquals("1", first.getAbsolute().toString());
|
||||
assertEquals(null, first.getSeason());
|
||||
assertEquals("2002-04-09", first.airdate().toString());
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public class TVRageClientTest {
|
|||
assertEquals("Chosen", chosen.getTitle());
|
||||
assertEquals("22", chosen.getEpisode().toString());
|
||||
assertEquals("7", chosen.getSeason().toString());
|
||||
assertEquals("144", chosen.getAbsolute().toString());
|
||||
assertEquals("2003-05-20", chosen.airdate().toString());
|
||||
}
|
||||
|
||||
|
@ -62,6 +63,7 @@ public class TVRageClientTest {
|
|||
assertEquals("Unaired Pilot", first.getTitle());
|
||||
assertEquals("0", first.getEpisode().toString());
|
||||
assertEquals("0", first.getSeason().toString());
|
||||
assertEquals("0", first.getAbsolute().toString());
|
||||
assertEquals(null, first.airdate());
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,7 @@ public class TheTVDBClientTest {
|
|||
assertEquals("Welcome to the Hellmouth (1)", first.getTitle());
|
||||
assertEquals("1", first.getEpisode().toString());
|
||||
assertEquals("1", first.getSeason().toString());
|
||||
assertEquals("1", first.getAbsolute().toString());
|
||||
assertEquals("1997-03-10", first.airdate().toString());
|
||||
|
||||
// check special episode
|
||||
|
@ -74,6 +75,7 @@ public class TheTVDBClientTest {
|
|||
assertEquals("Unaired Pilot", last.getTitle());
|
||||
assertEquals("1", last.getSeason().toString());
|
||||
assertEquals(null, last.getEpisode());
|
||||
assertEquals("1", first.getAbsolute().toString());
|
||||
assertEquals("1", last.getSpecial().toString());
|
||||
assertEquals(null, last.airdate());
|
||||
}
|
||||
|
@ -91,6 +93,7 @@ public class TheTVDBClientTest {
|
|||
assertEquals("Wax Lion", first.getTitle());
|
||||
assertEquals("1", first.getEpisode().toString());
|
||||
assertEquals("1", first.getSeason().toString());
|
||||
assertEquals(null, first.getAbsolute()); // should be "1" but data has not yet been entered
|
||||
assertEquals("2004-03-12", first.airdate().toString());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue