package moviescraper.doctord.view;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import org.apache.commons.lang3.StringUtils;
import moviescraper.doctord.controller.amalgamation.AllAmalgamationOrderingPreferences;
import moviescraper.doctord.controller.amalgamation.DataItemSourceAmalgamationPreference;
import moviescraper.doctord.controller.amalgamation.MovieScrapeResultGroup;
import moviescraper.doctord.controller.amalgamation.ScrapeAmalgamatedMovieWorker;
import moviescraper.doctord.controller.amalgamation.ScrapeAmalgamatedMovieWorker.ScrapeAmalgamatedMovieWorkerProperty;
import moviescraper.doctord.controller.amalgamation.ScraperGroupAmalgamationPreference;
import moviescraper.doctord.controller.siteparsingprofile.SiteParsingProfile;
import moviescraper.doctord.controller.siteparsingprofile.SiteParsingProfile.ScraperGroupName;
import moviescraper.doctord.controller.siteparsingprofile.specific.Data18MovieParsingProfile;
import moviescraper.doctord.controller.siteparsingprofile.specific.TheMovieDatabaseParsingProfile;
import moviescraper.doctord.model.Movie;
import moviescraper.doctord.model.SearchResult;
import moviescraper.doctord.model.dataitem.DataItemSource;
import moviescraper.doctord.model.dataitem.Thumb;
import moviescraper.doctord.model.preferences.MoviescraperPreferences;
import moviescraper.doctord.view.CustomComponents.AsyncImageComponent;
public class ScrapeAmalgamatedProgressDialog extends JDialog implements Runnable{
private static final long serialVersionUID = 1L;
//FileDetailPanel fileDetailPanel;
private List<Movie> currentMovieList;
private Movie currentAmalgamatedMovie;
//ScraperGroupAmalgamationPreference amalgamationPreferences;
private GUIMain guiMain;
private JButton cancelButton;
private JProgressBar progressBar;
private ScrapeAmalgamatedMovieWorker worker;
int currentFileIndexToScrape = 0;
private static final int maxLettersToDisplayOfFileName = 80;
private AmalgamationPropertyChangeListener propertyListener;
private ScraperGroupAmalgamationPreference scraperGroupAmalgamationPreference;
private List<ScraperProgressView> scraperProgressViews;
AllAmalgamationOrderingPreferences allAmalgamationOrderingPreferences;
private List<File> filesWeAreScraping;
private JLabel fileBeingScrapedLabel;
private boolean showPosterPicker;
private boolean showFanartPicker;
//private final PropertyChangeSupport propertyChangeSupport;
public static void main(String [] args)
{
DataItemSourceAmalgamationPreference overallOrdering = new DataItemSourceAmalgamationPreference(
new TheMovieDatabaseParsingProfile(), new Data18MovieParsingProfile());
ScraperGroupAmalgamationPreference amalgamationPreferences = new ScraperGroupAmalgamationPreference(
ScraperGroupName.AMERICAN_ADULT_DVD_SCRAPER_GROUP,
overallOrdering);
GUIMain guiMain = new GUIMain();
ScrapeAmalgamatedProgressDialog action = new ScrapeAmalgamatedProgressDialog(guiMain, guiMain.getAllAmalgamationOrderingPreferences(), amalgamationPreferences);
// schedule this for the event dispatch thread (edt)
SwingUtilities.invokeLater(action);
}
public void setFilesBeingScrapedLabel() {
if (fileBeingScrapedLabel == null)
fileBeingScrapedLabel = new JLabel();
if (filesWeAreScraping != null && filesWeAreScraping.size() > 0) {
File currentFile = filesWeAreScraping.get(currentFileIndexToScrape);
if (currentFile != null) {
String truncatedName = StringUtils.abbreviate(currentFile.getName(), maxLettersToDisplayOfFileName);
fileBeingScrapedLabel.setText("<html>Scraping: <b>" + truncatedName + "</b> ("
+ (currentFileIndexToScrape + 1) + "/" + filesWeAreScraping.size() + ")" + "</html>");
}
} else {
fileBeingScrapedLabel.setText("No files selected.");
}
}
public void cancelRunningScraper(SiteParsingProfile scraper)
{
if(worker != null)
{
worker.cancelRunningScraper(scraper);
}
}
public void reinitializeScrapingForNextMovie()
{
currentAmalgamatedMovie = null;
currentMovieList = new LinkedList<>();
for(ScraperProgressView currentScraperProgressView : scraperProgressViews)
{
currentScraperProgressView.resetPanelForNextScrape();
}
setFilesBeingScrapedLabel();
progressBar.setValue(0);
}
public ScrapeAmalgamatedProgressDialog(GUIMain guiMain, AllAmalgamationOrderingPreferences allAmalgamationOrderingPreferences, ScraperGroupAmalgamationPreference scraperGroupAmalgamationPreference)
{
super(guiMain.getFrmMoviescraper());
setTitle("Scraping...");
this.guiMain = guiMain;
this.filesWeAreScraping = guiMain.getCurrentlySelectedMovieFileList();
this.allAmalgamationOrderingPreferences = allAmalgamationOrderingPreferences;
scraperProgressViews = new LinkedList<>();
showPosterPicker = MoviescraperPreferences.getInstance().getSelectArtManuallyWhenScraping();
showFanartPicker = MoviescraperPreferences.getInstance().getSelectArtManuallyWhenScraping();
this.scraperGroupAmalgamationPreference = scraperGroupAmalgamationPreference;
currentMovieList = new LinkedList<>();
propertyListener = new AmalgamationPropertyChangeListener();
JPanel overallPanel = new JPanel(new BorderLayout());
this.setLocationRelativeTo(guiMain.getFrmMoviescraper());
this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
this.add(overallPanel);
progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
progressBar.setStringPainted(true);
cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if(worker != null)
{
worker.cancelAllRunningScrapers();
if(ScrapeAmalgamatedProgressDialog.this.guiMain != null)
{
ScrapeAmalgamatedProgressDialog.this.guiMain.setMainGUIEnabled(true);
}
ScrapeAmalgamatedProgressDialog.this.dispose();
}
}
});
fileBeingScrapedLabel = new JLabel();
setFilesBeingScrapedLabel();
overallPanel.add(fileBeingScrapedLabel, BorderLayout.NORTH);
JPanel southPanel = new JPanel(new BorderLayout());
southPanel.add(cancelButton, BorderLayout.SOUTH);
southPanel.add(progressBar, BorderLayout.CENTER);
overallPanel.add(southPanel, BorderLayout.SOUTH);
this.setVisible(true);
if (filesAreSelected()) {
//start scraping the first item in the selected file list, further items will be scraped once this one completes and a "AllScrapesFinished" message is received
scrapeWithIndex(currentFileIndexToScrape);
} else {
System.err.println("No file selected!");
}
JPanel individualScrapeProgressPanel = createScraperProgressPanel();
overallPanel.add(individualScrapeProgressPanel, BorderLayout.CENTER);
this.pack();
}
private JPanel createScraperProgressPanel() {
JPanel scraperProgressPanel = new JPanel();
scraperProgressPanel.setLayout(new BoxLayout(scraperProgressPanel, BoxLayout.Y_AXIS));
List<DataItemSource> activeScrapers = scraperGroupAmalgamationPreference.getActiveScrapersUsedInOverallPreference();
//update the state of what's active because it may have changed from elsewhere
for(DataItemSource currentScraper : activeScrapers)
{
if(currentScraper != null && currentScraper instanceof SiteParsingProfile && shouldScrapeThread(currentScraper))
{
ScraperProgressView currentProgressView = new ScraperProgressView(currentScraper, this);
scraperProgressPanel.add(currentProgressView);
this.addPropertyChangeListener(currentProgressView.getScraperProgressPropertyChangeListener());
scraperProgressViews.add(currentProgressView);
}
}
return scraperProgressPanel;
}
private boolean shouldScrapeThread(DataItemSource parsingProfile) {
//Default group used for single scraper operations
if(scraperGroupAmalgamationPreference.getScraperGroupName().equals(ScraperGroupName.DEFAULT_SCRAPER_GROUP))
{
return true;
}
for (ScraperGroupName currentName : ScraperGroupName.values()) {
ScraperGroupAmalgamationPreference currentPref = allAmalgamationOrderingPreferences
.getScraperGroupAmalgamationPreference(currentName);
LinkedList<DataItemSource> overallPrefs = currentPref
.getOverallAmalgamationPreference()
.getAmalgamationPreferenceOrder();
for(DataItemSource currentDataItemSource : overallPrefs)
{
if(currentDataItemSource.getDataItemSourceName().equals(parsingProfile.getDataItemSourceName()))
{
boolean disabled = currentDataItemSource.isDisabled();
return !disabled;
}
}
}
return false;
}
public void scrapeWithIndex(int index)
{
//TODO: I may not need to do all this reconstruction and removing listener stuff
//i added this in to fix a bug, but I think this was unrelated and can be cleaned up
//it would require some pretty extensive testing though, so i'm leaving this in to be safe for now
if(worker != null)
{
worker.cancel(true);
if(propertyListener != null)
worker.removePropertyChangeListener(propertyListener);
}
worker = new ScrapeAmalgamatedMovieWorker(allAmalgamationOrderingPreferences, scraperGroupAmalgamationPreference,
guiMain.getCurrentlySelectedMovieFileList().get(currentFileIndexToScrape), this);
propertyListener = new AmalgamationPropertyChangeListener();
worker.addPropertyChangeListener(propertyListener);
worker.execute();
}
/**
* Scrapes the next item (returns true) or returns false if there is no next item to scrape
* @return true if we scraped an item, false if we didn't
*/
public boolean scrapeNextItemIfNeeded()
{
boolean considerUserSelectionOneURLWhenScraping = MoviescraperPreferences.getInstance().getConsiderUserSelectionOneURLWhenScraping();
if (considerUserSelectionOneURLWhenScraping){
for (int i = 1; i < guiMain.getCurrentlySelectedMovieFileList().size(); i++){
if(currentAmalgamatedMovie != null)
{
guiMain.movieToWriteToDiskList.add(currentAmalgamatedMovie);
}
}
currentFileIndexToScrape = guiMain.getCurrentlySelectedMovieFileList().size();
}
if(filesAreSelected() && (currentFileIndexToScrape+1) < guiMain.getCurrentlySelectedMovieFileList().size())
{
currentFileIndexToScrape++;
reinitializeScrapingForNextMovie();
scrapeWithIndex(currentFileIndexToScrape);
return true;
}
return false;
}
public boolean filesAreSelected()
{
if(guiMain != null && guiMain.getCurrentlySelectedMovieFileList() != null
&& guiMain.getCurrentlySelectedMovieFileList().size() > 0)
return true;
else return false;
}
@Override
public void run(){}
private class AmalgamationPropertyChangeListener implements PropertyChangeListener
{
@Override
public void propertyChange(PropertyChangeEvent evt) {
firePropertyChange(evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
if(evt.getPropertyName().equals("progress"))
{
int progressAmount = (int)evt.getNewValue();
progressBar.setValue(progressAmount);
}
if(evt.getPropertyName().equals(ScrapeAmalgamatedMovieWorkerProperty.ALL_SCRAPES_FINISHED.toString()))
{
if(currentAmalgamatedMovie == null)
{
guiMain.movieToWriteToDiskList.add(currentAmalgamatedMovie);
}
else if(guiMain != null)
{
promptUserToPickPoster();
promptUserToPickFanart();
guiMain.movieToWriteToDiskList.add(currentAmalgamatedMovie);
guiMain.getFileDetailPanel().setNewMovie(currentAmalgamatedMovie, true, false);
}
//scrape the next selected item
boolean weAreDone = !scrapeNextItemIfNeeded();
if(weAreDone)
{
//renable the main gui and close our scraping dialog
if(guiMain != null)
{
guiMain.getFrmMoviescraper().setCursor(Cursor.getDefaultCursor());
ScrapeAmalgamatedProgressDialog.this.guiMain.setMainGUIEnabled(true);
ScrapeAmalgamatedProgressDialog.this.dispose();
}
}
}
if(evt.getPropertyName().equals(ScrapeAmalgamatedMovieWorkerProperty.SCRAPED_MOVIE.toString()))
{
@SuppressWarnings("unchecked")
List<Map<SiteParsingProfile,Movie>> newValue = (List<Map<SiteParsingProfile,Movie>>) evt.getNewValue();
newValue.removeAll(Collections.singleton(null));
for(Map<SiteParsingProfile, Movie> currentMap : newValue)
{
List<Movie> currentMovies = new ArrayList<>(currentMap.values());
currentMovies.removeAll(Collections.singleton(null));
currentMovieList.addAll(currentMovies);
}
updateAmalgamatedMovie();
}
}
}
public void updateAmalgamatedMovie() {
if(currentMovieList != null && currentMovieList.size() > 0)
{
//we construct a new one instead of using our local variable to get around bug where changes to amalgamation settings were not getting picked up until program restart
ScraperGroupAmalgamationPreference prefToUse = guiMain.getAllAmalgamationOrderingPreferences().getScraperGroupAmalgamationPreference(scraperGroupAmalgamationPreference.getScraperGroupName());
MovieScrapeResultGroup scrapedResultGroup = new MovieScrapeResultGroup(currentMovieList, prefToUse);
currentAmalgamatedMovie = scrapedResultGroup.amalgamateMovie();
}
}
/**
* Displays a dialog where the user can pick the poster to use. Only shows when there is more than one poster.
*/
private void promptUserToPickPoster() {
if (showPosterPicker && currentAmalgamatedMovie != null && currentAmalgamatedMovie.getPosters() != null
&& currentAmalgamatedMovie.getPosters().length > 1) {
Thumb posterFromUserSelection = showArtPicker(currentAmalgamatedMovie.getPosters(),
"Pick Poster",true);
currentAmalgamatedMovie.moveExistingPosterToFront(posterFromUserSelection);
}
}
/**
* Displays a dialog where the user can pick the fanart to use. Only shows when there is more than one fanart.
*/
private void promptUserToPickFanart() {
if (showFanartPicker && currentAmalgamatedMovie != null && currentAmalgamatedMovie.getFanart() != null
&& currentAmalgamatedMovie.getFanart().length > 1) {
Thumb fanartFromUserSelection = showArtPicker(currentAmalgamatedMovie.getFanart(),
"Pick Fanart",false);
currentAmalgamatedMovie.moveExistingFanartToFront(fanartFromUserSelection);
}
}
public static Thumb showArtPicker(Thumb [] thumbArray, String windowTitle, boolean isForPoster)
{
if(thumbArray.length > 0)
{
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JPanel thumbPane = new JPanel(new ModifiedFlowLayout());
AsyncImageComponent[] thumbPanels = new AsyncImageComponent[thumbArray.length];
boolean doAutoSelect = true;
for(int i = 0; i < thumbArray.length; i++)
{
thumbPanels[i] = new AsyncImageComponent(thumbArray[i],false, thumbPanels, doAutoSelect, isForPoster, true);
thumbPane.add(thumbPanels[i]);
}
JScrollPane thumbScroller = new JScrollPane(thumbPane);
panel.add(thumbScroller, BorderLayout.CENTER);
panel.setPreferredSize(new Dimension(325,600));
final JDialog bwin = new JDialog();
bwin.addWindowFocusListener(new WindowFocusListener()
{
@Override
public void windowLostFocus(WindowEvent e)
{
bwin.setVisible(false);
bwin.dispose();
}
@Override
public void windowGainedFocus(WindowEvent e)
{
}
});
bwin.add(panel);
bwin.pack();
int result = JOptionPane.showOptionDialog(null, panel, windowTitle,
JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE,
null, null, null);
if(result == JOptionPane.OK_OPTION)
{
//get the selected item's thumb
Thumb optionPickedFromPanel = null;
for(int i = 0; i < thumbPanels.length; i++)
{
if(thumbPanels[i].isSelected())
{
optionPickedFromPanel = thumbPanels[i].getThumb();
System.out.println("returning option picked from panel = " + optionPickedFromPanel);
return optionPickedFromPanel;
}
}
}
else return null;
}
return null;
}
public static boolean showPromptForUserProvidedURL(SiteParsingProfile siteScraper, File fileToScrape)
{
boolean promptUserForURLWhenScraping = MoviescraperPreferences.getInstance().getPromptForUserProvidedURLWhenScraping();
boolean chooseSearchResult = MoviescraperPreferences.getInstance().getSelectSearchResultManuallyWhenScraping();
boolean wasCustomURLSet = false;
if(promptUserForURLWhenScraping)
{
System.out.println("Prompting the user for a url for " + siteScraper);
String userProvidedURL = (String)JOptionPane.showInputDialog(
null,
"Enter URL of " + siteScraper.toString() + " to scrape from:",
"Scrape from this URL...",
JOptionPane.PLAIN_MESSAGE,
null,
null,
null);
if(userProvidedURL != null && userProvidedURL.length() > 0)
{
//TODO: validate this is a actually a URL and display an error message if it is not
//also maybe don't let them click OK if isn't a valid URL?
try{
URL isAValidURL = new URL(userProvidedURL);
siteScraper.setOverridenSearchResult(isAValidURL.toString());
wasCustomURLSet = true;
}
catch(MalformedURLException e)
{
e.printStackTrace();
}
}
}
//if the user doesn't choose their URL manually and they have the preference set to allow picking of search results,
//give the user the ability to pick one
if(chooseSearchResult && !wasCustomURLSet)
{
System.out.println("Prompt user for search results for " + siteScraper);
String searchString = siteScraper
.createSearchString(fileToScrape);
try {
SearchResult[] searchResults = siteScraper.getSearchResults(searchString);
if(searchResults != null && searchResults.length > 0)
{
SearchResult searchResultFromUser = showSearchResultPicker(searchResults,
siteScraper.getDataItemSourceName());
if(searchResultFromUser != null)
{
siteScraper.setOverridenSearchResult(searchResultFromUser.getUrlPath());
wasCustomURLSet = true;
}
else
{
//User hit cancel, do not scrape from this scraper
System.out.println("Discarding results from " + siteScraper.getDataItemSourceName());
siteScraper.setDiscardResults(true);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return wasCustomURLSet;
}
/**
* Display a list of option dialog where the user can choose between various search results
* @param searchResults - search results to display
* @param siteName - Title to show in window
* @return - the search result the user picks or null if the user picks the cancel option
*/
public static SearchResult showSearchResultPicker(SearchResult [] searchResults, String siteName)
{
if(searchResults.length > 0)
{
SelectionDialog selectionDialog = new SelectionDialog(searchResults, siteName);
Object[] choices = {"OK", "Skip Scraping From This Site"};
Object defaultChoice = choices[0];
int optionPicked = JOptionPane.showOptionDialog(null, selectionDialog, "Select Search Result for " + siteName,
JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE,
null, choices, defaultChoice);
if(optionPicked == JOptionPane.CANCEL_OPTION)
return null;
return selectionDialog.getSelectedValue();
}
else return null;
}
}