package moviescraper.doctord.model;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.nodes.Document;
import moviescraper.doctord.controller.FileDownloaderUtilities;
import moviescraper.doctord.controller.siteparsingprofile.SecurityPassthrough;
import moviescraper.doctord.controller.siteparsingprofile.SiteParsingProfile;
import moviescraper.doctord.controller.siteparsingprofile.specific.Data18MovieParsingProfile;
import moviescraper.doctord.controller.siteparsingprofile.specific.Data18WebContentParsingProfile;
import moviescraper.doctord.controller.siteparsingprofile.specific.DmmParsingProfile;
import moviescraper.doctord.controller.siteparsingprofile.specific.IAFDParsingProfile;
import moviescraper.doctord.controller.siteparsingprofile.specific.JavLibraryParsingProfile;
import moviescraper.doctord.controller.xmlserialization.KodiXmlMovieBean;
import moviescraper.doctord.model.dataitem.*;
import moviescraper.doctord.model.dataitem.Runtime;
import moviescraper.doctord.model.preferences.MoviescraperPreferences;
public class Movie {
/* Be careful if you decide you want to change the field names in this class (especially the arrays)
* because reflection is used in the movie amalgamation routine to get these fields by name, so you will need to
* update the references in the reflective call with the new name as well.
*/
private ArrayList<Actor> actors;
private ArrayList<Director> directors;
private Thumb[] fanart;
private Thumb[] extraFanart;
private Thumb preferredFanartToWriteToDisk;
private ArrayList<Genre> genres;
private ID id;
private MPAARating mpaa;
private OriginalTitle originalTitle;
private Outline outline;
private Plot plot;
private Thumb[] posters;
private Rating rating;
private ReleaseDate releaseDate;
private Runtime runtime;
private Set set;
private SortTitle sortTitle;
private Studio studio;
private Tagline tagline;
private ArrayList<Tag> tags;
private Title title;
private List<Title> allTitles = new ArrayList<>(); //this is currently not used for much; it used to allow the user to select from one of each title in a drop down box on the file detail panel, but now that amalgamation is here, that feature is not needed as much. It may make sense to put in a generic way to handle selecting between data item sources from amalgamation on a per item basis in the file detail panel, however
private Top250 top250;
private Trailer trailer;
private Votes votes;
private Year year;
private String fileName;
public Movie(ArrayList<Actor> actors, ArrayList<Director> directors,
Thumb[] fanart, Thumb[] extraFanart, ArrayList<Genre> genres, ArrayList<Tag> tags,
ID id, MPAARating mpaa,
OriginalTitle originalTitle, Outline outline, Plot plot,
Thumb[] posters, Rating rating, ReleaseDate releaseDate, Runtime runtime, Set set,
SortTitle sortTitle, Studio studio, Tagline tagline, Title title,
Top250 top250, Trailer trailer, Votes votes, Year year) {
super();
this.actors = actors;
this.directors = directors;
this.fanart = fanart;
this.extraFanart = extraFanart;
this.genres = genres;
this.tags = tags;
this.id = id;
this.mpaa = mpaa;
this.originalTitle = originalTitle;
this.outline = outline;
this.plot = plot;
this.posters = posters;
this.rating = rating;
this.releaseDate = releaseDate;
this.runtime = runtime;
this.set = set;
this.sortTitle = sortTitle;
this.studio = studio;
this.tagline = tagline;
this.title = title;
this.top250 = top250;
this.trailer = trailer;
this.votes = votes;
this.year = year;
}
public Movie(SiteParsingProfile siteToScrapeFrom) {
title = siteToScrapeFrom.scrapeTitle();
originalTitle = siteToScrapeFrom.scrapeOriginalTitle();
sortTitle = siteToScrapeFrom.scrapeSortTitle();
set = siteToScrapeFrom.scrapeSet();
rating = siteToScrapeFrom.scrapeRating();
year = siteToScrapeFrom.scrapeYear();
top250 = siteToScrapeFrom.scrapeTop250();
trailer = siteToScrapeFrom.scrapeTrailer();
votes = siteToScrapeFrom.scrapeVotes();
outline = siteToScrapeFrom.scrapeOutline();
plot = siteToScrapeFrom.scrapePlot();
tagline = siteToScrapeFrom.scrapeTagline();
studio = siteToScrapeFrom.scrapeStudio();
releaseDate = siteToScrapeFrom.scrapeReleaseDate();
runtime = siteToScrapeFrom.scrapeRuntime();
posters = siteToScrapeFrom.scrapePosters();
fanart = siteToScrapeFrom.scrapeFanart();
extraFanart = siteToScrapeFrom.scrapeExtraFanart();
mpaa = siteToScrapeFrom.scrapeMPAA();
id = siteToScrapeFrom.scrapeID();
actors = siteToScrapeFrom.scrapeActors();
genres = siteToScrapeFrom.scrapeGenres();
tags = siteToScrapeFrom.scrapeTags();
directors = siteToScrapeFrom.scrapeDirectors();
setAllDataItemSources(siteToScrapeFrom);
String fileNameOfScrapedMovie = siteToScrapeFrom.getFileNameOfScrapedMovie();
if(fileNameOfScrapedMovie != null && fileNameOfScrapedMovie.trim().length() > 0)
{
fileName = fileNameOfScrapedMovie;
}
MoviescraperPreferences scraperPreferences = MoviescraperPreferences.getInstance();
if(scraperPreferences.getUseFileNameAsTitle() && fileName != null && fileName.length() > 0)
{
title = new Title(fileName);
title.setDataItemSource(new DefaultDataItemSource());
}
appendIDToStartOfTitle();
}
/**
* @param siteToScrapeFrom
*/
private void setAllDataItemSources(SiteParsingProfile siteToScrapeFrom) {
originalTitle.setDataItemSource(siteToScrapeFrom);
title.setDataItemSource(siteToScrapeFrom);
sortTitle.setDataItemSource(siteToScrapeFrom);
set.setDataItemSource(siteToScrapeFrom);
rating.setDataItemSource(siteToScrapeFrom);
year.setDataItemSource(siteToScrapeFrom);
top250.setDataItemSource(siteToScrapeFrom);
trailer.setDataItemSource(siteToScrapeFrom);
votes.setDataItemSource(siteToScrapeFrom);
outline.setDataItemSource(siteToScrapeFrom);
plot.setDataItemSource(siteToScrapeFrom);
tagline.setDataItemSource(siteToScrapeFrom);
studio.setDataItemSource(siteToScrapeFrom);
releaseDate.setDataItemSource(siteToScrapeFrom);
runtime.setDataItemSource(siteToScrapeFrom);
setDataItemSourceOnThumbs(posters, siteToScrapeFrom);
setDataItemSourceOnThumbs(fanart, siteToScrapeFrom);
setDataItemSourceOnThumbs(extraFanart, siteToScrapeFrom);
mpaa.setDataItemSource(siteToScrapeFrom);
id.setDataItemSource(siteToScrapeFrom);
for(Actor currentActor : actors)
currentActor.setDataItemSource(siteToScrapeFrom);
for(Genre currentGenre : genres)
currentGenre.setDataItemSource(siteToScrapeFrom);
for(Tag currentTag : tags)
{
currentTag.setDataItemSource(siteToScrapeFrom);
}
for(Director currentDirector : directors)
currentDirector.setDataItemSource(siteToScrapeFrom);
}
/**
* If the appropriate preference is set, add the ID number to the end of the title field
*/
private void appendIDToStartOfTitle()
{
if(MoviescraperPreferences.getInstance().getAppendIDToStartOfTitle() && id != null &&
id.getId() != null && id.getId().trim().length() > 0 && hasValidTitle())
{
title.setTitle(id.getId() + " - " + title.getTitle());
}
}
private void setDataItemSourceOnThumbs(Thumb [] thumbs, DataItemSource dataItemSource)
{
for (Thumb thumb : thumbs)
{
thumb.setDataItemSource(dataItemSource);
}
}
/**
* Create a movie by reading in a values from a nfo file created by previously scraping the movie and then writing the metadata out to the file
* @param nfoFile
* @throws IOException
*/
public static Movie createMovieFromNfo(File nfoFile) throws IOException
{
Movie movieFromNfo = null;
try (FileInputStream fisTargetFile = new FileInputStream(nfoFile);) {
String targetFileStr = IOUtils.toString(fisTargetFile, "UTF-8");
//Sometimes there's some junk before the prolog tag. Do a workaround to remove that junk.
//This really isn't the cleanest way to do this, but it'll work for now
//check first to make sure the string even contains <?xml so we don't loop through an invalid file needlessly
if(targetFileStr.contains("<?xml"))
{
while(targetFileStr.length() > 0 && !targetFileStr.startsWith("<?xml"))
{
if(targetFileStr.length() > 1)
{
targetFileStr = targetFileStr.substring(1,targetFileStr.length());
}
else break;
}
}
KodiXmlMovieBean xmlMovieBean = KodiXmlMovieBean.makeFromXML(targetFileStr);
if(xmlMovieBean != null)
{
movieFromNfo = xmlMovieBean.toMovie();
}
return movieFromNfo;
}
}
public ArrayList<Actor> getActors() {
return actors;
}
public ArrayList<Director> getDirectors() {
return directors;
}
public Thumb[] getFanart() {
return fanart;
}
public ArrayList<Genre> getGenres() {
return genres;
}
public ArrayList<Tag> getTags()
{
return tags;
}
public ID getId() {
return id;
}
public MPAARating getMpaa() {
return mpaa;
}
public OriginalTitle getOriginalTitle() {
return originalTitle;
}
public Outline getOutline() {
return outline;
}
public Plot getPlot() {
return plot;
}
public Thumb[] getPosters() {
return posters;
}
public Rating getRating() {
return rating;
}
public Runtime getRuntime() {
return runtime;
}
public Set getSet() {
return set;
}
public SortTitle getSortTitle() {
return sortTitle;
}
public Studio getStudio() {
return studio;
}
public Tagline getTagline() {
return tagline;
}
public Title getTitle() {
return title;
}
public Top250 getTop250() {
return top250;
}
public Votes getVotes() {
return votes;
}
public Year getYear() {
return year;
}
public void setActors(ArrayList<Actor> actors) {
this.actors = actors;
}
public void setDirectors(ArrayList<Director> directors) {
this.directors = directors;
}
public void setFanart(Thumb[] fanart) {
this.fanart = fanart;
}
public void setGenres(ArrayList<Genre> genres) {
this.genres = genres;
}
public void setTags(ArrayList<Tag> tags)
{
this.tags = tags;
}
public void setId(ID id) {
this.id = id;
}
public void setMpaa(MPAARating mpaa) {
this.mpaa = mpaa;
}
public void setOriginalTitle(OriginalTitle originalTitle) {
this.originalTitle = originalTitle;
}
public void setOutline(Outline outline) {
this.outline = outline;
}
public void setPlot(Plot plot) {
this.plot = plot;
}
public void setPosters(Thumb[] posters) {
this.posters = posters;
}
public void setRating(Rating rating) {
this.rating = rating;
}
public void setRuntime(Runtime runtime) {
this.runtime = runtime;
}
public void setSet(Set set) {
this.set = set;
}
public void setSortTitle(SortTitle sortTitle) {
this.sortTitle = sortTitle;
}
public void setStudio(Studio studio) {
this.studio = studio;
}
public void setTagline(Tagline tagline) {
this.tagline = tagline;
}
public void setTitle(Title title) {
this.title = title;
}
public void setTop250(Top250 top250) {
this.top250 = top250;
}
public void setVotes(Votes votes) {
this.votes = votes;
}
public void setYear(Year year) {
this.year = year;
}
@Override
public String toString() {
return "Movie [title=" + title + ", originalTitle=" + originalTitle
+ ", sortTitle=" + sortTitle + ", set=" + set + ", rating="
+ rating + ", year=" + year + ", top250=" + top250 + ", trailer = " + trailer + ", votes="
+ votes + ", outline=" + outline + ", plot=" + plot
+ ", tagline=" + tagline + ", studio=" + studio + "releaseDate=" + releaseDate + ", runtime="
+ runtime + ", posters=" + Arrays.toString(posters)
+ ", fanart=" + Arrays.toString(fanart) + ", extrafanart = "
+ Arrays.toString(extraFanart) + ", mpaa=" + mpaa
+ ", id=" + id + ", genres=" + genres + ", tags=" + tags + ", actors=" + actors
+ ", directors=" + directors + "]";
}
public String toXML() {
return title.toXML();
}
public void writeExtraFanart(File directoryMovieIsIn) throws IOException
{
if(directoryMovieIsIn != null && directoryMovieIsIn.exists() && directoryMovieIsIn.isDirectory() && getExtraFanart().length > 0)
{
File extraFanartFolder = new File(directoryMovieIsIn.getPath() + File.separator + "extrafanart");
FileUtils.forceMkdir(extraFanartFolder);
int currentExtraFanartNumber = 1;
for(Thumb currentExtraFanart : this.getExtraFanart())
{
File fileNameToWrite = new File(extraFanartFolder.getPath() + File.separator + "fanart" + currentExtraFanartNumber + ".jpg");
//no need to overwrite perfectly good extra fanart since this stuff doesn't change. this will also save time when rescraping since extra IO isn't done.
if(!fileNameToWrite.exists())
{
System.out.println("Writing extrafanart to " + fileNameToWrite);
currentExtraFanart.writeImageToFile(fileNameToWrite);
}
currentExtraFanartNumber++;
}
}
}
public void writeToFile(File nfoFile, File posterFile, File fanartFile, File currentlySelectedFolderJpgFile, File targetFolderForExtraFanartFolderAndActorFolder, File trailerFile, MoviescraperPreferences preferences) throws IOException {
// Output the movie to XML using XStream and a proxy class to
// translate things to a format that Kodi expects
//ID only appended if preference set and not already at the start of the title
if(!title.getTitle().startsWith(id.getId()))
{
appendIDToStartOfTitle();
}
String xml = new KodiXmlMovieBean(this).toXML();
// add the xml header since xstream doesn't do this
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>"
+ "\n" + xml;
//System.out.println("Xml I am writing to file: \n" + xml);
if(nfoFile != null && xml.length() > 0)
nfoFile.delete();
FileUtils.writeStringToFile(nfoFile, xml,
org.apache.commons.lang3.CharEncoding.UTF_8);
Thumb posterToSaveToDisk = null;
if(posters != null && posters.length > 0)
posterToSaveToDisk = posters[0];
boolean writePoster = preferences.getWriteFanartAndPostersPreference();
boolean writeFanart = preferences.getWriteFanartAndPostersPreference();
boolean writePosterIfAlreadyExists = preferences.getOverWriteFanartAndPostersPreference();
boolean writeFanartIfAlreadyExists = preferences.getOverWriteFanartAndPostersPreference();
boolean createFolderJpgEnabledPreference = preferences.getCreateFolderJpgEnabledPreference();
// save the first poster out
// maybe we did some clipping, so we're going to have to reencode it
if (this.getPosters().length > 0 &&
(writePoster || createFolderJpgEnabledPreference) &&
((posterFile.exists() == writePosterIfAlreadyExists) || (!posterFile.exists() || (createFolderJpgEnabledPreference))))
{
if(posterToSaveToDisk != null && (posterToSaveToDisk.isModified() || createFolderJpgEnabledPreference || !posterFile.exists() || writePosterIfAlreadyExists))
{
//reencode the jpg since we probably did a resize
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = (ImageWriter)iter.next();
// instantiate an ImageWriteParam object with default compression options
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
iwp.setCompressionQuality(1); // an float between 0 and 1
// 1 specifies minimum compression and maximum quality
IIOImage image = new IIOImage((RenderedImage) posterToSaveToDisk.getThumbImage(), null, null);
if(writePoster && posterToSaveToDisk.isModified())
{
System.out.println("Writing poster to " + posterFile);
try (FileImageOutputStream posterFileOutput = new FileImageOutputStream(posterFile);) {
writer.setOutput(posterFileOutput);
writer.write(null, image, iwp);
}
}
//write out the poster file without reencoding it and resizing it
else if((!posterFile.exists() || writePosterIfAlreadyExists) && posterToSaveToDisk.getThumbURL() != null)
{
System.out.println("Writing poster file from nfo: " + posterFile);
FileDownloaderUtilities.writeURLToFile(posterToSaveToDisk.getThumbURL(), posterFile, posterToSaveToDisk.getReferrerURL());
}
if(createFolderJpgEnabledPreference && currentlySelectedFolderJpgFile != null)
{
if(!posterToSaveToDisk.isModified() && (!currentlySelectedFolderJpgFile.exists() || (currentlySelectedFolderJpgFile.exists() && writePosterIfAlreadyExists)))
{
System.out.println("Writing folder.jpg (no changes) to " + currentlySelectedFolderJpgFile);
FileDownloaderUtilities.writeURLToFile(posterToSaveToDisk.getThumbURL(), currentlySelectedFolderJpgFile, posterToSaveToDisk.getReferrerURL());
}
else
{
if(!currentlySelectedFolderJpgFile.exists() || (currentlySelectedFolderJpgFile.exists() && writePosterIfAlreadyExists))
{
System.out.println("Writing folder to " + currentlySelectedFolderJpgFile);
try (FileImageOutputStream folderFileOutput = new FileImageOutputStream(currentlySelectedFolderJpgFile);) {
writer.setOutput(folderFileOutput);
writer.write(null, image, iwp);
}
}
else
{
System.out.println("Skipping overwrite of folder.jpg due to preference setting");
}
}
}
writer.dispose();
}
}
// save the first fanart out
// we didn't modify it so we can write it directly from the URL
if (this.getFanart().length > 0 && writeFanart && ((fanartFile.exists() == writeFanartIfAlreadyExists) || !fanartFile.exists()))
{
if(fanart != null && fanart.length > 0)
{
Thumb fanartToSaveToDisk;
if(preferredFanartToWriteToDisk != null)
fanartToSaveToDisk = preferredFanartToWriteToDisk;
else
fanartToSaveToDisk = fanart[0];
System.out.println("saving out first fanart to " + fanartFile);
//can save ourself redownloading the image if it's already in memory, but we dont want to reencode the image, so only do this if it's modified
if(fanartToSaveToDisk.getImageIconThumbImage() != null && fanartToSaveToDisk.isModified())
{
try {
ImageIO.write(fanartToSaveToDisk.toBufferedImage(), "jpg", fanartFile);
}
catch (IOException e) {
System.err.println("Failed to write fanart due to io error");
e.printStackTrace();
}
}
//download the url and save it out to disk
else FileDownloaderUtilities.writeURLToFile(fanartToSaveToDisk.getThumbURL(), fanartFile, posterToSaveToDisk.getReferrerURL());
}
}
//write out the extrafanart, if the preference for it is set
if(targetFolderForExtraFanartFolderAndActorFolder != null && preferences.getExtraFanartScrapingEnabledPreference())
{
System.out.println("Starting write of extra fanart into " + targetFolderForExtraFanartFolderAndActorFolder);
writeExtraFanart(targetFolderForExtraFanartFolderAndActorFolder);
}
//write the .actor images, if the preference for it is set
if(preferences.getDownloadActorImagesToActorFolderPreference() && targetFolderForExtraFanartFolderAndActorFolder != null)
{
System.out.println("Writing .actor images into " + targetFolderForExtraFanartFolderAndActorFolder);
writeActorImagesToFolder(targetFolderForExtraFanartFolderAndActorFolder);
}
//write out the trailer, if the preference for it is set
Trailer trailerToWrite = getTrailer();
if(preferences.getWriteTrailerToFile() && trailerToWrite != null && trailerToWrite.getTrailer().length() > 0)
{
trailerToWrite.writeTrailerToFile(trailerFile);
}
}
public void writeActorImagesToFolder(File targetFolder) throws IOException
{
File actorFolder = null;
if(targetFolder.isDirectory())
{
actorFolder = new File( targetFolder + File.separator + ".actors");
}
else if(targetFolder.isFile())
{
actorFolder = new File(targetFolder.getParent() + File.separator + ".actors");
}
//Don't create an empty .actors folder with no actors underneath it
if(this.hasAtLeastOneActorThumbnail() && actorFolder != null)
{
FileUtils.forceMkdir(actorFolder);
//on windows this new folder should have the hidden attribute; on unix it is already "hidden" by having a . in front of the name
Path path = actorFolder.toPath();
//if statement needed for Linux checking .actors hidden flag when .actors is a symlink
if(!Files.isHidden(path))
{
Boolean hidden = (Boolean) Files.getAttribute(path, "dos:hidden", LinkOption.NOFOLLOW_LINKS);
if (hidden != null && !hidden) {
try{
Files.setAttribute(path, "dos:hidden", Boolean.TRUE, LinkOption.NOFOLLOW_LINKS);
}
catch(AccessDeniedException e){
System.err.println("I was not allowed to make .actors folder hidden. This is not a big deal - continuing with write of actor files...");
}
}
}
for(Actor currentActor : this.getActors())
{
String currentActorToFileName = currentActor.getName().replace(' ', '_');
File fileNameToWrite = new File(actorFolder.getPath() + File.separator + currentActorToFileName + ".jpg");
currentActor.writeImageToFile(fileNameToWrite);
//reload from disk instead of cache since the cache is now pointing to the wrong image and the disk has the correct newly edited one
if(currentActor.isThumbEdited())
ImageCache.removeImageFromCache(fileNameToWrite.toURI().toURL(), false);
}
}
}
public boolean hasPoster() {
if (this.posters.length > 0)
return true;
else
return false;
}
private static String replaceLast(String string, String toReplace, String replacement) {
int pos = string.lastIndexOf(toReplace);
if (pos > -1) {
return string.substring(0, pos)
+ replacement
+ string.substring(pos + toReplace.length(), string.length());
} else {
return string;
}
}
//returns the movie file path without anything like CD1, Disc A, etc and also gets rid of the file extension
//Example: MyMovie ABC-123 CD1.avi returns MyMovie ABC-123
//Example2: MyMovie ABC-123.avi returns MyMovie ABC-123
public static String getUnstackedMovieName(File file)
{
String fileName = file.toString();
fileName = replaceLast(fileName, file.getName(), SiteParsingProfile.stripDiscNumber(FilenameUtils.removeExtension(file.getName())));
return fileName;
}
public static String getFileNameOfNfo(File file, Boolean nfoNamedMovieDotNfo)
{
if(nfoNamedMovieDotNfo)
{
return file.getPath() + File.separator + "movie.nfo";
}
else return getTargetFilePath(file, ".nfo");
}
public static String getFileNameOfPoster(File file, boolean getNoMovieNameInImageFiles) {
if(getNoMovieNameInImageFiles)
{
if(file.isDirectory())
{
return file.getPath() + File.separator + "poster.jpg";
}
else
{
return file.getParent() + File.separator + "poster.jpg";
}
}
else return getTargetFilePath(file, "-poster.jpg");
}
public static String getFileNameOfFolderJpg(File selectedValue) {
if(selectedValue.isDirectory())
{
return selectedValue.getPath() + File.separator + "folder.jpg";
}
else return selectedValue.getParent() + File.separator + "folder.jpg";
}
public static String getFileNameOfExtraFanartFolderName(File selectedValue)
{
if(selectedValue != null && selectedValue.isDirectory())
{
return selectedValue.getPath();
}
else if(selectedValue != null && selectedValue.isFile())
{
return selectedValue.getParent();
}
else return null;
}
public static String getFileNameOfTrailer(File selectedValue) {
//sometimes the trailer has a different extension
//than the movie so we will try to brute force a find by trying all movie name extensions
for (String extension : MovieFilenameFilter.acceptedMovieExtensions)
{
String potentialTrailer = tryToFindActualTrailerHelper(selectedValue, "." + extension);
if(potentialTrailer != null)
return potentialTrailer;
}
return getTargetFilePath(selectedValue, "-trailer.mp4");
}
/**
* Checks for the given file a trailer file exists for it for the given file name extension
* @param selectedValue - base file name of movie or nfo
* @param extension - the file name extension we are checking
* @return - the path to the file if it found the trailer, otherwise null
*/
private static String tryToFindActualTrailerHelper(File selectedValue, String extension)
{
String potentialPath = getTargetFilePath(selectedValue, "-trailer" + extension);
File trailerCandidate = new File(potentialPath);
if(trailerCandidate.exists())
return potentialPath;
return null;
}
public static String getFileNameOfFanart(File file, boolean getNoMovieNameInImageFiles) {
if(getNoMovieNameInImageFiles)
{
if(file.isDirectory())
{
return file.getPath() + File.separator + "fanart.jpg";
}
else
{
return file.getParent() + File.separator + "fanart.jpg";
}
}
else return getTargetFilePath(file, "-fanart.jpg");
}
private static String getTargetFilePath(File file, String extension)
{
if(!file.isDirectory())
{
String nfoName = getUnstackedMovieName(file) + extension;
return nfoName;
}
//look in the directory for an nfo file, otherwise we will make one based on the last word (JAVID of the folder name)
else
{
final String extensionFromParameter = extension;
//getting the nfo files in this directory, if any
File [] directoryContents = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File directory, String fileName) {
return fileName.endsWith(extensionFromParameter);
}
});
//if there are 1 or more files, it's not really in spec, so just return the first one
if (directoryContents.length > 0)
{
return directoryContents[0].getPath();
}
else
{
//no file found in directory, so we will be setting the target to create one in that directory
File[] directoryContentsOfAllFiles = file.listFiles(new MovieFilenameFilter());
if(directoryContentsOfAllFiles.length > 0)
{
//check to see if there's at least one file in the directory that is a movie and go by naming based off the first file found
for(File currentFile : directoryContentsOfAllFiles)
{
if(currentFile.isFile())
{
String targetFileName = getUnstackedMovieName(currentFile) + extension;
//System.out.println("returning " + targetFileName);
return targetFileName;
}
}
}
//Use the folder name as the basis for the filename created
return new File(file.getAbsolutePath() + File.separator + file.getName() + extension).getPath();
}
}
}
/*private String [] searchResultsHelperForScrapeMovie(File movieFile, SiteParsingProfile siteToParseFrom)
{
String [] searchResults = siteToParseFrom.getSearchResults(searchString);
int levDistanceOfCurrentMatch = 999999; // just some super high number
String idFromMovieFile = SiteParsingProfile.findIDTagFromFile(movieFile);
//loop through search results and see if URL happens to contain ID number in the URL. This will improve accuracy!
for (int i = 0; i < searchResults.length; i++)
{
String urltoMatch = searchResults[i].toLowerCase();
String idFromMovieFileToMatch = idFromMovieFile.toLowerCase().replaceAll("-", "");
//System.out.println("Comparing " + searchResults[i].toLowerCase() + " to " + idFromMovieFile.toLowerCase().replaceAll("-", ""));
if (urltoMatch.contains(idFromMovieFileToMatch))
{
//let's do some fuzzy logic searching to try to get the "best" match in case we got some that are pretty close
//and update the variables accordingly so we know what our best match so far is
int candidateLevDistanceOfCurrentMatch = StringUtils.getLevenshteinDistance(urltoMatch.toLowerCase(), idFromMovieFileToMatch);
if (candidateLevDistanceOfCurrentMatch < levDistanceOfCurrentMatch)
{
levDistanceOfCurrentMatch = candidateLevDistanceOfCurrentMatch;
searchResultNumberToUse = i;
}
}
}
return searchResults;
}*/
//Version that allows us to update the GUI while scraping
public static Movie scrapeMovie(File movieFile, SiteParsingProfile siteToParseFrom, String urlToScrapeFromDMM, boolean useURLtoScrapeFrom) throws IOException{
//If the user manually canceled the results on this scraper in a dialog box, just return a null movie
if(siteToParseFrom.getDiscardResults())
return null;
String searchString = siteToParseFrom.createSearchString(movieFile);
SearchResult [] searchResults = null;
int searchResultNumberToUse = 0;
//no URL was passed in so we gotta figure it ourselves
if(!useURLtoScrapeFrom)
{
searchResults = siteToParseFrom.getSearchResults(searchString);
int levDistanceOfCurrentMatch = 999999; // just some super high number
String idFromMovieFile = SiteParsingProfile.findIDTagFromFile(movieFile, siteToParseFrom.isFirstWordOfFileIsID());
//loop through search results and see if URL happens to contain ID number in the URL. This will improve accuracy!
for (int i = 0; i < searchResults.length; i++)
{
String urltoMatch = searchResults[i].getUrlPath().toLowerCase();
String idFromMovieFileToMatch = idFromMovieFile.toLowerCase().replaceAll("-", "");
//System.out.println("Comparing " + searchResults[i].toLowerCase() + " to " + idFromMovieFile.toLowerCase().replaceAll("-", ""));
if (urltoMatch.contains(idFromMovieFileToMatch))
{
//let's do some fuzzy logic searching to try to get the "best" match in case we got some that are pretty close
//and update the variables accordingly so we know what our best match so far is
int candidateLevDistanceOfCurrentMatch = StringUtils.getLevenshteinDistance(urltoMatch.toLowerCase(), idFromMovieFileToMatch);
if (candidateLevDistanceOfCurrentMatch < levDistanceOfCurrentMatch)
{
levDistanceOfCurrentMatch = candidateLevDistanceOfCurrentMatch;
searchResultNumberToUse = i;
}
}
}
}
//just use the URL to parse from the parameter
else if(useURLtoScrapeFrom)
{
searchResults = new SearchResult[1];
if(siteToParseFrom instanceof DmmParsingProfile)
searchResults[0] = new SearchResult(urlToScrapeFromDMM);
else if(siteToParseFrom instanceof Data18MovieParsingProfile || siteToParseFrom instanceof Data18WebContentParsingProfile)
searchResults[0] = new SearchResult(urlToScrapeFromDMM);
else if(siteToParseFrom instanceof JavLibraryParsingProfile)
searchResults[0] = new SearchResult(((JavLibraryParsingProfile) siteToParseFrom).getOverrideURLJavLibrary());
else if(siteToParseFrom instanceof IAFDParsingProfile)
searchResults[0] = new SearchResult(urlToScrapeFromDMM);
//override any of the above if we have specifically set an override url
if(siteToParseFrom.getOverridenSearchResult() != null)
{
searchResults[0] = siteToParseFrom.getOverridenSearchResult();
searchResultNumberToUse = 0;
}
}
if (searchResults != null && searchResults.length > 0 && searchResults[searchResultNumberToUse].getUrlPath().length() > 0)
{
System.out.println("Scraping this webpage for movie: " + searchResults[searchResultNumberToUse].getUrlPath());
//for now just set the movie to the first thing found unless we found a link which had something close to the ID
SearchResult searchResultToUse = searchResults[searchResultNumberToUse];
Document searchMatch = SiteParsingProfile.downloadDocument(searchResultToUse);
//Handle any captchas etc that prevent us from getting our result
if (searchMatch != null && SecurityPassthrough.class.isAssignableFrom(siteToParseFrom.getClass()))
{
SecurityPassthrough siteParsingProfileSecurityPassthrough = (SecurityPassthrough) siteToParseFrom;
if(siteParsingProfileSecurityPassthrough.requiresSecurityPassthrough(searchMatch))
{
searchMatch = siteParsingProfileSecurityPassthrough.runSecurityPassthrough(searchMatch, searchResultToUse);
}
}
siteToParseFrom.setDocument(searchMatch);
siteToParseFrom.setOverrideURLDMM(urlToScrapeFromDMM);
Movie scrapedMovie = new Movie(siteToParseFrom);
return scrapedMovie;
}
else //no movie match found
{
return null;
}
}
public boolean hasAtLeastOneActorThumbnail() {
for(Actor currentActor : actors)
{
if(currentActor.getThumb() != null && currentActor.getThumb().getThumbURL() != null && !currentActor.getThumb().getThumbURL().equals(""))
{
return true;
}
}
return false;
}
public Thumb[] getExtraFanart() {
return extraFanart;
}
public void setExtraFanart(Thumb [] extraFanart) {
this.extraFanart = extraFanart;
}
public Trailer getTrailer() {
return trailer;
}
public void setTrailer(Trailer trailer) {
this.trailer = trailer;
}
public boolean hasFanart() {
if (this.fanart.length > 0)
return true;
else
return false;
}
public List<Title> getAllTitles() {
return allTitles;
}
public void setAllTitles(List<Title> allTitles) {
this.allTitles = allTitles;
}
public static Movie getEmptyMovie() {
ArrayList<Actor> actors = new ArrayList<>();
ArrayList<Director> directors = new ArrayList<>();
ArrayList<Genre> genres = new ArrayList<>();
ArrayList<Tag> tags = new ArrayList<>();
Thumb[] fanart = new Thumb[0];
Thumb[] extraFanart = new Thumb[0];
Thumb[] posters = new Thumb[0];
ID id = new ID("");
MPAARating mpaa = new MPAARating("");
OriginalTitle originalTitle = OriginalTitle.BLANK_ORIGINALTITLE;
Outline outline = Outline.BLANK_OUTLINE;
Plot plot = Plot.BLANK_PLOT;
Rating rating = Rating.BLANK_RATING;
ReleaseDate releaseDate = ReleaseDate.BLANK_RELEASEDATE;
Runtime runtime = Runtime.BLANK_RUNTIME;
Set set = Set.BLANK_SET;
SortTitle sortTitle= SortTitle.BLANK_SORTTITLE;
Studio studio = Studio.BLANK_STUDIO;
Tagline tagline = Tagline.BLANK_TAGLINE;
Title title = new Title("");
Top250 top250 = Top250.BLANK_TOP250;
Trailer trailer = new Trailer(null);
Votes votes = Votes.BLANK_VOTES;
Year year = Year.BLANK_YEAR;
return new Movie(actors, directors, fanart, extraFanart, genres, tags, id, mpaa, originalTitle, outline, plot, posters, rating, releaseDate, runtime, set, sortTitle, studio, tagline, title, top250, trailer, votes, year);
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public ReleaseDate getReleaseDate() {
return releaseDate;
}
public void setReleaseDate(ReleaseDate releaseDate) {
this.releaseDate = releaseDate;
}
/**
* remove the item from the picked from the existing poster list and put it at
* the front of the list. if the movie does not contain the poster, no change will be made
* @param posterToGoToFront - poster to put in front
*/
public void moveExistingPosterToFront(Thumb posterToGoToFront)
{
if (posterToGoToFront != null) {
ArrayList<Thumb> existingPosters = new ArrayList<>(
Arrays.asList(getPosters()));
boolean didListContainPoster = existingPosters.remove(posterToGoToFront);
if(didListContainPoster)
{
existingPosters.add(0, posterToGoToFront);
Thumb[] posterArray = new Thumb[existingPosters
.size()];
setPosters(existingPosters
.toArray(posterArray));
}
}
}
/**
* remove the item from the picked from the existing fanart list and put it at
* the front of the list. if the movie does not contain the fanart, no change will be made
* @param fanartToGoToFront - fanart to put in front
*/
public void moveExistingFanartToFront(Thumb fanartToGoToFront)
{
if (fanartToGoToFront != null) {
ArrayList<Thumb> existingFanarts = new ArrayList<>(
Arrays.asList(getFanart()));
boolean didListContainPoster = existingFanarts.remove(fanartToGoToFront);
if(didListContainPoster)
{
existingFanarts.add(0, fanartToGoToFront);
Thumb[] fanartArray = new Thumb[existingFanarts
.size()];
setFanart(existingFanarts
.toArray(fanartArray));
}
}
}
/**
* @return true if the movie has a non-null, non-zero length title, false otherwise
*/
public boolean hasValidTitle() {
return (title != null && title.getTitle() != null && title.getTitle().length() > 0);
}
}