mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
download: reworked connection:
- added shareable code with default proxy, headers and other settings for download tasks like images, symbols, mtgjson, etc; - use XmageURLConnection.downloadText for text resources - use XmageURLConnection.downloadBinary for any file resources - added user agent with app version for all requests; - added http logs and improved error messages;
This commit is contained in:
parent
fad63389d0
commit
e1cffbde40
35 changed files with 713 additions and 464 deletions
|
|
@ -46,7 +46,7 @@ import mage.remote.Connection;
|
|||
import mage.remote.Connection.ProxyType;
|
||||
import mage.util.DebugUtil;
|
||||
import mage.util.ThreadUtils;
|
||||
import mage.util.XMageThreadFactory;
|
||||
import mage.util.XmageThreadFactory;
|
||||
import mage.utils.MageVersion;
|
||||
import mage.view.GameEndView;
|
||||
import mage.view.UserRequestMessage;
|
||||
|
|
@ -134,7 +134,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
private static final MageUI UI = new MageUI();
|
||||
|
||||
private static final ScheduledExecutorService PING_SENDER_EXECUTOR = Executors.newSingleThreadScheduledExecutor(
|
||||
new XMageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_PING_SENDER)
|
||||
new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_PING_SENDER)
|
||||
);
|
||||
private static UpdateMemUsageTask updateMemUsageTask;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import mage.game.GameException;
|
|||
import mage.remote.Session;
|
||||
import mage.util.DeckUtil;
|
||||
import mage.util.ThreadUtils;
|
||||
import mage.util.XMageThreadFactory;
|
||||
import mage.util.XmageThreadFactory;
|
||||
import mage.view.CardView;
|
||||
import mage.view.SimpleCardView;
|
||||
import org.apache.log4j.Logger;
|
||||
|
|
@ -1496,7 +1496,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
|
|||
private void btnSubmitTimerActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSubmitTimerActionPerformed
|
||||
|
||||
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(
|
||||
new XMageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_SUBMIT_TIMER)
|
||||
new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_SUBMIT_TIMER)
|
||||
);
|
||||
timeToSubmit = 60;
|
||||
this.btnSubmitTimer.setEnabled(false);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import mage.client.util.gui.ArrowBuilder;
|
|||
import mage.constants.PlayerAction;
|
||||
import mage.constants.TurnPhase;
|
||||
import mage.util.ThreadUtils;
|
||||
import mage.util.XMageThreadFactory;
|
||||
import mage.util.XmageThreadFactory;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import javax.swing.*;
|
||||
|
|
@ -45,7 +45,7 @@ public class FeedbackPanel extends javax.swing.JPanel {
|
|||
|
||||
private static final int AUTO_CLOSE_END_DIALOG_TIMEOUT_SECS = 8;
|
||||
private static final ScheduledExecutorService AUTO_CLOSE_EXECUTOR = Executors.newSingleThreadScheduledExecutor(
|
||||
new XMageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_AUTO_CLOSE_TIMER)
|
||||
new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_AUTO_CLOSE_TIMER)
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,294 @@
|
|||
package mage.client.remote;
|
||||
|
||||
import mage.client.dialog.PreferencesDialog;
|
||||
import mage.remote.Connection;
|
||||
import mage.utils.MageVersion;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Network: proxy class to set up and use network connections like URLConnection
|
||||
* <p>
|
||||
* It used stream logic for data access
|
||||
* <p>
|
||||
* For text:
|
||||
* - download text data by XmageURLConnection.downloadText
|
||||
* <p>
|
||||
* For binary data (e.g. file download):
|
||||
* - get stream by XmageURLConnection.downloadBinary
|
||||
* - save stream to file or process
|
||||
* <p>
|
||||
* TODO: no needs in POST requests (support only GET), but can be added later for another third party APIs
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class XmageURLConnection {
|
||||
|
||||
private static final MageVersion version = new MageVersion(XmageURLConnection.class);
|
||||
private static final Logger logger = Logger.getLogger(XmageURLConnection.class);
|
||||
|
||||
private static final int CONNECTION_STARTING_TIMEOUT_MS = 10000;
|
||||
private static final int CONNECTION_READING_TIMEOUT_MS = 60000;
|
||||
|
||||
final String url;
|
||||
Proxy proxy = null;
|
||||
HttpURLConnection connection = null;
|
||||
HttpLoggingType loggingType = HttpLoggingType.ERRORS;
|
||||
|
||||
public XmageURLConnection(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
// example: 404 Not Found xxx
|
||||
enum HttpLoggingType {
|
||||
NONE,
|
||||
ERRORS,
|
||||
ALL
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional headers like non standard user agent, etc
|
||||
*/
|
||||
public void setRequestHeaders(Map<String, String> additionalHeaders) {
|
||||
makeSureConnectionStarted();
|
||||
|
||||
for (String key : additionalHeaders.keySet()) {
|
||||
this.connection.setRequestProperty(key, additionalHeaders.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*/
|
||||
public void startConnection() {
|
||||
initDefaultProxy();
|
||||
|
||||
try {
|
||||
URL url = new URL(this.url);
|
||||
|
||||
// proxy settings
|
||||
if (this.proxy != null) {
|
||||
this.connection = (HttpURLConnection) url.openConnection(this.proxy);
|
||||
} else {
|
||||
this.connection = (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
|
||||
// additional settings
|
||||
this.connection.setConnectTimeout(CONNECTION_STARTING_TIMEOUT_MS);
|
||||
this.connection.setReadTimeout(CONNECTION_READING_TIMEOUT_MS);
|
||||
|
||||
initDefaultHeaders();
|
||||
} catch (IOException e) {
|
||||
this.connection = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void initDefaultProxy() {
|
||||
Connection.ProxyType configProxyType = Connection.ProxyType.valueByText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_TYPE, "None"));
|
||||
Proxy.Type type;
|
||||
switch (configProxyType) {
|
||||
case HTTP:
|
||||
type = Proxy.Type.HTTP;
|
||||
break;
|
||||
case SOCKS:
|
||||
type = Proxy.Type.SOCKS;
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
type = Proxy.Type.DIRECT;
|
||||
break;
|
||||
}
|
||||
|
||||
this.proxy = Proxy.NO_PROXY;
|
||||
if (type != Proxy.Type.DIRECT) {
|
||||
try {
|
||||
String address = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_ADDRESS, "");
|
||||
int port = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_PORT, "80"));
|
||||
this.proxy = new Proxy(type, new InetSocketAddress(address, port));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Network: can't create proxy, check your settings or reset it - " + e, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initDefaultHeaders() {
|
||||
// warning, do not add Accept-Encoding - it processing inside URLConnection for http/https links (trying to use gzip by default)
|
||||
|
||||
// user agent due standard notation User-Agent: <product> / <product-version> <comment>
|
||||
// warning, dot not add os, language and other details
|
||||
this.connection.setRequestProperty("User-Agent", String.format("XMage/%s build: %s",
|
||||
version.toString(false), version.getBuildTime()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to server's resource
|
||||
*/
|
||||
public void connect() throws IOException {
|
||||
makeSureConnectionStarted();
|
||||
|
||||
this.connection.connect();
|
||||
printHttpResult();
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return this.connection != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get http status code from a web server (200 for ok, -1 for not connect, 400 and other for errors)
|
||||
*/
|
||||
public int getResponseCode() {
|
||||
makeSureConnectionStarted();
|
||||
|
||||
try {
|
||||
return this.connection.getResponseCode();
|
||||
} catch (IOException ignore) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get total file size to download (call it after start connection)
|
||||
* -1 for unknown size
|
||||
* 0 for text or small files
|
||||
* <p>
|
||||
* Warning, result depends on Accept-Encoding, so use it for information only
|
||||
*/
|
||||
public int getContentLength() {
|
||||
makeSureConnectionStarted();
|
||||
|
||||
return this.connection.getContentLength();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get http status message from a web server like Not Found
|
||||
*/
|
||||
public String getResponseMessage() {
|
||||
makeSureConnectionStarted();
|
||||
|
||||
try {
|
||||
return this.connection.getResponseMessage();
|
||||
} catch (IOException ignore) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get returned html from a web server
|
||||
*/
|
||||
public String getErrorResponseAsString() {
|
||||
makeSureConnectionStarted();
|
||||
|
||||
java.util.Scanner s = new java.util.Scanner(this.connection.getErrorStream()).useDelimiter("\\A");
|
||||
return s.hasNext() ? s.next() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get non-text data as stream, e.g. binary file content
|
||||
*/
|
||||
public InputStream getGoodResponseAsStream() throws IOException {
|
||||
makeSureConnectionStarted();
|
||||
|
||||
return this.connection.getInputStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get text data as string, e.g. html document
|
||||
*/
|
||||
public String getGoodResponseAsString() {
|
||||
makeSureConnectionStarted();
|
||||
StringBuffer tmp = new StringBuffer();
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(this.getGoodResponseAsStream()));
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
tmp.append(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Network: can't get text data from " + this.url + " - " + e, e);
|
||||
}
|
||||
|
||||
return String.valueOf(tmp);
|
||||
}
|
||||
|
||||
private void makeSureConnectionStarted() {
|
||||
if (!isConnected()) {
|
||||
throw new IllegalArgumentException("Wrong code usage: must call startConnection first", new Throwable());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast download of text data
|
||||
*
|
||||
* @return downloaded text on OK 200 response or empty on any other errors
|
||||
*/
|
||||
public static String downloadText(String resourceUrl) {
|
||||
XmageURLConnection con = new XmageURLConnection(resourceUrl);
|
||||
con.startConnection();
|
||||
if (con.isConnected()) {
|
||||
try {
|
||||
con.connect();
|
||||
if (con.getResponseCode() == 200) {
|
||||
return con.getGoodResponseAsString();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast download of binary data
|
||||
*
|
||||
* @return stream on OK 200 response or null on any other errors
|
||||
*/
|
||||
public static InputStream downloadBinary(String resourceUrl) {
|
||||
XmageURLConnection con = new XmageURLConnection(resourceUrl);
|
||||
con.startConnection();
|
||||
if (con.isConnected()) {
|
||||
try {
|
||||
con.connect();
|
||||
if (con.getResponseCode() == 200) {
|
||||
return con.getGoodResponseAsStream();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error(e, e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void printHttpResult() {
|
||||
if (this.connection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean needPrint;
|
||||
switch (this.loggingType) {
|
||||
case NONE:
|
||||
needPrint = false;
|
||||
break;
|
||||
case ERRORS:
|
||||
needPrint = getResponseCode() != HttpURLConnection.HTTP_OK;
|
||||
break;
|
||||
case ALL:
|
||||
default:
|
||||
needPrint = true;
|
||||
}
|
||||
|
||||
if (needPrint) {
|
||||
logger.info(String.format("http request %d %s %s", this.getResponseCode(), this.getResponseMessage(), this.url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,13 +22,15 @@ import java.awt.image.BufferedImage;
|
|||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicIntegerArray;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* Mana symbol resources
|
||||
*/
|
||||
public final class ManaSymbols {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ManaSymbols.class);
|
||||
|
|
@ -120,18 +122,13 @@ public final class ManaSymbols {
|
|||
ImageIO.write(image, "png", newFile);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Can't generate png image for symbol:" + symbol);
|
||||
logger.warn("Symbols: can't generate png image for symbol:" + symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// preload set images
|
||||
java.util.List<String> setCodes = ExpansionRepository.instance.getSetCodes();
|
||||
if (setCodes == null) {
|
||||
// the cards db file is probaly not included in the client. It will be created after the first connect to a server.
|
||||
logger.warn("No db information for sets found. Connect to a server to create database file on client side. Then try to restart the client.");
|
||||
return;
|
||||
}
|
||||
for (String set : setCodes) {
|
||||
|
||||
if (withoutSymbols.contains(set)) {
|
||||
|
|
@ -289,9 +286,9 @@ public final class ManaSymbols {
|
|||
// priority: SVG -> GIF
|
||||
// gif remain for backward compatibility
|
||||
|
||||
AtomicIntegerArray iconErrors = new AtomicIntegerArray(2); // 0 - svg, 1 - gif
|
||||
final List<String> svgFails = new ArrayList<>(); // scryfall
|
||||
final List<String> otherFails = new ArrayList<>(); // gatherer
|
||||
|
||||
AtomicBoolean fileErrors = new AtomicBoolean(false);
|
||||
Map<String, BufferedImage> sizedSymbols = new ConcurrentHashMap<>();
|
||||
IntStream.range(0, symbols.length).parallel().forEach(i -> {
|
||||
String symbol = symbols[i];
|
||||
|
|
@ -305,50 +302,53 @@ public final class ManaSymbols {
|
|||
try {
|
||||
InputStream fileStream = new FileInputStream(file);
|
||||
image = loadSymbolAsSVG(fileStream, file.getPath(), size, size);
|
||||
} catch (FileNotFoundException e) {
|
||||
// it's ok to hide error
|
||||
} catch (FileNotFoundException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gif
|
||||
if (image == null) {
|
||||
synchronized (svgFails) {
|
||||
svgFails.add(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
iconErrors.incrementAndGet(0); // svg fail
|
||||
|
||||
// gif (if svg fails)
|
||||
if (image == null) {
|
||||
file = getSymbolFileNameAsGIF(symbol, size);
|
||||
if (file.exists()) {
|
||||
image = loadSymbolAsGIF(file, size, size);
|
||||
}
|
||||
}
|
||||
if (image == null) {
|
||||
synchronized (otherFails) {
|
||||
otherFails.add(symbol);
|
||||
}
|
||||
}
|
||||
|
||||
// save
|
||||
if (image != null) {
|
||||
sizedSymbols.put(symbol, image);
|
||||
} else {
|
||||
iconErrors.incrementAndGet(1); // gif fail
|
||||
fileErrors.set(true);
|
||||
}
|
||||
});
|
||||
|
||||
// total errors
|
||||
String errorInfo = "";
|
||||
if (iconErrors.get(0) > 0) {
|
||||
errorInfo += "SVG miss - " + iconErrors.get(0);
|
||||
if (!svgFails.isEmpty()) {
|
||||
errorInfo += String.format("SVG miss - %s and %d others", svgFails.get(0), svgFails.size() - 1);
|
||||
}
|
||||
if (iconErrors.get(1) > 0) {
|
||||
if (!otherFails.isEmpty()) {
|
||||
if (!errorInfo.isEmpty()) {
|
||||
errorInfo += ", ";
|
||||
}
|
||||
errorInfo += "GIF miss - " + iconErrors.get(1);
|
||||
errorInfo += String.format("GIF miss - %s and %d others", otherFails.get(0), otherFails.size() - 1);
|
||||
}
|
||||
|
||||
if (!errorInfo.isEmpty()) {
|
||||
logger.warn("Symbols can't be loaded, make sure you download it by main menu - size " + size + ", " + errorInfo);
|
||||
logger.warn("Symbols: can't load, make sure you download it by main menu - size " + size + ", " + errorInfo);
|
||||
}
|
||||
|
||||
manaImages.put(size, sizedSymbols);
|
||||
return !fileErrors.get();
|
||||
return errorInfo.isEmpty();
|
||||
}
|
||||
|
||||
private static void renameSymbols(String path) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import mage.cards.MageCard;
|
|||
import mage.cards.MagePermanent;
|
||||
import mage.cards.action.ActionCallback;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.ImageCaches;
|
||||
import mage.interfaces.plugin.CardPlugin;
|
||||
import mage.view.CardView;
|
||||
import mage.view.CounterView;
|
||||
|
|
@ -103,7 +102,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
* yet, so use old component based rendering for the split cards.
|
||||
*/
|
||||
private CardPanel makeCardPanel(CardView view, UUID gameId, boolean loadImage, ActionCallback callback,
|
||||
boolean isFoil, Dimension dimension, int renderMode, boolean needFullPermanentRender) {
|
||||
boolean isFoil, Dimension dimension, int renderMode, boolean needFullPermanentRender) {
|
||||
switch (renderMode) {
|
||||
case 0:
|
||||
return new CardPanelRenderModeMTGO(view, gameId, loadImage, callback, isFoil, dimension,
|
||||
|
|
@ -118,7 +117,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
|
||||
@Override
|
||||
public MageCard getMagePermanent(PermanentView permanent, Dimension dimension, UUID gameId, ActionCallback callback,
|
||||
boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender) {
|
||||
boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender) {
|
||||
CardPanel cardPanel = makeCardPanel(permanent, gameId, loadImage, callback, false, dimension, renderMode,
|
||||
needFullPermanentRender);
|
||||
cardPanel.setShowCastingCost(true);
|
||||
|
|
@ -127,7 +126,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
|
||||
@Override
|
||||
public MageCard getMageCard(CardView cardView, Dimension dimension, UUID gameId, ActionCallback callback,
|
||||
boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender) {
|
||||
boolean canBeFoil, boolean loadImage, int renderMode, boolean needFullPermanentRender) {
|
||||
CardPanel cardPanel = makeCardPanel(cardView, gameId, loadImage, callback, false, dimension, renderMode,
|
||||
needFullPermanentRender);
|
||||
cardPanel.setShowCastingCost(true);
|
||||
|
|
@ -191,7 +190,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
&& stackPower == cardPower && stackToughness == cardToughness
|
||||
&& stackAbilities.equals(cardAbilities) && stackCounters.equals(cardCounters)
|
||||
&& (!perm.isCreature() || firstPanelPerm.getOriginalPermanent().hasSummoningSickness() == perm
|
||||
.getOriginalPermanent().hasSummoningSickness())) {
|
||||
.getOriginalPermanent().hasSummoningSickness())) {
|
||||
|
||||
if (!empty(firstPanelPerm.getOriginalPermanent().getAttachments())) {
|
||||
// Put this land to the left of lands with the same name and attachments.
|
||||
|
|
@ -235,7 +234,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
|
||||
@Override
|
||||
public int sortPermanents(Map<String, JComponent> ui, Map<UUID, MageCard> cards, boolean nonPermanentsOwnRow,
|
||||
boolean topPanel) {
|
||||
boolean topPanel) {
|
||||
// requires to find out is position have been changed that includes:
|
||||
// adding/removing permanents, type change
|
||||
|
||||
|
|
@ -279,7 +278,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
cardHeight = Math.round(cardWidth * CardPanel.ASPECT_RATIO);
|
||||
extraCardSpacingX = Math.round(cardWidth * EXTRA_CARD_SPACING_X);
|
||||
cardSpacingX = cardHeight - cardWidth + extraCardSpacingX; // need space for tap animation (horizontal
|
||||
// position)
|
||||
// position)
|
||||
cardSpacingY = Math.round(cardHeight * CARD_SPACING_Y);
|
||||
stackSpacingX = stackVertical ? 0 : Math.round(cardWidth * STACK_SPACING_X);
|
||||
stackSpacingY = Math.round(cardHeight * STACK_SPACING_Y);
|
||||
|
|
@ -363,7 +362,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
}
|
||||
for (int panelIndex = 0, panelCount = stack.size(); panelIndex < panelCount; panelIndex++) {
|
||||
MagePermanent panelPerm = stack.get(panelIndex); // it's original card panel, but you must change
|
||||
// top layer
|
||||
// top layer
|
||||
int stackPosition = panelCount - panelIndex - 1;
|
||||
if (cardsPanel != null) {
|
||||
cardsPanel.setComponentZOrder(panelPerm.getTopPanelRef(), panelIndex);
|
||||
|
|
@ -470,7 +469,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
}
|
||||
|
||||
private AttachmentLayoutInfos calculateNeededNumberOfVerticalColumns(int currentCol, Map<UUID, MageCard> cards,
|
||||
MageCard cardWithAttachments) {
|
||||
MageCard cardWithAttachments) {
|
||||
int maxCol = ++currentCol;
|
||||
int attachments = 0;
|
||||
MagePermanent permWithAttachments = (MagePermanent) cardWithAttachments.getMainPanel();
|
||||
|
|
@ -653,38 +652,34 @@ public class CardPluginImpl implements CardPlugin {
|
|||
final Downloader downloader = new Downloader();
|
||||
final DownloadGui downloadGui = new DownloadGui(downloader);
|
||||
|
||||
LOGGER.info("Symbols download prepare...");
|
||||
LOGGER.info("Download: prepare symbols to download...");
|
||||
Iterable<DownloadJob> jobs;
|
||||
|
||||
// mana symbols (low quality)
|
||||
jobs = new GathererSymbols();
|
||||
for (DownloadJob job : jobs) {
|
||||
downloader.add(job);
|
||||
}
|
||||
|
||||
// set code symbols (low quality)
|
||||
jobs = new GathererSets();
|
||||
for (DownloadJob job : jobs) {
|
||||
downloader.add(job);
|
||||
}
|
||||
|
||||
// mana symbols (high quality)
|
||||
jobs = new ScryfallSymbolsSource();
|
||||
for (DownloadJob job : jobs) {
|
||||
downloader.add(job);
|
||||
}
|
||||
|
||||
/*
|
||||
* it = new CardFrames(imagesDir); // TODO: delete frames download (not need
|
||||
* now)
|
||||
* for (DownloadJob job : it) {
|
||||
* g.getDownloader().add(job);
|
||||
* }
|
||||
*/
|
||||
|
||||
// additional resources
|
||||
jobs = new DirectLinksForDownload();
|
||||
for (DownloadJob job : jobs) {
|
||||
downloader.add(job);
|
||||
}
|
||||
|
||||
LOGGER.info("Symbols download needs " + downloader.getJobs().size() + " files");
|
||||
LOGGER.info("Download: app used " + downloader.getJobs().size() + " symbol files");
|
||||
|
||||
// download GUI dialog
|
||||
JDialog dialog = new JDialog((Frame) null, "Download symbols", false);
|
||||
|
|
@ -711,6 +706,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// downloader finisher
|
||||
worker.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@Override
|
||||
|
|
@ -718,7 +714,7 @@ public class CardPluginImpl implements CardPlugin {
|
|||
if (evt.getPropertyName().equals("state")) {
|
||||
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
|
||||
// all done, can close dialog and refresh symbols for UI
|
||||
LOGGER.info("Symbols download finished");
|
||||
LOGGER.info("Download: symbols download finished");
|
||||
dialog.dispose();
|
||||
ManaSymbols.loadImages();
|
||||
GUISizeHelper.refreshGUIAndCards(false);
|
||||
|
|
|
|||
|
|
@ -2,16 +2,15 @@ package org.mage.plugins.card.dl;
|
|||
|
||||
import org.mage.plugins.card.dl.beans.properties.Property;
|
||||
import org.mage.plugins.card.dl.lm.AbstractLaternaBean;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.*;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* Downloader job to download one resource
|
||||
* Download: download job to load one resource, used for symbols
|
||||
*
|
||||
* @author Clemens Koza, JayDi85
|
||||
*/
|
||||
|
|
@ -22,7 +21,7 @@ public class DownloadJob extends AbstractLaternaBean {
|
|||
}
|
||||
|
||||
private final String name;
|
||||
private Source source;
|
||||
private String url;
|
||||
private final Destination destination;
|
||||
private final boolean forceToDownload; // download image everytime, do not keep old image
|
||||
private final Property<State> state = properties.property("state", State.NEW);
|
||||
|
|
@ -30,13 +29,21 @@ public class DownloadJob extends AbstractLaternaBean {
|
|||
private final Property<Exception> error = properties.property("error");
|
||||
private final BoundedRangeModel progress = new DefaultBoundedRangeModel();
|
||||
|
||||
public DownloadJob(String name, Source source, Destination destination, boolean forceToDownload) {
|
||||
public DownloadJob(String name, String url, Destination destination, boolean forceToDownload) {
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
this.url = url;
|
||||
this.destination = destination;
|
||||
this.forceToDownload = forceToDownload;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the job's state. If the state is {@link State#ABORTED}, it instead
|
||||
* sets the error to "ABORTED"
|
||||
|
|
@ -80,7 +87,7 @@ public class DownloadJob extends AbstractLaternaBean {
|
|||
*/
|
||||
public void setError(String message, Exception error) {
|
||||
if (message == null) {
|
||||
message = "Download of " + name + " from " + source.toString() + " caused error: " + error.toString();
|
||||
message = "Download: " + name + " from " + url + " caused error - " + error;
|
||||
}
|
||||
this.state.setValue(State.ABORTED);
|
||||
this.error.setValue(error);
|
||||
|
|
@ -145,14 +152,6 @@ public class DownloadJob extends AbstractLaternaBean {
|
|||
return name;
|
||||
}
|
||||
|
||||
public Source getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(Source source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Destination getDestination() {
|
||||
return destination;
|
||||
}
|
||||
|
|
@ -161,71 +160,6 @@ public class DownloadJob extends AbstractLaternaBean {
|
|||
return forceToDownload;
|
||||
}
|
||||
|
||||
public static Source fromURL(final String url) {
|
||||
return fromURL(CardImageUtils.getProxyFromPreferences(), url);
|
||||
}
|
||||
|
||||
public static Source fromURL(final URL url) {
|
||||
return fromURL(CardImageUtils.getProxyFromPreferences(), url);
|
||||
}
|
||||
|
||||
public static Source fromURL(final Proxy proxy, final String url) {
|
||||
return new Source() {
|
||||
private URLConnection c;
|
||||
|
||||
public URLConnection getConnection() throws IOException {
|
||||
if (c == null) {
|
||||
c = proxy == null ? new URL(url).openConnection() : new URL(url).openConnection(proxy);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream open() throws IOException {
|
||||
return getConnection().getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() throws IOException {
|
||||
return getConnection().getContentLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return proxy != null ? proxy.type().toString() + ' ' : url;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
public static Source fromURL(final Proxy proxy, final URL url) {
|
||||
return new Source() {
|
||||
private URLConnection c;
|
||||
|
||||
public URLConnection getConnection() throws IOException {
|
||||
if (c == null) {
|
||||
c = proxy == null ? url.openConnection() : url.openConnection(proxy);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream open() throws IOException {
|
||||
return getConnection().getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() throws IOException {
|
||||
return getConnection().getContentLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return proxy != null ? proxy.type().toString() + ' ' : String.valueOf(url);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Destination toFile(final String file) {
|
||||
return toFile(new File(file));
|
||||
}
|
||||
|
|
@ -264,13 +198,6 @@ public class DownloadJob extends AbstractLaternaBean {
|
|||
};
|
||||
}
|
||||
|
||||
public interface Source {
|
||||
|
||||
InputStream open() throws IOException;
|
||||
|
||||
int length() throws IOException;
|
||||
}
|
||||
|
||||
public interface Destination {
|
||||
|
||||
OutputStream open() throws IOException;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ import java.net.Proxy;
|
|||
*/
|
||||
public interface DownloadServiceInfo {
|
||||
|
||||
Proxy getProxy();
|
||||
|
||||
boolean isNeedCancel();
|
||||
|
||||
void incErrorCount();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package org.mage.plugins.card.dl;
|
||||
|
||||
import mage.client.remote.XmageURLConnection;
|
||||
import mage.util.ThreadUtils;
|
||||
import mage.util.XMageThreadFactory;
|
||||
import mage.util.XmageThreadFactory;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jetlang.channels.Channel;
|
||||
import org.jetlang.channels.MemoryChannel;
|
||||
|
|
@ -9,13 +10,15 @@ import org.jetlang.core.Callback;
|
|||
import org.jetlang.fibers.Fiber;
|
||||
import org.jetlang.fibers.PoolFiberFactory;
|
||||
import org.mage.plugins.card.dl.DownloadJob.Destination;
|
||||
import org.mage.plugins.card.dl.DownloadJob.Source;
|
||||
import org.mage.plugins.card.dl.DownloadJob.State;
|
||||
import org.mage.plugins.card.dl.lm.AbstractLaternaBean;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.*;
|
||||
import java.net.ConnectException;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
|
@ -24,7 +27,7 @@ import java.util.concurrent.Executors;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Symbols downloader
|
||||
* Download: symbols download service
|
||||
*
|
||||
* @author Clemens Koza, JayDi85
|
||||
*/
|
||||
|
|
@ -37,7 +40,7 @@ public class Downloader extends AbstractLaternaBean {
|
|||
private CountDownLatch worksCount = null;
|
||||
|
||||
private final ExecutorService pool = Executors.newCachedThreadPool(
|
||||
new XMageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_SYMBOLS_DOWNLOADER, false)
|
||||
new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_SYMBOLS_DOWNLOADER, false)
|
||||
);
|
||||
private final List<Fiber> fibers = new ArrayList<>();
|
||||
|
||||
|
|
@ -105,11 +108,11 @@ public class Downloader extends AbstractLaternaBean {
|
|||
worksCount.await(60, TimeUnit.SECONDS);
|
||||
|
||||
if (worksCount.getCount() != 0) {
|
||||
logger.warn("Symbols download too long...");
|
||||
logger.warn("Download: symbols downloading too long");
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Need to stop symbols download...");
|
||||
logger.error("Download: symbols downloading must be stopped");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,8 +121,8 @@ public class Downloader extends AbstractLaternaBean {
|
|||
}
|
||||
|
||||
/**
|
||||
* Performs the download job: Transfers data from {@link Source} to
|
||||
* {@link Destination} and updates the download job's state to reflect the
|
||||
* Performs the download job: Transfers data from source to
|
||||
* destination and updates the download job's state to reflect the
|
||||
* progress.
|
||||
*/
|
||||
private class DownloadCallback implements Callback<DownloadJob> {
|
||||
|
|
@ -133,7 +136,7 @@ public class Downloader extends AbstractLaternaBean {
|
|||
// take new job
|
||||
job.doPrepareAndStartWork();
|
||||
if (job.getState() != State.WORKING) {
|
||||
logger.warn("Can't prepare symbols download job: " + job.getName());
|
||||
logger.warn("Download: can't prepare symbols download job: " + job.getName() + ", reason: " + job.getError());
|
||||
worksCount.countDown();
|
||||
return;
|
||||
}
|
||||
|
|
@ -146,7 +149,6 @@ public class Downloader extends AbstractLaternaBean {
|
|||
// real work for new job
|
||||
// download and save data
|
||||
try {
|
||||
Source src = job.getSource();
|
||||
Destination dst = job.getDestination();
|
||||
BoundedRangeModel progress = job.getProgress();
|
||||
|
||||
|
|
@ -155,65 +157,66 @@ public class Downloader extends AbstractLaternaBean {
|
|||
progress.setMaximum(1);
|
||||
progress.setValue(1);
|
||||
} else {
|
||||
// downloading
|
||||
// need to download
|
||||
|
||||
// clean local file
|
||||
if (dst.exists()) {
|
||||
try {
|
||||
dst.delete();
|
||||
} catch (IOException ex1) {
|
||||
logger.warn("While deleting not valid file", ex1);
|
||||
} catch (IOException e) {
|
||||
logger.warn("Download: can't delete old file " + e, e);
|
||||
}
|
||||
}
|
||||
progress.setMaximum(src.length());
|
||||
InputStream is = new BufferedInputStream(src.open());
|
||||
try {
|
||||
OutputStream os = new BufferedOutputStream(dst.open());
|
||||
try {
|
||||
byte[] buf = new byte[8 * 1024];
|
||||
int total = 0;
|
||||
for (int len; (len = is.read(buf)) != -1; ) {
|
||||
if (job.getState() == State.ABORTED) {
|
||||
throw new IOException("Job was aborted");
|
||||
|
||||
// download
|
||||
// start debug here with breakpoint like job.getName().contains("C/B")
|
||||
XmageURLConnection connection = new XmageURLConnection(job.getUrl());
|
||||
connection.startConnection();
|
||||
if (connection.isConnected()) {
|
||||
// start downloading
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
// all fine, can continue and save
|
||||
progress.setMaximum(connection.getContentLength());
|
||||
try (InputStream inputStream = connection.getGoodResponseAsStream();
|
||||
OutputStream outputStream = new BufferedOutputStream(dst.open())) {
|
||||
byte[] buf = new byte[8 * 1024];
|
||||
int total = 0;
|
||||
for (int len; (len = inputStream.read(buf)) != -1; ) {
|
||||
// fast cancel
|
||||
if (job.getState() == State.ABORTED) {
|
||||
throw new IOException("job was aborted");
|
||||
}
|
||||
progress.setValue(total += len);
|
||||
outputStream.write(buf, 0, len);
|
||||
}
|
||||
progress.setValue(total += len);
|
||||
os.write(buf, 0, len);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
try {
|
||||
dst.delete();
|
||||
} catch (IOException ex1) {
|
||||
logger.warn("While deleting", ex1);
|
||||
}
|
||||
throw ex;
|
||||
} finally {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
// something bad on downloading
|
||||
logger.warn("Download: " + job.getName() + " - catch error on downloading network resource "
|
||||
+ job.getUrl() + " - " + e, e);
|
||||
} finally {
|
||||
// clean up
|
||||
if (!dst.isValid()) {
|
||||
logger.warn("Download: " + job.getName() + " - downloaded invalid network resource (not exists?) " + job.getUrl());
|
||||
dst.delete();
|
||||
logger.warn("Resource not found " + job.getName() + " from " + job.getSource().toString());
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.warn("While closing", ex);
|
||||
}
|
||||
} else {
|
||||
// something bad with resource on server (example: wrong url)
|
||||
logger.warn("Download: " + job.getName() + " - can't find network resource " + job.getUrl());
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException ex) {
|
||||
logger.warn("While closing", ex);
|
||||
}
|
||||
} else {
|
||||
// something bad with network (example: can't connect due bad network)
|
||||
logger.warn("Download: " + job.getName() + " - can't connect to network resource " + job.getUrl());
|
||||
}
|
||||
}
|
||||
|
||||
// all done
|
||||
job.setState(State.FINISHED);
|
||||
} catch (ConnectException ex) {
|
||||
String message;
|
||||
if (ex.getMessage() != null) {
|
||||
message = ex.getMessage();
|
||||
} else {
|
||||
message = "Unknown error";
|
||||
}
|
||||
logger.warn("Error resource download " + job.getName() + " from " + job.getSource().toString() + ": " + message);
|
||||
} catch (IOException ex) {
|
||||
job.setError(ex);
|
||||
} catch (Exception e) {
|
||||
// TODO: save error in other logger.warn?
|
||||
job.setError(e);
|
||||
logger.warn("Download: " + job.getName() + " - unknown error for network resource " + job.getUrl() + " - " + e, e);
|
||||
} finally {
|
||||
worksCount.countDown();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,13 +54,10 @@ public interface CardImageSource {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set additional http headers like user agent, referer, cookies, etc
|
||||
* Set additional headers like user agent, referer, cookies, etc
|
||||
*/
|
||||
default Map<String, String> getHttpRequestHeaders(String fullUrl) {
|
||||
Map<String, String> headers = new LinkedHashMap<>();
|
||||
// TODO: add xmage name and client version here
|
||||
headers.put("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
|
||||
return headers;
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
default List<String> getSupportedSets() {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ import org.mage.plugins.card.dl.DownloadJob;
|
|||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
/**
|
||||
* Additional images from a third party sources
|
||||
* Download: additional images from a third party sources, used for symbols
|
||||
*
|
||||
* @author noxx
|
||||
*/
|
||||
|
|
@ -44,7 +43,7 @@ public class DirectLinksForDownload implements Iterable<DownloadJob> {
|
|||
for (Map.Entry<String, String> url : directLinks.entrySet()) {
|
||||
File dst = new File(outDir, url.getKey());
|
||||
// download images every time (need to update low quality image)
|
||||
jobs.add(new DownloadJob(url.getKey(), fromURL(url.getValue()), toFile(dst), true));
|
||||
jobs.add(new DownloadJob(url.getKey(), url.getValue(), toFile(dst), true));
|
||||
}
|
||||
return jobs.iterator();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,17 +6,18 @@ import mage.client.constants.Constants;
|
|||
import mage.constants.Rarity;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.dl.DownloadJob;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* WARNING, unsupported images plugin, last updates from 2018
|
||||
*/
|
||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
/**
|
||||
* Download: set code symbols download from wizards web size
|
||||
* <p>
|
||||
* Warning, it's outdated source with low quality images. TODO: must migrate to scryfall like mana icons
|
||||
*/
|
||||
public class GathererSets implements Iterable<DownloadJob> {
|
||||
|
||||
private class CheckResult {
|
||||
|
|
@ -338,6 +339,6 @@ public class GathererSets implements Iterable<DownloadJob> {
|
|||
set = codeReplacements.get(set);
|
||||
}
|
||||
String url = "https://gatherer.wizards.com/Handlers/Image.ashx?type=symbol&set=" + set + "&size=small&rarity=" + urlRarity;
|
||||
return new DownloadJob(set + '-' + rarity, fromURL(url), toFile(dst), false);
|
||||
return new DownloadJob(set + '-' + rarity, url, toFile(dst), false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,15 +8,15 @@ import java.io.File;
|
|||
import java.util.Iterator;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
|
||||
import static org.mage.plugins.card.dl.DownloadJob.toFile;
|
||||
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
|
||||
|
||||
/**
|
||||
* The class GathererSymbols.
|
||||
* Download: mana symbols download from wizards web size
|
||||
* <p>
|
||||
* Warning, it's outdated source with low quality images. Use scryfall source as primary.
|
||||
*
|
||||
* @author Clemens Koza
|
||||
* @version V0.0 25.08.2010
|
||||
* @author Clemens Koza, JayDi85
|
||||
*/
|
||||
public class GathererSymbols implements Iterable<DownloadJob> {
|
||||
|
||||
|
|
@ -125,8 +125,7 @@ public class GathererSymbols implements Iterable<DownloadJob> {
|
|||
}
|
||||
|
||||
String url = format(urlFmt, sizes[modSizeIndex], symbol);
|
||||
|
||||
return new DownloadJob(sym, fromURL(url), toFile(dst), false);
|
||||
return new DownloadJob(sym, url, toFile(dst), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,18 +4,16 @@ import com.google.gson.JsonArray;
|
|||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import mage.MageException;
|
||||
import mage.client.remote.XmageURLConnection;
|
||||
import mage.client.util.CardLanguage;
|
||||
import mage.util.JsonUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.dl.DownloadServiceInfo;
|
||||
import org.mage.plugins.card.images.CardDownloadData;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
|
|
@ -31,7 +29,7 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
private CardLanguage currentLanguage = CardLanguage.ENGLISH; // working language
|
||||
private final Map<CardDownloadData, String> preparedUrls = new HashMap<>();
|
||||
private static final int DOWNLOAD_TIMEOUT_MS = 100;
|
||||
|
||||
|
||||
public static ScryfallImageSource getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
|
@ -89,15 +87,15 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
baseUrl = link + localizedCode + "?format=image";
|
||||
// workaround to use cards without english images (some promos or special cards)
|
||||
if (link.endsWith("/")) {
|
||||
alternativeUrl = link.substring(0,link.length()-1) + "?format=image";
|
||||
alternativeUrl = link.substring(0, link.length() - 1) + "?format=image";
|
||||
}
|
||||
} else {
|
||||
// direct link to image
|
||||
baseUrl = link;
|
||||
// workaround to use localization in direct links
|
||||
if (link.contains("/?format=image")){
|
||||
baseUrl = link.replaceFirst("\\?format=image" , localizedCode + "?format=image");
|
||||
alternativeUrl = link.replaceFirst("/\\?format=image" , "?format=image");
|
||||
if (link.contains("/?format=image")) {
|
||||
baseUrl = link.replaceFirst("\\?format=image", localizedCode + "?format=image");
|
||||
alternativeUrl = link.replaceFirst("/\\?format=image", "?format=image");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -117,7 +115,7 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
// basic cards by api call (redirect to img link)
|
||||
// example: https://api.scryfall.com/cards/xln/121/en?format=image
|
||||
if (baseUrl == null) {
|
||||
String cn = ScryfallImageSupportCards.prepareCardNumber(card.getCollectorId()) ;
|
||||
String cn = ScryfallImageSupportCards.prepareCardNumber(card.getCollectorId());
|
||||
baseUrl = String.format("https://api.scryfall.com/cards/%s/%s/%s?format=image",
|
||||
formatSetName(card.getSet(), isToken),
|
||||
cn,
|
||||
|
|
@ -135,10 +133,10 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
|
||||
}
|
||||
|
||||
return new CardImageUrls(baseUrl, alternativeUrl );
|
||||
return new CardImageUrls(baseUrl, alternativeUrl);
|
||||
}
|
||||
|
||||
private String getFaceImageUrl(Proxy proxy, CardDownloadData card, boolean isToken) throws Exception {
|
||||
private String getFaceImageUrl(CardDownloadData card, boolean isToken) throws Exception {
|
||||
final String defaultCode = CardLanguage.ENGLISH.getCode();
|
||||
final String localizedCode = languageAliases.getOrDefault(this.getCurrentLanguage(), defaultCode);
|
||||
|
||||
|
|
@ -148,11 +146,11 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
String apiUrl = ScryfallImageSupportCards.findDirectDownloadLink(card.getSet(), card.getName(), card.getCollectorId());
|
||||
if (apiUrl != null) {
|
||||
if (apiUrl.endsWith("*/")) {
|
||||
apiUrl = apiUrl.substring(0 , apiUrl.length() - 2) + "★/" ;
|
||||
apiUrl = apiUrl.substring(0, apiUrl.length() - 2) + "★/";
|
||||
} else if (apiUrl.endsWith("+/")) {
|
||||
apiUrl = apiUrl.substring(0 , apiUrl.length() - 2) + "†/" ;
|
||||
apiUrl = apiUrl.substring(0, apiUrl.length() - 2) + "†/";
|
||||
} else if (apiUrl.endsWith("Ph/")) {
|
||||
apiUrl = apiUrl.substring(0 , apiUrl.length() - 3) + "Φ/" ;
|
||||
apiUrl = apiUrl.substring(0, apiUrl.length() - 3) + "Φ/";
|
||||
}
|
||||
// BY DIRECT URL
|
||||
// direct links via hardcoded API path. Used for cards with non-ASCII collector numbers
|
||||
|
|
@ -168,7 +166,7 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
} else {
|
||||
// BY CARD NUMBER
|
||||
// localized and default
|
||||
String cn = ScryfallImageSupportCards.prepareCardNumber (card.getCollectorId()) ;
|
||||
String cn = ScryfallImageSupportCards.prepareCardNumber(card.getCollectorId());
|
||||
needUrls.add(String.format("https://api.scryfall.com/cards/%s/%s/%s",
|
||||
formatSetName(card.getSet(), isToken),
|
||||
cn,
|
||||
|
|
@ -183,18 +181,16 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
InputStream jsonStream = null;
|
||||
String jsonUrl = null;
|
||||
for (String currentUrl : needUrls) {
|
||||
// connect to Scryfall API
|
||||
// find first workable api endpoint
|
||||
waitBeforeRequest();
|
||||
URL cardUrl = new URL(currentUrl);
|
||||
URLConnection request = (proxy == null ? cardUrl.openConnection() : cardUrl.openConnection(proxy));
|
||||
request.connect();
|
||||
try {
|
||||
jsonStream = (InputStream) request.getContent();
|
||||
jsonUrl = currentUrl;
|
||||
|
||||
jsonStream = XmageURLConnection.downloadBinary(currentUrl);
|
||||
if (jsonStream != null) {
|
||||
// found good url, can stop
|
||||
jsonUrl = currentUrl;
|
||||
break;
|
||||
} catch (FileNotFoundException e) {
|
||||
// localized image doesn't exists, try next url
|
||||
} else {
|
||||
// localized image doesn't exist, try next url
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -224,8 +220,6 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
@Override
|
||||
public boolean prepareDownloadList(DownloadServiceInfo downloadServiceInfo, List<CardDownloadData> downloadList) {
|
||||
// prepare download list example (
|
||||
Proxy proxy = downloadServiceInfo.getProxy();
|
||||
|
||||
preparedUrls.clear();
|
||||
|
||||
// prepare stats
|
||||
|
|
@ -248,7 +242,7 @@ public class ScryfallImageSource implements CardImageSource {
|
|||
if (card.isSecondSide()) {
|
||||
currentPrepareCount++;
|
||||
try {
|
||||
String url = getFaceImageUrl(proxy, card, card.isToken());
|
||||
String url = getFaceImageUrl(card, card.isToken());
|
||||
preparedUrls.put(card, url);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to prepare image URL (back face) for " + card.getName() + " (" + card.getSet() + ") #"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import mage.client.remote.XmageURLConnection;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.mage.plugins.card.dl.DownloadJob;
|
||||
import org.mage.plugins.card.utils.CardImageUtils;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
|
|
@ -65,7 +66,8 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
|
|||
try {
|
||||
sourceData = new String(Files.readAllBytes(Paths.get(sourcePath)));
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Can't open file to parse data: " + sourcePath + " , reason: " + e.getMessage());
|
||||
LOGGER.error("Can't open file to parse svg data: " + sourcePath + ", reason: " + e);
|
||||
return;
|
||||
}
|
||||
|
||||
// gen symbols list
|
||||
|
|
@ -129,10 +131,11 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
|
|||
|
||||
// listener for data parse after download complete
|
||||
private class ScryfallDownloadOnFinishedListener implements PropertyChangeListener {
|
||||
|
||||
private String downloadedFile;
|
||||
|
||||
public ScryfallDownloadOnFinishedListener(String ADestFile) {
|
||||
this.downloadedFile = ADestFile;
|
||||
public ScryfallDownloadOnFinishedListener(String downloadedFile) {
|
||||
this.downloadedFile = downloadedFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -152,10 +155,15 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onPreparing() throws Exception {
|
||||
public void onPreparing() {
|
||||
// parse help page and find real URL with svg icons on it
|
||||
this.cssUrl = "";
|
||||
|
||||
org.jsoup.nodes.Document doc = CardImageUtils.downloadHtmlDocument(CSS_SOURCE_URL);
|
||||
// download
|
||||
String sourceData = XmageURLConnection.downloadText(CSS_SOURCE_URL);
|
||||
org.jsoup.nodes.Document doc = Jsoup.parse(sourceData);
|
||||
|
||||
// process
|
||||
org.jsoup.select.Elements cssList = doc.select(CSS_SOURCE_SELECTOR);
|
||||
if (cssList.size() == 1) {
|
||||
this.cssUrl = cssList.first().attr("href");
|
||||
|
|
@ -164,13 +172,13 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
|
|||
if (this.cssUrl.isEmpty()) {
|
||||
throw new IllegalStateException("Can't find stylesheet url from scryfall colors page.");
|
||||
} else {
|
||||
this.setSource(fromURL(this.cssUrl));
|
||||
this.setUrl(this.cssUrl);
|
||||
}
|
||||
}
|
||||
|
||||
public ScryfallSymbolsDownloadJob() {
|
||||
// download init
|
||||
super("Scryfall symbols source", fromURL(""), toFile(DOWNLOAD_TEMP_FILE), true); // url setup on preparing stage
|
||||
// download init (real url will be added on prepare)
|
||||
super("Scryfall symbols source", "", toFile(DOWNLOAD_TEMP_FILE), true); // url setup on preparing stage
|
||||
String destFile = DOWNLOAD_TEMP_FILE;
|
||||
this.addPropertyChangeListener(STATE_PROP_NAME, new ScryfallDownloadOnFinishedListener(destFile));
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ import mage.cards.Sets;
|
|||
import mage.cards.repository.CardCriteria;
|
||||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.client.remote.XmageURLConnection;
|
||||
import mage.client.util.CardLanguage;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
|
@ -522,7 +524,8 @@ public enum WizardCardsImageSource implements CardImageSource {
|
|||
while (page < 999) {
|
||||
String searchUrl = "https://gatherer.wizards.com/Pages/Search/Default.aspx?sort=cn+&page=" + page + "&action=advanced&output=spoiler&method=visual&set=+%5B%22" + URLSetName + "%22%5D";
|
||||
logger.debug("URL: " + searchUrl);
|
||||
Document doc = CardImageUtils.downloadHtmlDocument(searchUrl);
|
||||
String sourceData = XmageURLConnection.downloadText(searchUrl);
|
||||
Document doc = Jsoup.parse(sourceData);
|
||||
Elements cardsImages = doc.select("img[src^=../../Handlers/]");
|
||||
if (cardsImages.isEmpty()) {
|
||||
break;
|
||||
|
|
@ -565,14 +568,15 @@ public enum WizardCardsImageSource implements CardImageSource {
|
|||
return setLinks;
|
||||
}
|
||||
|
||||
private void getLandVariations(LinkedHashMap<String, String> setLinks, String cardSet, int multiverseId, String cardName) throws IOException, NumberFormatException {
|
||||
private void getLandVariations(LinkedHashMap<String, String> setLinks, String cardSet, int multiverseId, String cardName) {
|
||||
CardCriteria criteria = new CardCriteria();
|
||||
criteria.name(cardName);
|
||||
criteria.setCodes(cardSet);
|
||||
List<CardInfo> cards = CardRepository.instance.findCards(criteria);
|
||||
|
||||
String urlLandDocument = "https://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=" + multiverseId;
|
||||
Document landDoc = CardImageUtils.downloadHtmlDocument(urlLandDocument);
|
||||
String sourceData = XmageURLConnection.downloadText(urlLandDocument);
|
||||
Document landDoc = Jsoup.parse(sourceData);
|
||||
Elements variations = landDoc.select("a.variationlink");
|
||||
if (!variations.isEmpty()) {
|
||||
if (variations.size() > cards.size()) {
|
||||
|
|
@ -617,9 +621,10 @@ public enum WizardCardsImageSource implements CardImageSource {
|
|||
}
|
||||
}
|
||||
|
||||
private Map<String, Integer> getlocalizedMultiverseIds(Integer englishMultiverseId) throws IOException {
|
||||
private Map<String, Integer> getlocalizedMultiverseIds(Integer englishMultiverseId) {
|
||||
String cardLanguagesUrl = "https://gatherer.wizards.com/Pages/Card/Languages.aspx?multiverseid=" + englishMultiverseId;
|
||||
Document cardLanguagesDoc = CardImageUtils.downloadHtmlDocument(cardLanguagesUrl);
|
||||
String sourceData = XmageURLConnection.downloadText(cardLanguagesUrl);
|
||||
Document cardLanguagesDoc = Jsoup.parse(sourceData);
|
||||
Elements languageTableRows = cardLanguagesDoc.select("tr.cardItem");
|
||||
Map<String, Integer> localizedIds = new HashMap<>();
|
||||
if (!languageTableRows.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@ import mage.cards.repository.TokenRepository;
|
|||
import mage.client.MageFrame;
|
||||
import mage.client.dialog.DownloadImagesDialog;
|
||||
import mage.client.dialog.PreferencesDialog;
|
||||
import mage.client.remote.XmageURLConnection;
|
||||
import mage.client.util.CardLanguage;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.sets.ConstructedFormats;
|
||||
import mage.remote.Connection;
|
||||
import mage.util.ThreadUtils;
|
||||
import mage.util.XMageThreadFactory;
|
||||
import mage.util.XmageThreadFactory;
|
||||
import net.java.truevfs.access.TFile;
|
||||
import net.java.truevfs.access.TFileInputStream;
|
||||
import net.java.truevfs.access.TFileOutputStream;
|
||||
|
|
@ -31,7 +31,8 @@ import java.awt.*;
|
|||
import java.awt.event.ItemEvent;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
|
@ -83,7 +84,6 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
private static CardImageSource selectedSource;
|
||||
|
||||
private final Object sync = new Object();
|
||||
private Proxy proxy = Proxy.NO_PROXY;
|
||||
|
||||
enum DownloadSources {
|
||||
WIZARDS("1. wizards.com - low quality, cards only", WizardCardsImageSource.instance),
|
||||
|
|
@ -635,97 +635,72 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
base.mkdir();
|
||||
}
|
||||
|
||||
Connection.ProxyType configProxyType = Connection.ProxyType.valueByText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_TYPE, "None"));
|
||||
Proxy.Type type = Proxy.Type.DIRECT;
|
||||
switch (configProxyType) {
|
||||
case HTTP:
|
||||
type = Proxy.Type.HTTP;
|
||||
break;
|
||||
case SOCKS:
|
||||
type = Proxy.Type.SOCKS;
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
proxy = Proxy.NO_PROXY;
|
||||
break;
|
||||
}
|
||||
|
||||
if (type != Proxy.Type.DIRECT) {
|
||||
try {
|
||||
String address = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_ADDRESS, "");
|
||||
Integer port = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_PORT, "80"));
|
||||
proxy = new Proxy(type, new InetSocketAddress(address, port));
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Gui_DownloadPicturesService : error 1 - " + ex);
|
||||
}
|
||||
}
|
||||
|
||||
int downloadThreadsAmount = Math.max(1, Integer.parseInt((String) uiDialog.getDownloadThreadsCombo().getSelectedItem()));
|
||||
|
||||
if (proxy != null) {
|
||||
logger.info("Started download of " + cardsDownloadQueue.size() + " images"
|
||||
+ " from source: " + selectedSource.getSourceName()
|
||||
+ ", language: " + selectedSource.getCurrentLanguage().getCode()
|
||||
+ ", threads: " + downloadThreadsAmount);
|
||||
updateProgressMessage("Preparing download list...");
|
||||
if (selectedSource.prepareDownloadList(this, cardsDownloadQueue)) {
|
||||
update(0, cardsDownloadQueue.size());
|
||||
ExecutorService executor = Executors.newFixedThreadPool(
|
||||
downloadThreadsAmount,
|
||||
new XMageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_IMAGES_DOWNLOADER, false)
|
||||
);
|
||||
for (int i = 0; i < cardsDownloadQueue.size() && !this.isNeedCancel(); i++) {
|
||||
try {
|
||||
CardDownloadData card = cardsDownloadQueue.get(i);
|
||||
|
||||
logger.debug("Downloading image: " + card.getName() + " (" + card.getSet() + ')');
|
||||
logger.info("Started download of " + cardsDownloadQueue.size() + " images"
|
||||
+ " from source: " + selectedSource.getSourceName()
|
||||
+ ", language: " + selectedSource.getCurrentLanguage().getCode()
|
||||
+ ", threads: " + downloadThreadsAmount);
|
||||
updateProgressMessage("Preparing download list...");
|
||||
if (selectedSource.prepareDownloadList(this, cardsDownloadQueue)) {
|
||||
update(0, cardsDownloadQueue.size());
|
||||
ExecutorService executor = Executors.newFixedThreadPool(
|
||||
downloadThreadsAmount,
|
||||
new XmageThreadFactory(ThreadUtils.THREAD_PREFIX_CLIENT_IMAGES_DOWNLOADER, false)
|
||||
);
|
||||
for (int i = 0; i < cardsDownloadQueue.size() && !this.isNeedCancel(); i++) {
|
||||
try {
|
||||
CardDownloadData card = cardsDownloadQueue.get(i);
|
||||
|
||||
CardImageUrls urls;
|
||||
if (card.isToken()) {
|
||||
if (!"0".equals(card.getCollectorId())) {
|
||||
continue;
|
||||
}
|
||||
urls = selectedSource.generateTokenUrl(card);
|
||||
} else {
|
||||
urls = selectedSource.generateCardUrl(card);
|
||||
logger.debug("Downloading image: " + card.getName() + " (" + card.getSet() + ')');
|
||||
|
||||
CardImageUrls urls;
|
||||
if (card.isToken()) {
|
||||
if (!"0".equals(card.getCollectorId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (urls == null) {
|
||||
String imageRef = selectedSource.getNextHttpImageUrl();
|
||||
String fileName = selectedSource.getFileForHttpImage(imageRef);
|
||||
if (imageRef != null && fileName != null) {
|
||||
imageRef = selectedSource.getSourceName() + imageRef;
|
||||
try {
|
||||
card.setToken(selectedSource.isTokenSource());
|
||||
Runnable task = new DownloadTask(card, imageRef, fileName, selectedSource.getTotalImages());
|
||||
executor.execute(task);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
} else if (selectedSource.getTotalImages() == -1) {
|
||||
logger.info("Image not available on " + selectedSource.getSourceName() + ": " + card.getName() + " (" + card.getSet() + ')');
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1, cardsDownloadQueue.size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Runnable task = new DownloadTask(card, urls, cardsDownloadQueue.size());
|
||||
executor.execute(task);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.error(ex, ex);
|
||||
urls = selectedSource.generateTokenUrl(card);
|
||||
} else {
|
||||
urls = selectedSource.generateCardUrl(card);
|
||||
}
|
||||
|
||||
if (urls == null) {
|
||||
String imageRef = selectedSource.getNextHttpImageUrl();
|
||||
String fileName = selectedSource.getFileForHttpImage(imageRef);
|
||||
if (imageRef != null && fileName != null) {
|
||||
imageRef = selectedSource.getSourceName() + imageRef;
|
||||
try {
|
||||
card.setToken(selectedSource.isTokenSource());
|
||||
Runnable task = new DownloadTask(card, imageRef, fileName, selectedSource.getTotalImages());
|
||||
executor.execute(task);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
} else if (selectedSource.getTotalImages() == -1) {
|
||||
logger.info("Image not available on " + selectedSource.getSourceName() + ": " + card.getName() + " (" + card.getSet() + ')');
|
||||
synchronized (sync) {
|
||||
update(cardIndex + 1, cardsDownloadQueue.size());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Runnable task = new DownloadTask(card, urls, cardsDownloadQueue.size());
|
||||
executor.execute(task);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
logger.error(ex, ex);
|
||||
}
|
||||
}
|
||||
|
||||
executor.shutdown();
|
||||
while (!executor.isTerminated()) {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
executor.shutdown();
|
||||
while (!executor.isTerminated()) {
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(1);
|
||||
} catch (InterruptedException ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
TVFS.umount();
|
||||
} catch (FsSyncException e) {
|
||||
|
|
@ -743,11 +718,6 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
GUISizeHelper.refreshGUIAndCards(false);
|
||||
}
|
||||
|
||||
static String convertStreamToString(InputStream is) {
|
||||
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
|
||||
return s.hasNext() ? s.next() : "";
|
||||
}
|
||||
|
||||
private final class DownloadTask implements Runnable {
|
||||
|
||||
private final CardDownloadData card;
|
||||
|
|
@ -834,17 +804,17 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
|
||||
// can download images from many alternative urls
|
||||
List<String> downloadUrls;
|
||||
XmageURLConnection connection = null;
|
||||
if (this.urls != null) {
|
||||
downloadUrls = this.urls.getDownloadList();
|
||||
} else {
|
||||
downloadUrls = new ArrayList<>();
|
||||
}
|
||||
|
||||
// try to find first workable link
|
||||
boolean isDownloadOK = false;
|
||||
URLConnection httpConn = null;
|
||||
List<String> errorsList = new ArrayList<>();
|
||||
for (String currentUrl : downloadUrls) {
|
||||
URL url = new URL(currentUrl);
|
||||
|
||||
// fast stop on cancel
|
||||
if (DownloadPicturesService.getInstance().isNeedCancel()) {
|
||||
|
|
@ -852,29 +822,27 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
}
|
||||
|
||||
// timeout before each request
|
||||
selectedSource.doPause(url.toString());
|
||||
selectedSource.doPause(currentUrl);
|
||||
|
||||
httpConn = url.openConnection(proxy);
|
||||
if (httpConn != null) {
|
||||
connection = new XmageURLConnection(currentUrl);
|
||||
connection.startConnection();
|
||||
if (connection.isConnected()) {
|
||||
|
||||
// custom headers like user agent
|
||||
Map<String, String> headers = selectedSource.getHttpRequestHeaders(url.toString());
|
||||
for (String key : headers.keySet()) {
|
||||
httpConn.setRequestProperty(key, headers.get(key));
|
||||
}
|
||||
// custom headers (ues
|
||||
connection.setRequestHeaders(selectedSource.getHttpRequestHeaders(currentUrl));
|
||||
|
||||
try {
|
||||
httpConn.connect();
|
||||
connection.connect();
|
||||
} catch (SocketException e) {
|
||||
incErrorCount();
|
||||
errorsList.add("Wrong image URL or java app is not allowed to use network. Check your firewall or proxy settings. Error: " + e.getMessage() + ". Image URL: " + url.toString());
|
||||
errorsList.add("Wrong image URL or java app is not allowed to use network. Check your firewall or proxy settings. Error: " + e.getMessage() + ". Image URL: " + currentUrl);
|
||||
break;
|
||||
} catch (UnknownHostException e) {
|
||||
incErrorCount();
|
||||
errorsList.add("Unknown site. Check your DNS settings. Error: " + e.getMessage() + ". Image URL: " + url.toString());
|
||||
errorsList.add("Unknown site. Check your DNS settings. Error: " + e.getMessage() + ". Image URL: " + currentUrl);
|
||||
break;
|
||||
}
|
||||
int responseCode = ((HttpURLConnection) httpConn).getResponseCode();
|
||||
int responseCode = connection.getResponseCode();
|
||||
|
||||
// check result
|
||||
if (responseCode != 200) {
|
||||
|
|
@ -883,13 +851,13 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
card.getSet(),
|
||||
card.getName(),
|
||||
responseCode,
|
||||
url
|
||||
currentUrl
|
||||
);
|
||||
errorsList.add(info);
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
// Shows the returned html from the request to the web server
|
||||
logger.debug("Returned HTML ERROR:\n" + convertStreamToString(((HttpURLConnection) httpConn).getErrorStream()));
|
||||
logger.debug("Returned HTML ERROR:\n" + connection.getErrorResponseAsString());
|
||||
}
|
||||
|
||||
// go to next try
|
||||
|
|
@ -902,12 +870,12 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
}
|
||||
}
|
||||
|
||||
// can save result
|
||||
if (isDownloadOK && httpConn != null) {
|
||||
// if workable link found then save result
|
||||
if (isDownloadOK && connection.isConnected()) {
|
||||
// save data to temp
|
||||
try (InputStream in = new BufferedInputStream(httpConn.getInputStream());
|
||||
OutputStream tfileout = new TFileOutputStream(fileTempImage);
|
||||
OutputStream out = new BufferedOutputStream(tfileout)) {
|
||||
try (InputStream in = new BufferedInputStream(connection.getGoodResponseAsStream());
|
||||
OutputStream tempFileStream = new TFileOutputStream(fileTempImage);
|
||||
OutputStream out = new BufferedOutputStream(tempFileStream)) {
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) != -1) {
|
||||
|
|
@ -931,6 +899,8 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// all fine, can save data part
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
}
|
||||
|
|
@ -947,7 +917,6 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
} catch (Exception e) {
|
||||
logger.error("Can't delete temp file: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
// download errors
|
||||
|
|
@ -1013,11 +982,6 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
uiDialog.getStartButton().setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Proxy getProxy() {
|
||||
return proxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getSync() {
|
||||
return sync;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import mage.cards.repository.TokenRepository;
|
|||
import mage.client.MageFrame;
|
||||
import mage.client.constants.Constants;
|
||||
import mage.client.dialog.PreferencesDialog;
|
||||
import mage.client.remote.XmageURLConnection;
|
||||
import mage.remote.Connection;
|
||||
import mage.remote.Connection.ProxyType;
|
||||
import mage.view.CardView;
|
||||
|
|
@ -201,33 +202,6 @@ public final class CardImageUtils {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static Document downloadHtmlDocument(String urlString) throws NumberFormatException, IOException {
|
||||
Preferences prefs = MageFrame.getPreferences();
|
||||
Connection.ProxyType proxyType = Connection.ProxyType.valueByText(prefs.get("proxyType", "None"));
|
||||
Document doc;
|
||||
if (proxyType == ProxyType.NONE) {
|
||||
doc = Jsoup.connect(urlString).timeout(60 * 1000).get();
|
||||
} else {
|
||||
String proxyServer = prefs.get("proxyAddress", "");
|
||||
int proxyPort = Integer.parseInt(prefs.get("proxyPort", "0"));
|
||||
URL url = new URL(urlString);
|
||||
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyServer, proxyPort));
|
||||
HttpURLConnection uc = (HttpURLConnection) url.openConnection(proxy);
|
||||
uc.setConnectTimeout(10000);
|
||||
uc.setReadTimeout(60000);
|
||||
uc.connect();
|
||||
|
||||
String line;
|
||||
StringBuffer tmp = new StringBuffer();
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream()));
|
||||
while ((line = in.readLine()) != null) {
|
||||
tmp.append(line);
|
||||
}
|
||||
doc = Jsoup.parse(String.valueOf(tmp));
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static void checkAndFixImageFiles() {
|
||||
// search broken, temp or outdated files and delete it
|
||||
// real images check is slow, so it used on images download only (not here)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package mage.client.util;
|
||||
|
||||
import mage.client.remote.XmageURLConnection;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class DownloaderTest {
|
||||
|
||||
@Test
|
||||
public void test_DownloadText_ByHttp() {
|
||||
String s = XmageURLConnection.downloadText("http://google.com");
|
||||
Assert.assertTrue("must have text data", s.contains("<head>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DownloadText_ByHttps() {
|
||||
String s = XmageURLConnection.downloadText("https://google.com");
|
||||
Assert.assertTrue("must have text data", s.contains("<head>"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DownloadFile_ByHttp() throws IOException {
|
||||
// use any public image here
|
||||
InputStream stream = XmageURLConnection.downloadBinary("http://www.google.com/tia/tia.png");
|
||||
Assert.assertNotNull(stream);
|
||||
BufferedImage image = ImageIO.read(stream);
|
||||
Assert.assertNotNull(stream);
|
||||
Assert.assertTrue("must have image data", image.getWidth() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_DownloadFile_ByHttps() throws IOException {
|
||||
// use any public image here
|
||||
InputStream stream = XmageURLConnection.downloadBinary("https://www.google.com/tia/tia.png");
|
||||
Assert.assertNotNull(stream);
|
||||
BufferedImage image = ImageIO.read(stream);
|
||||
Assert.assertNotNull(stream);
|
||||
Assert.assertTrue("must have image data", image.getWidth() > 0);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue