+ updated to TheMovieDB API v3

* lots of changes because now imdbid/tmdbid exist equally, but may not be available depending on the circumstances, so so there is lots of workarounds in MediaBindingBean to account for both ids
* updated artwork scripts to use current TMDb class
This commit is contained in:
Reinhard Pointner 2012-07-24 17:44:54 +00:00
parent 7ec109cce1
commit 44cd76bae1
15 changed files with 358 additions and 426 deletions

View File

@ -85,8 +85,7 @@ import net.sourceforge.tuned.FileUtilities.ParentFilter;
public class CmdlineOperations implements CmdlineInterface { public class CmdlineOperations implements CmdlineInterface {
@Override @Override
public List<File> rename(Collection<File> files, RenameAction action, String conflict, String output, String formatExpression, String db, String query, String sortOrder, String filterExpression, String lang, boolean strict) public List<File> rename(Collection<File> files, RenameAction action, String conflict, String output, String formatExpression, String db, String query, String sortOrder, String filterExpression, String lang, boolean strict) throws Exception {
throws Exception {
ExpressionFormat format = (formatExpression != null) ? new ExpressionFormat(formatExpression) : null; ExpressionFormat format = (formatExpression != null) ? new ExpressionFormat(formatExpression) : null;
ExpressionFilter filter = (filterExpression != null) ? new ExpressionFilter(filterExpression) : null; ExpressionFilter filter = (filterExpression != null) ? new ExpressionFilter(filterExpression) : null;
File outputDir = (output != null && output.length() > 0) ? new File(output).getAbsoluteFile() : null; File outputDir = (output != null && output.length() > 0) ? new File(output).getAbsoluteFile() : null;
@ -143,8 +142,7 @@ public class CmdlineOperations implements CmdlineInterface {
} }
public List<File> renameSeries(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFormat format, EpisodeListProvider db, String query, SortOrder sortOrder, public List<File> renameSeries(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFormat format, EpisodeListProvider db, String query, SortOrder sortOrder, ExpressionFilter filter, Locale locale, boolean strict) throws Exception {
ExpressionFilter filter, Locale locale, boolean strict) throws Exception {
CLILogger.config(format("Rename episodes using [%s]", db.getName())); CLILogger.config(format("Rename episodes using [%s]", db.getName()));
List<File> mediaFiles = filter(files, VIDEO_FILES, SUBTITLE_FILES); List<File> mediaFiles = filter(files, VIDEO_FILES, SUBTITLE_FILES);
@ -290,8 +288,7 @@ public class CmdlineOperations implements CmdlineInterface {
} }
public List<File> renameMovie(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFormat format, MovieIdentificationService service, String query, Locale locale, boolean strict) public List<File> renameMovie(Collection<File> files, RenameAction renameAction, ConflictAction conflictAction, File outputDir, ExpressionFormat format, MovieIdentificationService service, String query, Locale locale, boolean strict) throws Exception {
throws Exception {
CLILogger.config(format("Rename movies using [%s]", service.getName())); CLILogger.config(format("Rename movies using [%s]", service.getName()));
// ignore sample files // ignore sample files

View File

@ -146,13 +146,19 @@ public class MediaBindingBean {
@Define("imdbid") @Define("imdbid")
public String getImdbId() { public String getImdbId() throws Exception {
int imdb = getMovie().getImdbId(); int imdbid = getMovie().getImdbId();
if (imdb <= 0) if (imdbid <= 0) {
if (getMovie().getTmdbId() <= 0) {
return null; return null;
}
return String.format("%07d", imdb); // lookup IMDbID for TMDbID
imdbid = WebServices.TMDb.getMovieInfo(getMovie(), null).getImdbId();
}
return String.format("%07d", imdbid);
} }
@ -381,6 +387,12 @@ public class MediaBindingBean {
} }
@Define("collection")
public Object getCollection() {
return getMetaInfo().getProperty("collection");
}
@Define("info") @Define("info")
public synchronized AssociativeScriptObject getMetaInfo() { public synchronized AssociativeScriptObject getMetaInfo() {
if (metaInfo == null) { if (metaInfo == null) {
@ -388,7 +400,7 @@ public class MediaBindingBean {
if (infoObject instanceof Episode) if (infoObject instanceof Episode)
metaInfo = WebServices.TheTVDB.getSeriesInfoByName(((Episode) infoObject).getSeriesName(), Locale.ENGLISH); metaInfo = WebServices.TheTVDB.getSeriesInfoByName(((Episode) infoObject).getSeriesName(), Locale.ENGLISH);
if (infoObject instanceof Movie) if (infoObject instanceof Movie)
metaInfo = WebServices.TMDb.getMovieInfo((Movie) infoObject, Locale.ENGLISH); metaInfo = WebServices.TMDb.getMovieInfo(getMovie(), Locale.ENGLISH);
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to retrieve metadata: " + infoObject, e); throw new RuntimeException("Failed to retrieve metadata: " + infoObject, e);
} }
@ -403,10 +415,13 @@ public class MediaBindingBean {
Object data = null; Object data = null;
try { try {
if (infoObject instanceof Episode) if (infoObject instanceof Episode) {
data = WebServices.IMDb.getImdbApiMovieInfo(new Movie(getEpisode().getSeriesName(), getEpisode().getSeriesStartDate().getYear(), -1)); data = WebServices.IMDb.getImdbApiMovieInfo(new Movie(getEpisode().getSeriesName(), getEpisode().getSeriesStartDate().getYear(), -1, -1));
if (infoObject instanceof Movie) }
data = WebServices.IMDb.getImdbApiMovieInfo(getMovie()); if (infoObject instanceof Movie) {
Movie m = getMovie();
data = WebServices.IMDb.getImdbApiMovieInfo(m.getImdbId() > 0 ? m : new Movie(null, -1, WebServices.TMDb.getMovieInfo(getMovie(), Locale.ENGLISH).getId(), -1));
}
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException("Failed to retrieve metadata: " + infoObject, e); throw new RuntimeException("Failed to retrieve metadata: " + infoObject, e);
} }

View File

@ -226,7 +226,6 @@ public class ReleaseInfo {
return new FileFolderNameFilter(compile(getBundle(getClass().getName()).getString("pattern.file.ignore"))); return new FileFolderNameFilter(compile(getBundle(getClass().getName()).getString("pattern.file.ignore")));
} }
// fetch release group names online and try to update the data every other day // fetch release group names online and try to update the data every other day
protected final CachedResource<String[]> releaseGroupResource = new PatternResource(getBundle(getClass().getName()).getString("url.release-groups")); protected final CachedResource<String[]> releaseGroupResource = new PatternResource(getBundle(getClass().getName()).getString("url.release-groups"));
protected final CachedResource<String[]> queryBlacklistResource = new PatternResource(getBundle(getClass().getName()).getString("url.query-blacklist")); protected final CachedResource<String[]> queryBlacklistResource = new PatternResource(getBundle(getClass().getName()).getString("url.query-blacklist"));
@ -264,7 +263,7 @@ public class ReleaseInfo {
int imdbid = scanner.nextInt(); int imdbid = scanner.nextInt();
String name = scanner.next(); String name = scanner.next();
int year = scanner.nextInt(); int year = scanner.nextInt();
movies.add(new Movie(name, year, imdbid)); movies.add(new Movie(name, year, imdbid, -1));
} }
return movies.toArray(new Movie[0]); return movies.toArray(new Movie[0]);
@ -335,7 +334,6 @@ public class ReleaseInfo {
return patterns; return patterns;
} }
private final Map<Set<Locale>, Map<String, Locale>> languageMapCache = synchronizedMap(new WeakHashMap<Set<Locale>, Map<String, Locale>>(2)); private final Map<Set<Locale>, Map<String, Locale>> languageMapCache = synchronizedMap(new WeakHashMap<Set<Locale>, Map<String, Locale>>(2));

View File

@ -2,4 +2,4 @@
parameter.exclude: ^StreamKind|Count$ parameter.exclude: ^StreamKind|Count$
# preview expressions (keys are tagged so they can be sorted alphabetically) # preview expressions (keys are tagged so they can be sorted alphabetically)
expressions: n,y,s,e,t,airdate,startdate,absolute,special,imdbid,episode,sxe,s00e00,movie,vc,ac,cf,vf,af,resolution,hpi,ws,sdhd,source,group,crc32,fn,ext,file,pi,pn,lang,actors,director,genres,certification,rating,dim,info.runtime,info.status,imdb.rating,imdb.votes,media.title,media.durationString,media.overallBitRateString,video.codecID,video.frameRate,video.displayAspectRatioString,video.height,video.scanType,audio.format,audio.bitRateString,audio.language,text.codecInfo,text.language expressions: n,y,s,e,t,airdate,startdate,absolute,special,imdbid,episode,sxe,s00e00,movie,vc,ac,cf,vf,af,resolution,hpi,ws,sdhd,source,group,crc32,fn,ext,file,pi,pn,lang,actors,director,collection,genres,certification,rating,dim,info.runtime,info.status,imdb.rating,imdb.votes,media.title,media.durationString,media.overallBitRateString,video.codecID,video.frameRate,video.displayAspectRatioString,video.height,video.scanType,audio.format,audio.bitRateString,audio.language,text.codecInfo,text.language

View File

@ -29,7 +29,6 @@ import java.util.regex.Pattern;
import javax.swing.Icon; import javax.swing.Icon;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.web.TMDbClient.Artwork;
import net.sourceforge.filebot.web.TMDbClient.MovieInfo; import net.sourceforge.filebot.web.TMDbClient.MovieInfo;
import net.sourceforge.filebot.web.TMDbClient.MovieInfo.MovieProperty; import net.sourceforge.filebot.web.TMDbClient.MovieInfo.MovieProperty;
import net.sourceforge.filebot.web.TMDbClient.Person; import net.sourceforge.filebot.web.TMDbClient.Person;
@ -85,7 +84,7 @@ public class IMDbClient implements MovieIdentificationService {
String year = node.getNextSibling().getTextContent().replaceAll("[\\p{Punct}\\p{Space}]+", ""); // remove non-number characters String year = node.getNextSibling().getTextContent().replaceAll("[\\p{Punct}\\p{Space}]+", ""); // remove non-number characters
String href = getAttribute("href", node); String href = getAttribute("href", node);
results.add(new Movie(name, Integer.parseInt(year), getImdbId(href))); results.add(new Movie(name, Integer.parseInt(year), getImdbId(href), -1));
} catch (Exception e) { } catch (Exception e) {
// ignore illegal movies (TV Shows, Videos, Video Games, etc) // ignore illegal movies (TV Shows, Videos, Video Games, etc)
} }
@ -139,7 +138,7 @@ public class IMDbClient implements MovieIdentificationService {
} }
} }
return new Movie(name, Pattern.matches("\\d{4}", year) ? Integer.parseInt(year) : -1, getImdbId(url)); return new Movie(name, Pattern.matches("\\d{4}", year) ? Integer.parseInt(year) : -1, getImdbId(url), -1);
} catch (Exception e) { } catch (Exception e) {
// ignore, we probably got redirected to an error page // ignore, we probably got redirected to an error page
return null; return null;
@ -211,11 +210,11 @@ public class IMDbClient implements MovieIdentificationService {
} }
Map<MovieProperty, String> fields = new EnumMap<MovieProperty, String>(MovieProperty.class); Map<MovieProperty, String> fields = new EnumMap<MovieProperty, String>(MovieProperty.class);
fields.put(MovieProperty.name, data.get("title")); fields.put(MovieProperty.title, data.get("title"));
fields.put(MovieProperty.certification, data.get("rated")); fields.put(MovieProperty.certification, data.get("rated"));
fields.put(MovieProperty.tagline, data.get("plot")); fields.put(MovieProperty.tagline, data.get("plot"));
fields.put(MovieProperty.rating, data.get("imdbRating")); fields.put(MovieProperty.vote_average, data.get("imdbRating"));
fields.put(MovieProperty.votes, data.get("imdbVotes").replaceAll("\\D", "")); fields.put(MovieProperty.vote_count, data.get("imdbVotes").replaceAll("\\D", ""));
fields.put(MovieProperty.imdb_id, data.get("imdbID")); fields.put(MovieProperty.imdb_id, data.get("imdbID"));
List<String> genres = new ArrayList<String>(); List<String> genres = new ArrayList<String>();
@ -225,10 +224,9 @@ public class IMDbClient implements MovieIdentificationService {
List<Person> actors = new ArrayList<Person>(); List<Person> actors = new ArrayList<Person>();
for (String it : data.get("actors").split(",")) { for (String it : data.get("actors").split(",")) {
actors.add(new Person(it.trim(), null, "Actor", null, null)); actors.add(new Person(it.trim(), null, null));
} }
List<Artwork> image = singletonList(new Artwork("poster", data.get("poster"), null, null, null)); return new MovieInfo(fields, genres, new ArrayList<String>(0), actors);
return new MovieInfo(fields, genres, actors, image);
} }
} }

View File

@ -2,10 +2,14 @@
package net.sourceforge.filebot.web; package net.sourceforge.filebot.web;
import java.util.Arrays;
public class Movie extends SearchResult { public class Movie extends SearchResult {
protected int year; protected int year;
protected int imdbId; protected int imdbId;
protected int tmdbId;
protected Movie() { protected Movie() {
@ -14,14 +18,15 @@ public class Movie extends SearchResult {
public Movie(Movie obj) { public Movie(Movie obj) {
this(obj.name, obj.year, obj.imdbId); this(obj.name, obj.year, obj.imdbId, obj.tmdbId);
} }
public Movie(String name, int year, int imdbId) { public Movie(String name, int year, int imdbId, int tmdbId) {
super(name); super(name);
this.year = year; this.year = year;
this.imdbId = imdbId; this.imdbId = imdbId;
this.tmdbId = tmdbId;
} }
@ -35,12 +40,19 @@ public class Movie extends SearchResult {
} }
public int getTmdbId() {
return tmdbId;
}
@Override @Override
public boolean equals(Object object) { public boolean equals(Object object) {
if (object instanceof Movie) { if (object instanceof Movie) {
Movie other = (Movie) object; Movie other = (Movie) object;
if (imdbId > 0 && other.imdbId > 0) { if (imdbId > 0 && other.imdbId > 0) {
return imdbId == other.imdbId; return imdbId == other.imdbId;
} else if (tmdbId > 0 && other.tmdbId > 0) {
return tmdbId == other.tmdbId;
} }
return year == other.year && name.equals(other.name); return year == other.year && name.equals(other.name);
@ -58,7 +70,7 @@ public class Movie extends SearchResult {
@Override @Override
public int hashCode() { public int hashCode() {
return imdbId; return Arrays.hashCode(new Object[] { name, year });
} }

View File

@ -50,7 +50,6 @@ public class MovieFormat extends Format {
return sb; return sb;
} }
private final Pattern moviePattern = Pattern.compile("([^\\p{Punct}]+?)[\\p{Punct}\\s]+(\\d{4})(?:[\\p{Punct}\\s]+|$)"); private final Pattern moviePattern = Pattern.compile("([^\\p{Punct}]+?)[\\p{Punct}\\s]+(\\d{4})(?:[\\p{Punct}\\s]+|$)");
private final Pattern partPattern = Pattern.compile("(?:Part|CD)\\D?(\\d)$", Pattern.CASE_INSENSITIVE); private final Pattern partPattern = Pattern.compile("(?:Part|CD)\\D?(\\d)$", Pattern.CASE_INSENSITIVE);
@ -73,7 +72,7 @@ public class MovieFormat extends Format {
String name = m.group(1).trim(); String name = m.group(1).trim();
int year = Integer.parseInt(m.group(2)); int year = Integer.parseInt(m.group(2));
Movie movie = new Movie(name, year, -1); Movie movie = new Movie(name, year, -1, -1);
if (partIndex >= 0) { if (partIndex >= 0) {
movie = new MoviePart(movie, partIndex, partCount); movie = new MoviePart(movie, partIndex, partCount);
} }

View File

@ -9,17 +9,17 @@ public class MoviePart extends Movie {
public MoviePart(MoviePart obj) { public MoviePart(MoviePart obj) {
this(obj.name, obj.year, obj.imdbId, obj.partIndex, obj.partCount); this(obj.name, obj.year, obj.imdbId, obj.tmdbId, obj.partIndex, obj.partCount);
} }
public MoviePart(Movie movie, int partIndex, int partCount) { public MoviePart(Movie movie, int partIndex, int partCount) {
this(movie.name, movie.year, movie.imdbId, partIndex, partCount); this(movie.name, movie.year, movie.imdbId, movie.tmdbId, partIndex, partCount);
} }
public MoviePart(String name, int year, int imdbId, int partIndex, int partCount) { public MoviePart(String name, int year, int imdbId, int tmdbId, int partIndex, int partCount) {
super(name, year, imdbId); super(name, year, imdbId, tmdbId);
this.partIndex = partIndex; this.partIndex = partIndex;
this.partCount = partCount; this.partCount = partCount;
} }

View File

@ -143,7 +143,7 @@ public class OpenSubtitlesXmlRpc {
String name = matcher.group(1).replaceAll("\"", "").trim(); String name = matcher.group(1).replaceAll("\"", "").trim();
int year = Integer.parseInt(matcher.group(2)); int year = Integer.parseInt(matcher.group(2));
movies.add(new Movie(name, year, Integer.parseInt(imdbid))); movies.add(new Movie(name, year, Integer.parseInt(imdbid), -1));
} catch (Exception e) { } catch (Exception e) {
Logger.getLogger(OpenSubtitlesXmlRpc.class.getName()).log(Level.FINE, String.format("Ignore movie [%s]: %s", movie, e.getMessage())); Logger.getLogger(OpenSubtitlesXmlRpc.class.getName()).log(Level.FINE, String.format("Ignore movie [%s]: %s", movie, e.getMessage()));
} }
@ -238,7 +238,7 @@ public class OpenSubtitlesXmlRpc {
int year = Integer.parseInt(info.get("MovieYear")); int year = Integer.parseInt(info.get("MovieYear"));
int imdb = Integer.parseInt(info.get("MovieImdbID")); int imdb = Integer.parseInt(info.get("MovieImdbID"));
movieHashMap.put(hash, new Movie(name, year, imdb)); movieHashMap.put(hash, new Movie(name, year, imdb, -1));
} }
} }
@ -261,7 +261,7 @@ public class OpenSubtitlesXmlRpc {
String name = data.get("title"); String name = data.get("title");
int year = Integer.parseInt(data.get("year")); int year = Integer.parseInt(data.get("year"));
return new Movie(name, year, imdbid); return new Movie(name, year, imdbid, -1);
} catch (RuntimeException e) { } catch (RuntimeException e) {
// ignore, invalid response // ignore, invalid response
Logger.getLogger(getClass().getName()).log(Level.WARNING, String.format("Failed to lookup movie by imdbid %s: %s", imdbid, e.getMessage())); Logger.getLogger(getClass().getName()).log(Level.WARNING, String.format("Failed to lookup movie by imdbid %s: %s", imdbid, e.getMessage()));

View File

@ -97,7 +97,7 @@ public class SublightSubtitleClient implements SubtitleProvider, VideoHashSubtit
// remove classifier (e.g. tt0436992 -> 0436992) // remove classifier (e.g. tt0436992 -> 0436992)
int id = Integer.parseInt(imdb.getId().substring(2)); int id = Integer.parseInt(imdb.getId().substring(2));
results.add(new Movie(imdb.getTitle(), imdb.getYear(), id)); results.add(new Movie(imdb.getTitle(), imdb.getYear(), id, -1));
} }
} }
@ -120,6 +120,7 @@ public class SublightSubtitleClient implements SubtitleProvider, VideoHashSubtit
} }
@Override
public Map<File, List<SubtitleDescriptor>> getSubtitleList(File[] files, final String languageName) throws Exception { public Map<File, List<SubtitleDescriptor>> getSubtitleList(File[] files, final String languageName) throws Exception {
Map<File, List<SubtitleDescriptor>> subtitles = new HashMap<File, List<SubtitleDescriptor>>(files.length); Map<File, List<SubtitleDescriptor>> subtitles = new HashMap<File, List<SubtitleDescriptor>>(files.length);

View File

@ -5,17 +5,18 @@ package net.sourceforge.filebot.web;
import static java.util.Arrays.*; import static java.util.Arrays.*;
import static java.util.Collections.*; import static java.util.Collections.*;
import static net.sourceforge.filebot.web.WebRequest.*; import static net.sourceforge.filebot.web.WebRequest.*;
import static net.sourceforge.tuned.XPathUtilities.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.Reader;
import java.io.Serializable; import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.AbstractList;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -27,19 +28,18 @@ import java.util.logging.Logger;
import javax.swing.Icon; import javax.swing.Icon;
import net.sourceforge.filebot.ResourceManager; import net.sourceforge.filebot.ResourceManager;
import net.sourceforge.filebot.web.TMDbClient.Artwork.ArtworkProperty;
import net.sourceforge.filebot.web.TMDbClient.MovieInfo.MovieProperty; import net.sourceforge.filebot.web.TMDbClient.MovieInfo.MovieProperty;
import net.sourceforge.filebot.web.TMDbClient.Person.PersonProperty; import net.sourceforge.filebot.web.TMDbClient.Person.PersonProperty;
import org.w3c.dom.Document; import org.json.simple.JSONArray;
import org.w3c.dom.Node; import org.json.simple.JSONObject;
import org.xml.sax.SAXException; import org.json.simple.JSONValue;
public class TMDbClient implements MovieIdentificationService { public class TMDbClient implements MovieIdentificationService {
private static final String host = "api.themoviedb.org"; private static final String host = "api.themoviedb.org";
private static final String version = "2.1"; private static final String version = "3";
private static final FloodLimit SEARCH_LIMIT = new FloodLimit(10, 12, TimeUnit.SECONDS); private static final FloodLimit SEARCH_LIMIT = new FloodLimit(10, 12, TimeUnit.SECONDS);
private static final FloodLimit REQUEST_LIMIT = new FloodLimit(30, 12, TimeUnit.SECONDS); private static final FloodLimit REQUEST_LIMIT = new FloodLimit(30, 12, TimeUnit.SECONDS);
@ -66,40 +66,41 @@ public class TMDbClient implements MovieIdentificationService {
@Override @Override
public List<Movie> searchMovie(String query, Locale locale) throws IOException { public List<Movie> searchMovie(String query, Locale locale) throws IOException {
JSONObject response = request("search/movie", singletonMap("query", query), locale, SEARCH_LIMIT);
List<Movie> result = new ArrayList<Movie>();
for (JSONObject it : jsonList(response.get("results"))) {
// e.g. {"id":16320,"title":"冲出宁静号","release_date":"2005-09-30","original_title":"Serenity"}
String title = (String) it.get("title");
if (title == null || title.isEmpty()) {
title = (String) it.get("original_title");
}
try { try {
return getMovies("Movie.search", encode(query), locale, SEARCH_LIMIT); long id = (Long) it.get("id");
} catch (SAXException e) { int year = -1;
// TMDb output is sometimes malformed xml try {
Logger.getLogger(getClass().getName()).log(Level.WARNING, e.getMessage()); String release = (String) it.get("release_date");
return emptyList(); year = new Scanner(release).useDelimiter("\\D+").nextInt();
} catch (Exception e) {
throw new IllegalArgumentException("Missing data: year");
}
result.add(new Movie(title, year, -1, (int) id));
} catch (Exception e) {
Logger.getLogger(TMDbClient.class.getName()).log(Level.FINE, String.format("Ignore movie [%s]: %s", title, e.getMessage()));
} }
} }
return result;
public List<Movie> searchMovie(String hash, long bytesize, Locale locale) throws IOException, SAXException {
return getMovies("Media.getInfo", hash + "/" + bytesize, locale, SEARCH_LIMIT);
} }
@Override @Override
public Movie getMovieDescriptor(int imdbid, Locale locale) throws Exception { public Movie getMovieDescriptor(int imdbid, Locale locale) throws IOException {
Document dom = fetchResource("Movie.imdbLookup", String.format("tt%07d", imdbid), locale, REQUEST_LIMIT); MovieInfo info = getMovieInfo(String.format("tt%07d", imdbid), locale, false);
Node movie = selectNode("//movie", dom); if (info != null) {
return new Movie(info.getName(), info.getReleased().getYear(), info.getImdbId(), info.getId());
if (movie == null)
return null;
String name = getTextContent("name", movie);
String released = getTextContent("released", movie);
int year = -1;
try {
year = new Scanner(released).useDelimiter("\\D+").nextInt();
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Illegal release year: " + released);
} }
return null;
return new Movie(name, year, imdbid);
} }
@ -109,159 +110,173 @@ public class TMDbClient implements MovieIdentificationService {
} }
protected List<Movie> getMovies(String method, String parameter, Locale locale, FloodLimit limit) throws IOException, SAXException { public MovieInfo getMovieInfo(Movie movie, Locale locale) throws IOException {
Document dom = fetchResource(method, parameter, locale, limit); if (movie.getTmdbId() >= 0) {
List<Movie> result = new ArrayList<Movie>(); return getMovieInfo(String.valueOf(movie.getTmdbId()), locale, true);
} else if (movie.getImdbId() >= 0) {
for (Node node : selectNodes("//movie", dom)) { return getMovieInfo(String.format("tt%07d", movie.getImdbId()), locale, true);
String name = getTextContent("name", node);
try {
// release date format will be YYYY-MM-DD, but we only care about the year
int year = -1;
try {
year = new Scanner(getTextContent("released", node)).useDelimiter("\\D+").nextInt();
} catch (RuntimeException e) {
throw new IllegalArgumentException("Missing data: year");
}
// imdb id will be tt1234567, but we only care about the number
int imdbid = -1;
try {
imdbid = new Scanner(getTextContent("imdb_id", node)).useDelimiter("\\D+").nextInt();
} catch (RuntimeException e) {
// ignore
}
result.add(new Movie(name, year, imdbid));
} catch (Exception e) {
Logger.getLogger(TMDbClient.class.getName()).log(Level.FINE, String.format("Ignore movie [%s]: %s", name, e.getMessage()));
}
}
return result;
}
protected URL getResourceLocation(String method, String parameter, Locale locale) throws MalformedURLException {
// e.g. http://api.themoviedb.org/2.1/Movie.search/en/xml/{apikey}/serenity
return new URL("http", host, "/" + version + "/" + method + "/" + locale.getLanguage() + "/xml/" + apikey + "/" + parameter);
}
protected Document fetchResource(String method, String parameter, Locale locale, final FloodLimit limit) throws IOException, SAXException {
return getDocument(new CachedPage(getResourceLocation(method, parameter, locale)) {
@Override
protected Reader openConnection(URL url) throws IOException {
try {
if (limit != null) {
limit.acquirePermit();
}
return super.openConnection(url);
} catch (InterruptedException e) {
throw new IOException(e);
}
};
}.get());
}
public MovieInfo getMovieInfo(Movie movie, Locale locale) throws Exception {
if (movie.getImdbId() >= 0) {
return getMovieInfoByIMDbID(movie.getImdbId(), Locale.ENGLISH);
} else { } else {
return getMovieInfoByName(movie.getName(), movie.getYear(), Locale.ENGLISH); for (Movie it : searchMovie(movie.getName(), locale)) {
if (movie.getName().equalsIgnoreCase(it.getName()) && movie.getYear() == it.getYear()) {
return getMovieInfo(String.valueOf(movie.getTmdbId()), locale, true);
} }
} }
public MovieInfo getMovieInfoByName(String name, int year, Locale locale) throws Exception {
for (Movie it : searchMovie(name, locale)) {
if (name.equalsIgnoreCase(it.getName()) && year == it.getYear()) {
return getMovieInfo(it, locale);
} }
}
return null; return null;
} }
public MovieInfo getMovieInfoByIMDbID(int imdbid, Locale locale) throws Exception { public MovieInfo getMovieInfo(String id, Locale locale, boolean extendedInfo) throws IOException {
if (imdbid < 0) JSONObject response = request("movie/" + id, null, locale, REQUEST_LIMIT);
throw new IllegalArgumentException("Illegal IMDb ID: " + imdbid);
// resolve imdbid to tmdbid Map<MovieProperty, String> fields = new EnumMap<MovieProperty, String>(MovieProperty.class);
Document dom = fetchResource("Movie.imdbLookup", String.format("tt%07d", imdbid), locale, REQUEST_LIMIT); for (MovieProperty key : MovieProperty.values()) {
Object value = response.get(key.name());
String tmdbid = selectString("//movie/id", dom); if (value != null) {
if (tmdbid == null || tmdbid.isEmpty()) { fields.put(key, value.toString());
throw new IllegalArgumentException("Unable to lookup tmdb entry: " + String.format("tt%07d", imdbid)); }
} }
// get complete movie info via tmdbid lookup try {
dom = fetchResource("Movie.getInfo", tmdbid, locale, REQUEST_LIMIT); JSONObject collection = (JSONObject) response.get("belongs_to_collection");
fields.put(MovieProperty.collection, (String) collection.get("name"));
// select info from xml } catch (Exception e) {
Node node = selectNode("//movie", dom); // ignore
Map<MovieProperty, String> movieProperties = new EnumMap<MovieProperty, String>(MovieProperty.class);
for (MovieProperty property : MovieProperty.values()) {
movieProperties.put(property, getTextContent(property.name(), node));
} }
List<String> genres = new ArrayList<String>(); List<String> genres = new ArrayList<String>();
for (Node category : selectNodes("//category[@type='genre']", node)) { for (JSONObject it : jsonList(response.get("genres"))) {
genres.add(getAttribute("name", category)); genres.add((String) it.get("name"));
} }
List<Artwork> artwork = new ArrayList<Artwork>(); List<String> spokenLanguages = new ArrayList<String>();
for (Node image : selectNodes("//image", node)) { for (JSONObject it : jsonList(response.get("spoken_languages"))) {
Map<ArtworkProperty, String> artworkProperties = new EnumMap<ArtworkProperty, String>(ArtworkProperty.class); spokenLanguages.add((String) it.get("iso_639_1"));
for (ArtworkProperty property : ArtworkProperty.values()) { }
artworkProperties.put(property, getAttribute(property.name(), image));
if (extendedInfo) {
JSONObject releases = request("movie/" + fields.get(MovieProperty.id) + "/releases", null, null, REQUEST_LIMIT);
for (JSONObject it : jsonList(releases.get("countries"))) {
if ("US".equals(it.get("iso_3166_1"))) {
fields.put(MovieProperty.certification, (String) it.get("certification"));
}
} }
artwork.add(new Artwork(artworkProperties));
} }
List<Person> cast = new ArrayList<Person>(); List<Person> cast = new ArrayList<Person>();
for (Node image : selectNodes("//person", node)) { if (extendedInfo) {
Map<PersonProperty, String> personProperties = new EnumMap<PersonProperty, String>(PersonProperty.class); JSONObject castResponse = request("movie/" + fields.get(MovieProperty.id) + "/casts", null, null, REQUEST_LIMIT);
for (PersonProperty property : PersonProperty.values()) { for (String section : new String[] { "cast", "crew" }) {
personProperties.put(property, getAttribute(property.name(), image)); for (JSONObject it : jsonList(castResponse.get(section))) {
Map<PersonProperty, String> person = new EnumMap<PersonProperty, String>(PersonProperty.class);
for (PersonProperty key : PersonProperty.values()) {
Object value = it.get(key.name());
if (value != null) {
person.put(key, value.toString());
}
}
cast.add(new Person(person));
}
} }
cast.add(new Person(personProperties));
} }
return new MovieInfo(movieProperties, genres, cast, artwork); return new MovieInfo(fields, genres, spokenLanguages, cast);
}
public List<Artwork> getArtwork(String id) throws IOException {
// http://api.themoviedb.org/3/movie/11/images
JSONObject config = request("configuration", null, null, REQUEST_LIMIT);
String baseUrl = (String) ((JSONObject) config.get("images")).get("base_url");
JSONObject images = request("movie/" + id + "/images", null, null, REQUEST_LIMIT);
List<Artwork> artwork = new ArrayList<Artwork>();
for (String section : new String[] { "backdrops", "posters" }) {
for (JSONObject it : jsonList(images.get(section))) {
try {
String url = baseUrl + "original" + (String) it.get("file_path");
long width = (Long) it.get("width");
long height = (Long) it.get("height");
String lang = (String) it.get("iso_639_1");
artwork.add(new Artwork(section, new URL(url), (int) width, (int) height, lang));
} catch (Exception e) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, "Invalid artwork: " + it, e);
}
}
}
return artwork;
}
public JSONObject request(String resource, Map<String, String> parameters, Locale locale, final FloodLimit limit) throws IOException {
// default parameters
LinkedHashMap<String, String> data = new LinkedHashMap<String, String>();
if (parameters != null) {
data.putAll(parameters);
}
if (locale != null && !locale.getLanguage().isEmpty()) {
data.put("language", locale.getLanguage());
}
data.put("api_key", apikey);
URL url = new URL("http", host, "/" + version + "/" + resource + "?" + encodeParameters(data));
CachedResource<String> json = new CachedResource<String>(url.toString(), String.class, 7 * 24 * 60 * 60 * 1000) {
@Override
public String process(ByteBuffer data) throws Exception {
return Charset.forName("UTF-8").decode(data).toString();
}
@Override
protected ByteBuffer fetchData(URL url, long lastModified) throws IOException {
if (limit != null) {
try {
limit.acquirePermit();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
return super.fetchData(url, lastModified);
}
};
return (JSONObject) JSONValue.parse(json.get());
}
protected List<JSONObject> jsonList(final Object array) {
return new AbstractList<JSONObject>() {
@Override
public JSONObject get(int index) {
return (JSONObject) ((JSONArray) array).get(index);
}
@Override
public int size() {
return ((JSONArray) array).size();
}
};
} }
public static class MovieInfo implements Serializable { public static class MovieInfo implements Serializable {
public static enum MovieProperty { public static enum MovieProperty {
translated, adult, backdrop_path, budget, homepage, id, imdb_id, original_title, overview, popularity, poster_path, release_date, revenue, runtime, tagline, title, vote_average, vote_count, certification, collection
adult,
language,
original_name,
name,
type,
id,
imdb_id,
url,
overview,
votes,
rating,
tagline,
certification,
released,
runtime
} }
protected Map<MovieProperty, String> fields; protected Map<MovieProperty, String> fields;
protected String[] genres; protected String[] genres;
protected Person[] cast; protected String[] spokenLanguages;
protected Artwork[] images;
protected Person[] people;
protected MovieInfo() { protected MovieInfo() {
@ -269,11 +284,11 @@ public class TMDbClient implements MovieIdentificationService {
} }
protected MovieInfo(Map<MovieProperty, String> fields, List<String> genres, List<Person> cast, List<Artwork> images) { protected MovieInfo(Map<MovieProperty, String> fields, List<String> genres, List<String> spokenLanguages, List<Person> people) {
this.fields = new EnumMap<MovieProperty, String>(fields); this.fields = new EnumMap<MovieProperty, String>(fields);
this.genres = genres.toArray(new String[0]); this.genres = genres.toArray(new String[0]);
this.cast = cast.toArray(new Person[0]); this.spokenLanguages = spokenLanguages.toArray(new String[0]);
this.images = images.toArray(new Artwork[0]); this.people = people.toArray(new Person[0]);
} }
@ -287,19 +302,18 @@ public class TMDbClient implements MovieIdentificationService {
} }
public boolean isTranslated() {
return Boolean.valueOf(get(MovieProperty.translated));
}
public boolean isAdult() { public boolean isAdult() {
return Boolean.valueOf(get(MovieProperty.adult)); return Boolean.valueOf(get(MovieProperty.adult));
} }
public Locale getLanguage() { public List<Locale> getSpokenLanguages() {
try { try {
return new Locale(get(MovieProperty.language)); List<Locale> locales = new ArrayList<Locale>();
for (String it : spokenLanguages) {
locales.add(new Locale(it));
}
return locales;
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
@ -307,17 +321,12 @@ public class TMDbClient implements MovieIdentificationService {
public String getOriginalName() { public String getOriginalName() {
return get(MovieProperty.original_name); return get(MovieProperty.original_title);
} }
public String getName() { public String getName() {
return get(MovieProperty.name); return get(MovieProperty.title);
}
public String getType() {
return get(MovieProperty.type);
} }
@ -340,9 +349,9 @@ public class TMDbClient implements MovieIdentificationService {
} }
public URL getUrl() { public URL getHomepage() {
try { try {
return new URL(get(MovieProperty.url)); return new URL(get(MovieProperty.homepage));
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
@ -356,7 +365,7 @@ public class TMDbClient implements MovieIdentificationService {
public Integer getVotes() { public Integer getVotes() {
try { try {
return new Integer(get(MovieProperty.votes)); return new Integer(get(MovieProperty.vote_count));
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
@ -365,7 +374,7 @@ public class TMDbClient implements MovieIdentificationService {
public Double getRating() { public Double getRating() {
try { try {
return new Double(get(MovieProperty.rating)); return new Double(get(MovieProperty.vote_average));
} catch (Exception e) { } catch (Exception e) {
return null; return null;
} }
@ -383,9 +392,15 @@ public class TMDbClient implements MovieIdentificationService {
} }
public String getCollection() {
// e.g. Star Wars Collection
return get(MovieProperty.collection);
}
public Date getReleased() { public Date getReleased() {
// e.g. 2005-09-30 // e.g. 2005-09-30
return Date.parse(get(MovieProperty.released), "yyyy-MM-dd"); return Date.parse(get(MovieProperty.release_date), "yyyy-MM-dd");
} }
@ -404,12 +419,12 @@ public class TMDbClient implements MovieIdentificationService {
public List<Person> getCast() { public List<Person> getCast() {
return unmodifiableList(asList(cast)); return unmodifiableList(asList(people));
} }
public String getDirector() { public String getDirector() {
for (Person person : cast) { for (Person person : people) {
if (person.isDirector()) if (person.isDirector())
return person.getName(); return person.getName();
} }
@ -419,7 +434,7 @@ public class TMDbClient implements MovieIdentificationService {
public List<String> getActors() { public List<String> getActors() {
List<String> actors = new ArrayList<String>(); List<String> actors = new ArrayList<String>();
for (Person person : cast) { for (Person person : people) {
if (person.isActor()) { if (person.isActor()) {
actors.add(person.getName()); actors.add(person.getName());
} }
@ -428,99 +443,6 @@ public class TMDbClient implements MovieIdentificationService {
} }
public List<Artwork> getImages() {
return unmodifiableList(asList(images));
}
@Override
public String toString() {
return fields.toString();
}
}
public static class Artwork implements Serializable {
public static enum ArtworkProperty {
type,
url,
size,
width,
height
}
protected Map<ArtworkProperty, String> fields;
protected Artwork() {
// used by serializer
}
public Artwork(Map<ArtworkProperty, String> fields) {
this.fields = new EnumMap<ArtworkProperty, String>(fields);
}
public Artwork(String type, String url, String size, String width, String height) {
fields = new EnumMap<ArtworkProperty, String>(ArtworkProperty.class);
fields.put(ArtworkProperty.type, type);
fields.put(ArtworkProperty.url, url);
fields.put(ArtworkProperty.size, size);
fields.put(ArtworkProperty.width, width);
fields.put(ArtworkProperty.height, height);
}
public String get(Object key) {
return fields.get(ArtworkProperty.valueOf(key.toString()));
}
public String get(ArtworkProperty key) {
return fields.get(key);
}
public String getType() {
return get(ArtworkProperty.type);
}
public URL getUrl() {
try {
return new URL(get(ArtworkProperty.url));
} catch (Exception e) {
return null;
}
}
public String getSize() {
return get(ArtworkProperty.size);
}
public Integer getWidth() {
try {
return new Integer(get(ArtworkProperty.width));
} catch (Exception e) {
return null;
}
}
public Integer getHeight() {
try {
return new Integer(get(ArtworkProperty.height));
} catch (Exception e) {
return null;
}
}
@Override @Override
public String toString() { public String toString() {
return fields.toString(); return fields.toString();
@ -531,14 +453,9 @@ public class TMDbClient implements MovieIdentificationService {
public static class Person implements Serializable { public static class Person implements Serializable {
public static enum PersonProperty { public static enum PersonProperty {
name, name, character, job
character,
job,
thumb,
department
} }
protected Map<PersonProperty, String> fields; protected Map<PersonProperty, String> fields;
@ -552,13 +469,11 @@ public class TMDbClient implements MovieIdentificationService {
} }
public Person(String name, String character, String job, String thumb, String department) { public Person(String name, String character, String job) {
fields = new EnumMap<PersonProperty, String>(PersonProperty.class); fields = new EnumMap<PersonProperty, String>(PersonProperty.class);
fields.put(PersonProperty.name, name); fields.put(PersonProperty.name, name);
fields.put(PersonProperty.character, character); fields.put(PersonProperty.character, character);
fields.put(PersonProperty.job, job); fields.put(PersonProperty.job, job);
fields.put(PersonProperty.thumb, thumb);
fields.put(PersonProperty.department, department);
} }
@ -587,22 +502,8 @@ public class TMDbClient implements MovieIdentificationService {
} }
public String getDepartment() {
return get(PersonProperty.department);
}
public URL getThumb() {
try {
return new URL(get(PersonProperty.thumb));
} catch (Exception e) {
return null;
}
}
public boolean isActor() { public boolean isActor() {
return "Actor".equals(getJob()); return getJob() == null;
} }
@ -617,4 +518,56 @@ public class TMDbClient implements MovieIdentificationService {
} }
} }
public static class Artwork {
private String category;
private String language;
private int width;
private int height;
private URL url;
public Artwork(String category, URL url, int width, int height, String language) {
this.category = category;
this.url = url;
this.width = width;
this.height = height;
this.language = language;
}
public String getCategory() {
return category;
}
public String getLanguage() {
return language;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public URL getUrl() {
return url;
}
@Override
public String toString() {
return String.format("{category: %s, width: %s, height: %s, language: %s, url: %s}", category, width, height, language, url);
}
}
} }

View File

@ -9,12 +9,12 @@ import java.util.List;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import net.sublight.webservice.Subtitle;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import net.sublight.webservice.Subtitle;
public class SublightSubtitleClientTest { public class SublightSubtitleClientTest {
@ -46,7 +46,7 @@ public class SublightSubtitleClientTest {
@Test @Test
public void getSubtitleListEnglish() { public void getSubtitleListEnglish() {
List<SubtitleDescriptor> list = client.getSubtitleList(new Movie("Heroes", 2006, 813715), "English"); List<SubtitleDescriptor> list = client.getSubtitleList(new Movie("Heroes", 2006, 813715, -1), "English");
SubtitleDescriptor sample = list.get(0); SubtitleDescriptor sample = list.get(0);
assertEquals("English", sample.getLanguageName()); assertEquals("English", sample.getLanguageName());
@ -58,7 +58,7 @@ public class SublightSubtitleClientTest {
@Test @Test
public void getSubtitleListAllLanguages() { public void getSubtitleListAllLanguages() {
List<SubtitleDescriptor> list = client.getSubtitleList(new Movie("Terminator 2", 1991, 103064), "Croatian"); List<SubtitleDescriptor> list = client.getSubtitleList(new Movie("Terminator 2", 1991, 103064, -1), "Croatian");
SubtitleDescriptor sample = list.get(0); SubtitleDescriptor sample = list.get(0);

View File

@ -8,10 +8,11 @@ import static org.junit.Assert.*;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import org.junit.Test; import net.sourceforge.filebot.web.TMDbClient.Artwork;
import net.sourceforge.filebot.web.TMDbClient.MovieInfo; import net.sourceforge.filebot.web.TMDbClient.MovieInfo;
import org.junit.Test;
public class TMDbClientTest { public class TMDbClientTest {
@ -25,18 +26,8 @@ public class TMDbClientTest {
assertEquals("冲出宁静号", movie.getName()); assertEquals("冲出宁静号", movie.getName());
assertEquals(2005, movie.getYear()); assertEquals(2005, movie.getYear());
assertEquals(379786, movie.getImdbId()); assertEquals(-1, movie.getImdbId());
} assertEquals(16320, movie.getTmdbId());
@Test
public void searchByHash() throws Exception {
List<Movie> results = tmdb.searchMovie("907172e7fe51ba57", 742086656, Locale.ENGLISH);
Movie movie = results.get(0);
assertEquals("Sin City", movie.getName());
assertEquals(2005, movie.getYear(), 0);
assertEquals(401792, movie.getImdbId(), 0);
} }
@ -47,20 +38,31 @@ public class TMDbClientTest {
assertEquals("Transformers", movie.getName()); assertEquals("Transformers", movie.getName());
assertEquals(2007, movie.getYear(), 0); assertEquals(2007, movie.getYear(), 0);
assertEquals(418279, movie.getImdbId(), 0); assertEquals(418279, movie.getImdbId(), 0);
assertEquals(1858, movie.getTmdbId(), 0);
} }
@Test @Test
public void getMovieInfo() throws Exception { public void getMovieInfo() throws Exception {
MovieInfo movie = tmdb.getMovieInfo(new Movie(null, 0, 418279), Locale.ENGLISH); MovieInfo movie = tmdb.getMovieInfo(new Movie(null, 0, 418279, -1), Locale.ENGLISH);
assertEquals("Transformers", movie.getName()); assertEquals("Transformers", movie.getName());
assertEquals("2007-07-03", movie.getReleased().toString()); assertEquals("2007-07-03", movie.getReleased().toString());
assertEquals("Adventure", movie.getGenres().get(0)); assertEquals("PG-13", movie.getCertification());
assertEquals("Deborah Lynn Scott", movie.getCast().get(0).getName()); assertEquals("[Action, Adventure, Science Fiction, Thriller]", movie.getGenres().toString());
assertEquals("Costume Design", movie.getCast().get(0).getJob()); assertEquals("[en]", movie.getSpokenLanguages().toString());
assertEquals("thumb", movie.getImages().get(0).getSize()); assertEquals("Shia LaBeouf", movie.getActors().get(0));
assertEquals("http://cf2.imgobject.com/t/p/w92/bgSHbGEA1OM6qDs3Qba4VlSZsNG.jpg", movie.getImages().get(0).getUrl().toString()); assertEquals("Michael Bay", movie.getDirector());
assertEquals("Paul Rubell", movie.getCast().get(30).getName());
assertEquals("Editor", movie.getCast().get(30).getJob());
}
@Test
public void getArtwork() throws Exception {
List<Artwork> artwork = tmdb.getArtwork("tt0418279");
assertEquals("backdrops", artwork.get(0).getCategory());
assertEquals("http://cf2.imgobject.com/t/p/original/p4OHBbXfxToWF4e36uEhQMSidWu.jpg", artwork.get(0).getUrl().toString());
} }
@ -68,7 +70,7 @@ public class TMDbClientTest {
public void floodLimit() throws Exception { public void floodLimit() throws Exception {
for (Locale it : Locale.getAvailableLocales()) { for (Locale it : Locale.getAvailableLocales()) {
List<Movie> results = tmdb.searchMovie("Serenity", it); List<Movie> results = tmdb.searchMovie("Serenity", it);
assertEquals(379786, results.get(0).getImdbId()); assertEquals(16320, results.get(0).getTmdbId());
} }
} }

View File

@ -1,56 +1,11 @@
// filebot -script "http://filebot.sf.net/scripts/artwork.tmdb.groovy" -trust-script /path/to/media/ // filebot -script fn:artwork.tmdb /path/to/movies/
/* /*
* Fetch movie artwork. The movie is determined using the parent folders name. * Fetch movie artwork. The movie is determined using the parent folders name.
*/ */
def fetchArtwork(outputFile, movieInfo, artworkType, artworkSize) { // xbmc artwork/nfo utility
// select and fetch artwork include("fn:lib/xbmc")
def artwork = movieInfo.images.find { it.type == artworkType && it.size == artworkSize }
if (artwork == null) {
println "Artwork not found: $outputFile"
return null
}
println "Fetching $outputFile => $artwork"
return artwork.url.saveAs(outputFile)
}
def fetchNfo(outputFile, movieInfo) {
movieInfo.applyXmlTemplate('''<movie>
<title>$name</title>
<year>$released.year</year>
<rating>$rating</rating>
<votes>$votes</votes>
<plot>$overview</plot>
<runtime>$runtime</runtime>
<mpaa>$certification</mpaa>
<genre>${!genres.empty ? genres[0] : ''}</genre>
<id>tt${imdbId.pad(7)}</id>
</movie>
''')
.replaceAll(/\t|\r|\n/, '') // xbmc can't handle leading/trailing whitespace properly
.saveAs(outputFile)
}
def fetchMovieArtworkAndNfo(movieDir, movie) {
println "Fetch nfo and artwork for $movie"
def movieInfo = TheMovieDB.getMovieInfo(movie, Locale.ENGLISH)
println movieInfo
movieInfo.images.each {
println "Available artwork: $it.url => $it"
}
// fetch nfo
fetchNfo(movieDir['movie.nfo'], movieInfo)
// fetch series banner, fanart, posters, etc
fetchArtwork(movieDir['folder.jpg'], movieInfo, 'poster', 'original')
fetchArtwork(movieDir['backdrop.jpg'], movieInfo, 'backdrop', 'original')
}
args.eachMediaFolder { dir -> args.eachMediaFolder { dir ->
@ -72,7 +27,7 @@ args.eachMediaFolder { dir ->
// sort by relevance // sort by relevance
options = options.sortBySimilarity(query, { it.name }) options = options.sortBySimilarity(query, { it.name })
// auto-select series // auto-select movie
def movie = options[0] def movie = options[0]
// maybe require user input // maybe require user input

View File

@ -93,15 +93,16 @@ def fetchSeriesArtworkAndNfo(seriesDir, seasonDir, series, season, locale = _arg
// functions for TheMovieDB artwork/nfo // functions for TheMovieDB artwork/nfo
def fetchMovieArtwork(outputFile, movieInfo, artworkType, artworkSize) { def fetchMovieArtwork(outputFile, movieInfo, category, language) {
// select and fetch artwork // select and fetch artwork
def artwork = movieInfo.images.find { it.type == artworkType && it.size == artworkSize } def artwork = TheMovieDB.getArtwork(movieInfo.id as String)
if (artwork == null) { def selection = [language, 'en', null].findResult{ l -> artwork.find{ (l == it.language || l == null) && it.category == category } }
if (selection == null) {
println "Artwork not found: $outputFile" println "Artwork not found: $outputFile"
return null return null
} }
println "Fetching $outputFile => $artwork" println "Fetching $outputFile => $selection"
return artwork.url.saveAs(outputFile) return selection.url.saveAs(outputFile)
} }
def fetchMovieNfo(outputFile, movieInfo) { def fetchMovieNfo(outputFile, movieInfo) {
@ -124,12 +125,13 @@ def fetchMovieNfo(outputFile, movieInfo) {
def fetchMovieArtworkAndNfo(movieDir, movie, locale = _args.locale) { def fetchMovieArtworkAndNfo(movieDir, movie, locale = _args.locale) {
try { try {
def movieInfo = TheMovieDB.getMovieInfo(movie, locale) def movieInfo = TheMovieDB.getMovieInfo(movie, locale)
// fetch nfo // fetch nfo
fetchMovieNfo(movieDir['movie.nfo'], movieInfo) fetchMovieNfo(movieDir['movie.nfo'], movieInfo)
// fetch series banner, fanart, posters, etc // fetch series banner, fanart, posters, etc
fetchMovieArtwork(movieDir['folder.jpg'], movieInfo, 'poster', 'original') fetchMovieArtwork(movieDir['folder.jpg'], movieInfo, 'posters', locale.language)
fetchMovieArtwork(movieDir['backdrop.jpg'], movieInfo, 'backdrop', 'original') fetchMovieArtwork(movieDir['backdrop.jpg'], movieInfo, 'backdrops', locale.language)
} catch(e) { } catch(e) {
println "${e.class.simpleName}: ${e.message}" println "${e.class.simpleName}: ${e.message}"
} }