add high quality image updater to downloads dialog.

master
Lodici 2014-08-16 11:22:28 +01:00
parent 71a86d2e64
commit e0ff27f606
10 changed files with 405 additions and 65 deletions

View File

@ -402,7 +402,7 @@ public class CardDefinitions {
return (missingCards == null ? false : missingCards.containsKey(key));
}
public static Collection<MagicCardDefinition> getMissingCards() {
public static synchronized Collection<MagicCardDefinition> getMissingCards() {
if (missingCards == null) {
try {
loadMissingCards(getMissingCardNames());

View File

@ -1,26 +1,23 @@
package magic.data;
import magic.model.MagicCardDefinition;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import magic.model.MagicCardDefinition;
import magic.utility.MagicFiles;
public class DownloadImageFile extends WebDownloader {
private final File file;
private File file;
private final URL url;
private final MagicCardDefinition cdef;
private final String cardName;
DownloadImageFile(final File file, final URL url) {
this(file, url, MagicCardDefinition.UNKNOWN);
}
DownloadImageFile(final File file, final URL url, final MagicCardDefinition cdef) {
this.file=file;
this.url=url;
this.cdef=cdef;
public DownloadImageFile(final MagicCardDefinition cdef) throws MalformedURLException {
file = MagicFiles.getCardImageFile(cdef);
url = new URL(cdef.getImageURL());
cardName = cdef.getName();
}
@Override
@ -35,20 +32,19 @@ public class DownloadImageFile extends WebDownloader {
@Override
public void download(final Proxy proxy) throws IOException {
if (!filenamePrefix.isEmpty()) {
file = new File(file.getParent(), filenamePrefix + file.getName());
}
WebDownloader.downloadToFile(proxy, url, file);
}
@Override
public boolean exists() {
return file.exists() && file.length() != 0L && !cdef.isIgnored(file.length());
public String getCardName() {
return cardName;
}
public String getCardName() {
if (cdef != null) {
return cdef.getName();
} else {
return "";
}
@Override
public URL getDownloadUrl() {
return url;
}
}

View File

@ -1,7 +1,5 @@
package magic.data;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -11,28 +9,20 @@ import magic.model.MagicCardDefinition;
@SuppressWarnings("serial")
public class MissingImages extends ArrayList<WebDownloader> {
private final Collection<MagicCardDefinition> cards;
public MissingImages(final Collection<MagicCardDefinition> cards) {
this.cards = cards;
loadDownloadImageFiles();
loadDownloadImageFiles(cards);
}
private void loadDownloadImageFiles() {
final File cardImagesPath = GeneralConfig.getInstance().getCardImagesPath().toFile();
final File cardsPathFile = new File(cardImagesPath, CardDefinitions.CARD_IMAGE_FOLDER);
if (!cardsPathFile.exists() && !cardsPathFile.mkdir()) {
System.err.println("WARNING. Unable to create " + cardsPathFile);
}
final File tokensPathFile = new File(cardImagesPath, CardDefinitions.TOKEN_IMAGE_FOLDER);
if (!tokensPathFile.exists() && !tokensPathFile.mkdir()) {
System.err.println("WARNING. Unable to create " + tokensPathFile);
}
for (final MagicCardDefinition cardDefinition : cards) {
process(cardDefinition, cardsPathFile, tokensPathFile);
private void loadDownloadImageFiles(final Collection<MagicCardDefinition> cards) {
for (final MagicCardDefinition card : cards) {
if (card.getImageURL() != null) {
try {
add(new DownloadImageFile(card));
} catch (final java.net.MalformedURLException ex) {
System.err.println("!!! Malformed URL " + card.getImageURL());
}
}
}
Collections.sort(this, new Comparator<WebDownloader>() {
@ -43,20 +33,5 @@ public class MissingImages extends ArrayList<WebDownloader> {
});
}
private void process(final MagicCardDefinition cardDefinition, final File cardsPathFile, final File tokensPathFile) {
final String imageURL = cardDefinition.getImageURL();
if (imageURL != null) {
final String imageFilename = cardDefinition.getImageName() + CardDefinitions.CARD_IMAGE_EXT;
final File imageFile = new File(cardDefinition.isToken() ? tokensPathFile : cardsPathFile, imageFilename);
try { //create URL
final WebDownloader dl = new DownloadImageFile(imageFile, new URL(imageURL), cardDefinition);
if (!dl.exists()) {
add(dl);
}
} catch (final java.net.MalformedURLException ex) {
System.err.println("ERROR! URL malformed " + imageURL);
}
}
}
}

View File

@ -19,7 +19,9 @@ public abstract class WebDownloader {
public abstract File getFile();
public abstract boolean exists();
public abstract URL getDownloadUrl();
protected String filenamePrefix = "";
public static void downloadToFile(final Proxy proxy, final URL url, final File file) throws IOException {
if (proxy != Proxy.NO_PROXY && proxy.type() != Proxy.Type.DIRECT) {
@ -47,6 +49,10 @@ public abstract class WebDownloader {
}
}
public void setFilenamePrefix(final String prefix) {
filenamePrefix = prefix;
}
public static String getHTML(final Proxy proxy, final URL url) {
InputStream inputStream = null;
BufferedReader dataStream = null;

View File

@ -28,6 +28,7 @@ import magic.ui.*;
import magic.ui.theme.Theme;
import magic.ui.theme.ThemeFactory;
import magic.ui.widget.FontsAndBorders;
import magic.ui.widget.downloader.HQImagesDownloadPanel;
import magic.ui.widget.downloader.ImageDownloadPanel;
import magic.ui.widget.downloader.ImageDownloadPanel.DownloaderState;
import magic.ui.widget.downloader.PlayableDownloadPanel;
@ -46,6 +47,7 @@ public class DownloadImagesDialog extends JDialog implements ActionListener, Pro
private ImageDownloadPanel playableDownloaderPanel;
private ImageDownloadPanel unimplementedDownloaderPanel;
private ImageDownloadPanel highQualityDownloaderPanel;
public DownloadImagesDialog(final MagicFrame frame) {
super(frame, true);
@ -100,9 +102,16 @@ public class DownloadImagesDialog extends JDialog implements ActionListener, Pro
final JPanel panel = new JPanel(new MigLayout("flowy, insets 8, gapy 10"));
panel.add(getPlayableDownloaderPanel(), "w 100%");
panel.add(getUnimplementedDownloaderPanel(), "w 100%");
panel.add(getHighQualityDownloaderPanel(), "w 100%");
return panel;
}
private ImageDownloadPanel getHighQualityDownloaderPanel() {
highQualityDownloaderPanel = new HQImagesDownloadPanel();
highQualityDownloaderPanel.addPropertyChangeListener("downloaderState", this);
return highQualityDownloaderPanel;
}
private ImageDownloadPanel getPlayableDownloaderPanel() {
playableDownloaderPanel = new PlayableDownloadPanel();
playableDownloaderPanel.addPropertyChangeListener("downloaderState", this);

View File

@ -0,0 +1,189 @@
package magic.ui.widget.downloader;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import magic.MagicUtility;
import magic.data.CardDefinitions;
import magic.data.WebDownloader;
import magic.model.MagicCardDefinition;
import magic.utility.MagicDownload;
import magic.utility.MagicFiles;
import org.apache.commons.io.FileUtils;
@SuppressWarnings("serial")
public class HQImagesDownloadPanel extends ImageDownloadPanel {
@Override
protected SwingWorker<Void, Integer> getImageDownloadWorker(final Proxy proxy) {
return new DownloadImagesWorker(CONFIG.getProxy());
}
@Override
protected String getProgressCaption() {
return "Low quality images = ";
}
@Override
protected Collection<MagicCardDefinition> getCards() {
assert !SwingUtilities.isEventDispatchThread();
final List<MagicCardDefinition> cards = new ArrayList<>();
for (final MagicCardDefinition cardDefinition : CardDefinitions.getCards()) {
if (cardDefinition.getImageURL() != null) {
final File imageFile = MagicFiles.getCardImageFile(cardDefinition);
if (imageFile.exists() && isLowQualityImage(imageFile)) {
cards.add(cardDefinition);
}
}
}
return cards;
}
private boolean isLowQualityImage(final File imageFile) {
Dimension imageSize = null;
try {
imageSize = getImageDimensions(imageFile);
return (imageSize.width < 480);
} catch (IOException | NullPointerException ex) {
System.err.println(imageFile.getName() + " (" + imageSize + ") : " + ex);
return false;
}
}
public static Dimension getImageDimensions(final File resourceFile) throws IOException {
try (final ImageInputStream in = ImageIO.createImageInputStream(resourceFile)) {
final Iterator<ImageReader> readers = ImageIO.getImageReaders(in);
if (readers.hasNext()) {
final ImageReader reader = readers.next();
try {
reader.setInput(in);
return new Dimension(reader.getWidth(0), reader.getHeight(0));
} finally {
reader.dispose();
}
}
}
return null;
}
@Override
protected String getLogFilename() {
return "hqimages.log";
}
@Override
protected String getDownloadButtonCaption() {
return "Check for new HQ images & download";
}
private class DownloadImagesWorker extends SwingWorker<Void, Integer> {
private final List<String> downloadedImages = new ArrayList<>();
private final Proxy proxy;
private volatile int imageSizeChangedCount = 0;
public DownloadImagesWorker(final Proxy proxy) {
this.proxy = proxy;
}
@Override
protected Void doInBackground() throws Exception {
int fileCount = 0;
int errorCount = 0;
final int MAX_DOWNLOAD_ERRORS = 10;
for (WebDownloader imageFile : files) {
final File localImageFile = imageFile.getFile();
final long localFileSize = imageFile.getFile().length();
final long remoteFileSize = MagicDownload.getDownloadableFileSize1(imageFile.getDownloadUrl());
// System.out.println(imageFile.getFilename() + " : R=" + remoteFileSize + ", L=" + localFileSize);
if (remoteFileSize != localFileSize) {
try {
// save downloaded image file with ~ prefix.
imageFile.setFilenamePrefix("~");
imageFile.download(proxy);
final File tempImageFile = imageFile.getFile();
final Dimension tempImageSize = getImageDimensions(tempImageFile);
final Dimension localImageSize = getImageDimensions(localImageFile);
if (!tempImageSize.equals(localImageSize)) {
// only interested in counting where image size changes because
// you can also get downloads where the file size has changed
// but the image size is still the same and in this context
// that means the image is still LQ so don't decrement the LQ
// count displayed in the progress bar.
imageSizeChangedCount++;
}
FileUtils.copyFile(tempImageFile, localImageFile);
tempImageFile.delete();
} catch (IOException ex) {
if (errorCount++ >= MAX_DOWNLOAD_ERRORS) {
throw new RuntimeException("Maximum download errors exceeded!", ex);
} else {
System.err.println("Image download failed : " + imageFile.getFilename() + " -> " + ex);
imageFile = null;
}
}
if (imageFile != null) {
downloadedImages.add(imageFile.getFilename());
}
}
fileCount++;
if (isCancelled()) {
break;
} else {
publish(new Integer(fileCount));
}
}
magic.data.HighQualityCardImagesProvider.getInstance().clearCache();
if (MagicUtility.isDevMode() && downloadedImages.size() > 0) {
saveDownloadLog(downloadedImages);
}
return null;
}
@Override
protected void done() {
try {
get();
} catch (InterruptedException | ExecutionException ex) {
throw new RuntimeException(ex);
} catch (CancellationException ex) {
// System.out.println("DownloadSwingWorker cancelled by user!");
} finally {
setButtonState(false);
resetProgressBar();
}
scanForMissingImages();
notifyStatusChanged(DownloaderState.STOPPED);
}
@Override
protected void process(List<Integer> chunks) {
final int countInteger = chunks.get(chunks.size() - 1);
if (!isCancelled()) {
progressBar.setValue(countInteger);
captionLabel.setText(getProgressCaption() + (files.size() - imageSizeChangedCount));
}
}
private void resetProgressBar() {
assert SwingUtilities.isEventDispatchThread();
progressBar.setValue(0);
progressBar.setString(null);
}
}
}

View File

@ -1,9 +1,12 @@
package magic.ui.widget.downloader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.SwingUtilities;
import magic.data.CardDefinitions;
import magic.model.MagicCardDefinition;
import magic.utility.MagicFiles;
@SuppressWarnings("serial")
public class PlayableDownloadPanel extends MissingImagesDownloadPanel {
@ -16,7 +19,15 @@ public class PlayableDownloadPanel extends MissingImagesDownloadPanel {
@Override
protected Collection<MagicCardDefinition> getCards() {
assert !SwingUtilities.isEventDispatchThread();
return CardDefinitions.getCards();
final List<MagicCardDefinition> cards = new ArrayList<>();
for (final MagicCardDefinition card : CardDefinitions.getCards()) {
if (card.getImageURL() != null) {
if (!MagicFiles.getCardImageFile(card).exists()) {
cards.add(card);
}
}
}
return cards;
}
@Override

View File

@ -1,9 +1,12 @@
package magic.ui.widget.downloader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.SwingUtilities;
import magic.data.CardDefinitions;
import magic.model.MagicCardDefinition;
import magic.utility.MagicFiles;
@SuppressWarnings("serial")
public class UnimplementedDownloadPanel extends MissingImagesDownloadPanel {
@ -16,7 +19,15 @@ public class UnimplementedDownloadPanel extends MissingImagesDownloadPanel {
@Override
protected Collection<MagicCardDefinition> getCards() {
assert !SwingUtilities.isEventDispatchThread();
return CardDefinitions.getMissingCards();
final List<MagicCardDefinition> cards = new ArrayList<>();
for (final MagicCardDefinition card : CardDefinitions.getMissingCards()) {
if (card.getImageURL() != null) {
if (!MagicFiles.getCardImageFile(card).exists()) {
cards.add(card);
}
}
}
return cards;
}
@Override
@ -28,4 +39,5 @@ public class UnimplementedDownloadPanel extends MissingImagesDownloadPanel {
protected String getDownloadButtonCaption() {
return "Download new images";
}
}

View File

@ -0,0 +1,95 @@
package magic.utility;
import java.awt.Dimension;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
/**
* Utility class for useful or common file-system related tasks.
*
*/
public final class MagicDownload {
private MagicDownload() {}
public static long getDownloadableFileSize1(final URL downloadUrl) {
long cLength = -1;
try {
URLConnection conn = downloadUrl.openConnection();
cLength = conn.getContentLengthLong();
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
return cLength;
}
public static long getDownloadableFileSize1(final String urlString) {
try {
return getDownloadableFileSize1(new URL(urlString));
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
}
public static long getDownloadableFileSize2(String fileUrl) {
URL oracle;
try {
oracle = new URL(fileUrl);
} catch (MalformedURLException ex) {
throw new RuntimeException(ex);
}
HttpURLConnection yc;
try {
yc = (HttpURLConnection) oracle.openConnection();
populateDesktopHttpHeaders(yc);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
long fileSize = 0;
try {
// retrieve file size from Content-Length header field
fileSize = Long.parseLong(yc.getHeaderField("Content-Length"));
} catch (NumberFormatException nfe) {
}
return fileSize;
}
private static void populateDesktopHttpHeaders(URLConnection urlCon) {
// add custom header in order to be easily detected
urlCon.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:25.0) Gecko/20100101 Firefox/25.0");
urlCon.setRequestProperty("Accept-Language",
"el-gr,el;q=0.8,en-us;q=0.5,en;q=0.3");
urlCon.setRequestProperty("Accept-Charset",
"ISO-8859-7,utf-8;q=0.7,*;q=0.7");
}
public static Dimension getImageDimensions(final File resourceFile) throws IOException {
try (final ImageInputStream in = ImageIO.createImageInputStream(resourceFile)) {
final Iterator<ImageReader> readers = ImageIO.getImageReaders(in);
if (readers.hasNext()) {
final ImageReader reader = readers.next();
try {
reader.setInput(in);
return new Dimension(reader.getWidth(0), reader.getHeight(0));
} finally {
reader.dispose();
}
}
}
return null;
}
}

View File

@ -13,11 +13,12 @@ import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import javax.swing.JOptionPane;
import magic.MagicMain;
import magic.MagicUtility;
import magic.data.CardImagesProvider;
import magic.data.GeneralConfig;
import magic.model.MagicCardDefinition;
/**
* Utility class for useful or common file-system related tasks.
@ -26,6 +27,52 @@ import magic.MagicUtility;
public final class MagicFiles {
private MagicFiles() {}
private static final String CARD_IMAGE_EXT = CardImagesProvider.IMAGE_EXTENSION;
private enum ImagesPath {
CARDS("cards"),
TOKENS("tokens");
private final GeneralConfig CONFIG = GeneralConfig.getInstance();
private final String directoryName;
private ImagesPath(final String directoryName) {
this.directoryName = directoryName;
}
public Path getPath() {
return CONFIG.getCardImagesPath().resolve(directoryName);
}
}
private static Path getImagesPath(final ImagesPath imageType) {
return imageType.getPath();
}
private static String getImageFilename(final MagicCardDefinition card, final int index) {
final int imageIndex = index % card.getImageCount();
final String indexPostfix = imageIndex > 0 ? String.valueOf(imageIndex + 1) : "";
return card.getImageName() + indexPostfix + CARD_IMAGE_EXT;
}
/**
* Returns a File object representing the given card's image file.
*/
public static File getCardImageFile(final MagicCardDefinition card, final int index) {
final Path imageDirectory = card.isToken() ?
getImagesPath(ImagesPath.TOKENS) :
getImagesPath(ImagesPath.CARDS);
return new File(imageDirectory.toFile(), getImageFilename(card, index));
}
/**
* Returns a File object representing the given card's image file.
*/
public static File getCardImageFile(final MagicCardDefinition card) {
return getCardImageFile(card, 0);
}
/**
* Deletes all directory contents and then directory itself.
*/