* Fixed svg symbols download from scryfall (#5135);

This commit is contained in:
Oleg Agafonov 2018-07-10 05:49:21 +04:00
parent 6dce0c26f3
commit 69465fd9b6
6 changed files with 140 additions and 60 deletions

View file

@ -158,6 +158,7 @@ public class DownloadGui extends JPanel {
b.addActionListener(e -> {
switch(this.job.getState()) {
case NEW:
case PREPARING:
case WORKING:
this.job.setState(State.ABORTED);
}

View file

@ -23,16 +23,16 @@ import org.mage.plugins.card.utils.CardImageUtils;
* The class DownloadJob.
*
* @version V0.0 25.08.2010
* @author Clemens Koza
* @author Clemens Koza, JayDi85
*/
public class DownloadJob extends AbstractLaternaBean {
public enum State {
NEW, WORKING, FINISHED, ABORTED
NEW, PREPARING, WORKING, FINISHED, ABORTED
}
private final String name;
private final Source source;
private Source source;
private final Destination destination;
private final Property<State> state = properties.property("state", State.NEW);
private final Property<String> message = properties.property("message");
@ -98,6 +98,36 @@ public class DownloadJob extends AbstractLaternaBean {
this.message.setValue(message);
}
/**
* Inner prepare cycle from new to working
*/
public void doPrepareAndStartWork() {
if (this.state.getValue() != State.NEW) {
setError("Can't call prepare at this point.");
return;
}
this.state.setValue(State.PREPARING);
try {
onPreparing();
} catch (Exception e) {
setError("Prepare error: " + e.getMessage(), e);
return;
}
// change to working state on good prepare call
this.state.setValue(State.WORKING);
}
/**
* Prepare code to override in custom download tasks (it's calls before work start)
*/
public void onPreparing() throws Exception {
return;
}
/**
* Sets the job's message.
*
@ -131,6 +161,10 @@ public class DownloadJob extends AbstractLaternaBean {
return source;
}
public void setSource(Source source) {
this.source = source;
}
public Destination getDestination() {
return destination;
}

View file

@ -60,6 +60,7 @@ public class Downloader extends AbstractLaternaBean implements Disposable {
for (DownloadJob j : jobs) {
switch (j.getState()) {
case NEW:
case PREPARING:
case WORKING:
j.setState(State.ABORTED);
}
@ -82,8 +83,11 @@ public class Downloader extends AbstractLaternaBean implements Disposable {
}
public void add(DownloadJob job) {
if (job.getState() == State.PREPARING) {
throw new IllegalArgumentException("Job already preparing");
}
if (job.getState() == State.WORKING) {
throw new IllegalArgumentException("Job already running");
throw new IllegalArgumentException("Job already working");
}
if (job.getState() == State.FINISHED) {
throw new IllegalArgumentException("Job already finished");
@ -106,13 +110,22 @@ public class Downloader extends AbstractLaternaBean implements Disposable {
@Override
public void onMessage(DownloadJob job) {
//the job won't be processed by multiple threads
// start to work
// the job won't be processed by multiple threads
synchronized (job) {
if (job.getState() != State.NEW) {
return;
}
job.setState(State.WORKING);
job.doPrepareAndStartWork();
if (job.getState() != State.WORKING) {
return;
}
}
// download and save data
try {
Source src = job.getSource();
Destination dst = job.getDestination();

View file

@ -9,8 +9,13 @@ import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mage.MageException;
import mage.util.StreamUtils;
import org.jsoup.select.Elements;
import org.mage.plugins.card.dl.DownloadJob;
import org.mage.plugins.card.utils.CardImageUtils;
import javax.swing.text.Document;
import static org.mage.card.arcane.ManaSymbols.getSymbolFileNameAsSVG;
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
@ -18,14 +23,14 @@ import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
// TODO: add force to download symbols (rewrite exist files)
/**
*
* @author jaydi85@gmail.com
*
* @author JayDi85
*/
public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
static final String SOURCE_URL = "https://assets.scryfall.com/assets/scryfall.css"; // search css-file on https://scryfall.com/docs/api/colors
//static final String SOURCE_URL = "https://assets.scryfall.com/assets/scryfall.css"; // old version with direct css-file on https://scryfall.com/docs/api/colors
static final String CSS_SOURCE_URL = "https://scryfall.com/docs/api/colors";
static final String CSS_SOURCE_SELECTOR = "link[rel=stylesheet]"; // <link rel="stylesheet" media="all" href="https://assets.scryfall.com/assets/scryfall-8cdaa786c4c86d5c49317e3e0fed72a0e409666753d3d3e8c906f33a188e19ed.css">
static final String STATE_PROP_NAME = "state";
static final String DOWNLOAD_TEMP_FILE = getImagesDir() + File.separator + "temp" + File.separator + "scryfall-symbols-source.txt";
@ -55,21 +60,21 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
return jobs.iterator();
}
private void parseData(String sourcePath){
private void parseData(String sourcePath) {
String sourceData = "";
try {
sourceData = new String(Files.readAllBytes(Paths.get(sourcePath)));
}catch (IOException e) {
} catch (IOException e) {
LOGGER.error("Can't open file to parse data: " + sourcePath + " , reason: " + e.getMessage());
}
// gen symbols list
ArrayList<String> allMageSymbols = new ArrayList<>();
for(int i = 0; i < SYMBOLS_LIST.length; i++){
for (int i = 0; i < SYMBOLS_LIST.length; i++) {
allMageSymbols.add(SYMBOLS_LIST[i]);
}
for(Integer i = SYMBOLS_NUMBER_START; i <= SYMBOLS_NUMBER_END; i++){
for (Integer i = SYMBOLS_NUMBER_START; i <= SYMBOLS_NUMBER_END; i++) {
allMageSymbols.add(String.valueOf(SYMBOLS_NUMBER_START + i));
}
@ -88,23 +93,22 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
// dirs maker
File dir = getSymbolFileNameAsSVG("W").getParentFile();
if(!dir.exists()){
if (!dir.exists()) {
dir.mkdirs();
}
// decode and save data (only if not exist)
for(String needCode: allMageSymbols){
for (String needCode : allMageSymbols) {
String searchCode = needCode.replace("/", "");
if(!foundedData.containsKey(searchCode))
{
if (!foundedData.containsKey(searchCode)) {
LOGGER.warn("Can't found symbol code from scryfall: " + searchCode);
continue;
}
File destFile = getSymbolFileNameAsSVG(searchCode);
if (destFile.exists() && (destFile.length() > 0)){
if (destFile.exists() && (destFile.length() > 0)) {
continue;
}
FileOutputStream stream = null;
@ -118,7 +122,7 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
stream.write(fileData);
LOGGER.info("New svg symbol downloaded: " + needCode);
} catch (Exception e) {
} catch (Exception e) {
LOGGER.error("Can't decode svg icon and save to file: " + destFile.getPath() + ", reason: " + e.getMessage());
} finally {
StreamUtils.closeQuietly(stream);
@ -127,23 +131,26 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
}
private class ScryfallSymbolsDownloadJob extends DownloadJob{
private class ScryfallSymbolsDownloadJob extends DownloadJob {
private String cssUrl = ""; // need to find url from colors page https://scryfall.com/docs/api/colors
// listener for data parse after download complete
private class ScryDownloadOnFinishedListener implements PropertyChangeListener {
private class ScryfallDownloadOnFinishedListener implements PropertyChangeListener {
private String downloadedFile;
public ScryDownloadOnFinishedListener(String ADestFile){
public ScryfallDownloadOnFinishedListener(String ADestFile) {
this.downloadedFile = ADestFile;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (!evt.getPropertyName().equals(STATE_PROP_NAME)){
if (!evt.getPropertyName().equals(STATE_PROP_NAME)) {
throw new IllegalArgumentException("Unknown download property " + evt.getPropertyName());
}
if (evt.getNewValue() != State.FINISHED){
if (evt.getNewValue() != State.FINISHED) {
return;
}
@ -152,16 +159,34 @@ public class ScryfallSymbolsSource implements Iterable<DownloadJob> {
}
}
@Override
public void onPreparing() throws Exception {
this.cssUrl = "";
org.jsoup.nodes.Document doc = CardImageUtils.downloadHtmlDocument(CSS_SOURCE_URL);
org.jsoup.select.Elements cssList = doc.select(CSS_SOURCE_SELECTOR);
if (cssList.size() == 1) {
this.cssUrl = cssList.first().attr("href").toString();
}
if (this.cssUrl.isEmpty()) {
throw new IllegalStateException("Can't find stylesheet url from scryfall colors page.");
} else {
this.setSource(fromURL(this.cssUrl));
}
}
private String destFile = "";
public ScryfallSymbolsDownloadJob() {
super("Scryfall symbols source", fromURL(SOURCE_URL), toFile(DOWNLOAD_TEMP_FILE));
// download init
super("Scryfall symbols source", fromURL(""), toFile(DOWNLOAD_TEMP_FILE)); // url setup on preparing stage
this.destFile = DOWNLOAD_TEMP_FILE;
this.addPropertyChangeListener(STATE_PROP_NAME, new ScryDownloadOnFinishedListener(this.destFile));
this.addPropertyChangeListener(STATE_PROP_NAME, new ScryfallDownloadOnFinishedListener(this.destFile));
// clear dest file (always download new data)
File file = new File(this.destFile);
if (file.exists()){
if (file.exists()) {
file.delete();
}
}

View file

@ -35,6 +35,7 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.utils.CardImageUtils;
/**
* @author North
@ -535,7 +536,7 @@ public enum WizardCardsImageSource implements CardImageSource {
while (page < 999) {
String searchUrl = "http://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 = getDocument(searchUrl);
Document doc = CardImageUtils.downloadHtmlDocument(searchUrl);
Elements cardsImages = doc.select("img[src^=../../Handlers/]");
if (cardsImages.isEmpty()) {
break;
@ -587,33 +588,6 @@ public enum WizardCardsImageSource implements CardImageSource {
return setLinks;
}
private Document getDocument(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;
}
private void getLandVariations(LinkedHashMap<String, String> setLinks, String cardSet, int multiverseId, String cardName) throws IOException, NumberFormatException {
CardCriteria criteria = new CardCriteria();
criteria.nameExact(cardName);
@ -621,7 +595,7 @@ public enum WizardCardsImageSource implements CardImageSource {
List<CardInfo> cards = CardRepository.instance.findCards(criteria);
String urlLandDocument = "http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=" + multiverseId;
Document landDoc = getDocument(urlLandDocument);
Document landDoc = CardImageUtils.downloadHtmlDocument(urlLandDocument);
Elements variations = landDoc.select("a.variationlink");
if (!variations.isEmpty()) {
if (variations.size() > cards.size()) {
@ -668,7 +642,7 @@ public enum WizardCardsImageSource implements CardImageSource {
private HashMap<String, Integer> getlocalizedMultiverseIds(Integer englishMultiverseId) throws IOException {
String cardLanguagesUrl = "http://gatherer.wizards.com/Pages/Card/Languages.aspx?multiverseid=" + englishMultiverseId;
Document cardLanguagesDoc = getDocument(cardLanguagesUrl);
Document cardLanguagesDoc = CardImageUtils.downloadHtmlDocument(cardLanguagesUrl);
Elements languageTableRows = cardLanguagesDoc.select("tr.cardItem");
HashMap<String, Integer> localizedIds = new HashMap<>();
if (!languageTableRows.isEmpty()) {

View file

@ -1,11 +1,17 @@
package org.mage.plugins.card.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.util.HashMap;
import java.util.Locale;
import java.util.prefs.Preferences;
import mage.client.MageFrame;
import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
@ -13,6 +19,8 @@ import mage.remote.Connection;
import mage.remote.Connection.ProxyType;
import net.java.truevfs.access.TFile;
import org.apache.log4j.Logger;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.properties.SettingsManager;
@ -22,7 +30,6 @@ public final class CardImageUtils {
private static final Logger log = Logger.getLogger(CardImageUtils.class);
/**
*
* @param card
* @return String if image exists, else null
*/
@ -54,7 +61,6 @@ public final class CardImageUtils {
}
/**
*
* @param card
* @return String regardless of whether image exists
*/
@ -280,4 +286,31 @@ 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;
}
}