diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java index fce453e0ef9..6ff5ffd881a 100644 --- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java +++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java @@ -291,11 +291,8 @@ public final class ManaSymbols { }; t.setTranscodingHints(transcoderHints); t.transcode(input, null); - } - catch (TranscoderException ex) { - // Requires Java 6 - ex.printStackTrace(); - throw new IOException("Couldn't convert " + svgFile); + } catch (Exception e) { + throw new IOException("Couldn't convert svg file: " + svgFile + " , reason: " + e.getMessage()); } finally { cssFile.delete(); @@ -348,7 +345,7 @@ public final class ManaSymbols { //imagePointer[0]; } - private static File getSymbolFileNameAsSVG(String symbol){ + public static File getSymbolFileNameAsSVG(String symbol){ return new File(getResourceSymbolsPath(ResourceSymbolSize.SVG) + symbol + ".svg"); } @@ -364,7 +361,7 @@ public final class ManaSymbols { return loadSVG(sourceFile, resizeToWidth, resizeToHeight, true); } catch (Exception e) { - LOGGER.error("Can't load svg symbol: " + sourceFile.getPath()); + LOGGER.error("Can't load svg symbol: " + sourceFile.getPath() + " , reason: " + e.getMessage()); return null; } } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java index 5825c34def2..8ef8279f8ac 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java @@ -21,6 +21,7 @@ import org.mage.plugins.card.dl.Downloader; import org.mage.plugins.card.dl.sources.DirectLinksForDownload; import org.mage.plugins.card.dl.sources.GathererSets; import org.mage.plugins.card.dl.sources.GathererSymbols; +import org.mage.plugins.card.dl.sources.ScryfallSymbolsSource; import org.mage.plugins.card.images.ImageCache; import org.mage.plugins.card.info.CardInfoPaneImpl; @@ -529,7 +530,9 @@ public class CardPluginImpl implements CardPlugin { public void downloadSymbols(String imagesDir) { final DownloadGui g = new DownloadGui(new Downloader()); - Iterable it = new GathererSymbols(); + Iterable it; + + it = new GathererSymbols(); for (DownloadJob job : it) { g.getDownloader().add(job); } @@ -539,6 +542,11 @@ public class CardPluginImpl implements CardPlugin { g.getDownloader().add(job); } + it = new ScryfallSymbolsSource(); + for (DownloadJob job : it) { + g.getDownloader().add(job); + } + /* it = new CardFrames(imagesDir); // TODO: delete frames download (not need now) for (DownloadJob job : it) { @@ -551,7 +559,7 @@ public class CardPluginImpl implements CardPlugin { g.getDownloader().add(job); } - JDialog d = new JDialog((Frame) null, "Download pictures", false); + JDialog d = new JDialog((Frame) null, "Download symbols", false); d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); d.addWindowListener(new WindowAdapter() { @Override diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java index 8d189b5796b..6efcfd3eef4 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java @@ -28,7 +28,6 @@ import org.mage.plugins.card.utils.CardImageUtils; public class DownloadJob extends AbstractLaternaBean { public enum State { - NEW, WORKING, FINISHED, ABORTED } diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java index 8406c4e5415..57047e783df 100644 --- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GathererSets.java @@ -313,4 +313,4 @@ public class GathererSets implements Iterable { String url = "http://gatherer.wizards.com/Handlers/Image.ashx?type=symbol&set=" + set + "&size=small&rarity=" + urlRarity; return new DownloadJob(set + '-' + rarity, fromURL(url), toFile(dst)); } -} +} \ No newline at end of file diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java new file mode 100644 index 00000000000..caef7e5d211 --- /dev/null +++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java @@ -0,0 +1,171 @@ +package org.mage.plugins.card.dl.sources; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.mage.plugins.card.dl.DownloadJob; + +import static org.mage.card.arcane.ManaSymbols.getSymbolFileNameAsSVG; +import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir; + +// TODO: add force to download symbols (rewrite exist files) + +/** + * + * @author jaydi85@gmail.com + * + */ +public class ScryfallSymbolsSource implements Iterable { + + + 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 STATE_PROP_NAME = "state"; + static final String DOWNLOAD_TEMP_FILE = getImagesDir() + File.separator + "temp" + File.separator + "scryfall-symbols-source.txt"; + + // card-symbol-(.{1,10}){background-image.+base64,(.+)("\)}) + // see https://regex101.com/ + static final String REGEXP_MANA_PATTERN = "card-symbol-(.{1,10})\\{background-image.+base64,(.+)(\"\\)\\})"; + + + protected static final org.apache.log4j.Logger LOGGER = org.apache.log4j.Logger.getLogger(ScryfallSymbolsSource.class); + + private static final int SYMBOLS_NUMBER_START = 0; + private static final int SYMBOLS_NUMBER_END = 20; + // copy-past symbols list from gatherer download + private static final String[] SYMBOLS_LIST = {"W", "U", "B", "R", "G", + "W/U", "U/B", "B/R", "R/G", "G/W", "W/B", "U/R", "B/G", "R/W", "G/U", + "2/W", "2/U", "2/B", "2/R", "2/G", + "WP", "UP", "BP", "RP", "GP", + "X", "S", "T", "Q", "C", "E"}; + + @Override + public Iterator iterator() { + ArrayList jobs = new ArrayList<>(); + + // all symbols on one page + jobs.add(generateDownloadJob()); + + return jobs.iterator(); + } + + private void parseData(String sourcePath){ + + String sourceData = ""; + 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()); + } + + // gen symbols list + ArrayList allMageSymbols = new ArrayList<>(); + 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++){ + allMageSymbols.add(String.valueOf(SYMBOLS_NUMBER_START + i)); + } + + Map foundedData = new HashMap<>(); + + // search raw data + sourceData = sourceData.replaceAll(".card-symbol", "\n.card-symbol"); // css as one line, but need multiline + Pattern regex = Pattern.compile(REGEXP_MANA_PATTERN); + Matcher regexMatcher = regex.matcher(sourceData); + while (regexMatcher.find()) { + String symbolCode = regexMatcher.group(1).trim(); + String symbolData = regexMatcher.group(2).trim().replace(" ", "").replaceAll("\n", ""); // decoder need only wrapped text as one line + + foundedData.put(symbolCode, symbolData); + } + + // dirs maker + File dir = getSymbolFileNameAsSVG("W").getParentFile(); + if(!dir.exists()){ + dir.mkdirs(); + } + + // decode and save data (only if not exist) + for(String needCode: allMageSymbols){ + + String searchCode = needCode.replace("/", ""); + + 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)){ + continue; + } + + try { + // base64 transform + String data64 = foundedData.get(searchCode); + Base64.Decoder dec = Base64.getDecoder(); + byte[] fileData = dec.decode(data64); + + FileOutputStream stream = new FileOutputStream(destFile); + stream.write(fileData); + stream.close(); + + LOGGER.info("New svg symbol downloaded: " + needCode); + } catch (Exception e) { + LOGGER.error("Can't decode svg icon and save to file: " + destFile.getPath() + ", reason: " + e.getMessage()); + } + } + } + + + private class ScryfallSymbolsDownloadJob extends DownloadJob{ + + // listener for data parse after download complete + private class ScryDownloadOnFinishedListener implements PropertyChangeListener { + private String downloadedFile; + + public ScryDownloadOnFinishedListener(String ADestFile){ + this.downloadedFile = ADestFile; + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (!evt.getPropertyName().equals(STATE_PROP_NAME)){ + throw new IllegalArgumentException("Unknown download property " + evt.getPropertyName()); + } + + if (evt.getNewValue() != State.FINISHED){ + return; + } + + // parse data and save to dest + parseData(this.downloadedFile); + } + } + + private String destFile = ""; + + public ScryfallSymbolsDownloadJob() { + super("Scryfall symbols source", fromURL(SOURCE_URL), toFile(DOWNLOAD_TEMP_FILE)); + this.destFile = DOWNLOAD_TEMP_FILE; + this.addPropertyChangeListener(STATE_PROP_NAME, new ScryDownloadOnFinishedListener(this.destFile)); + + // clear dest file (always download new data) + File file = new File(this.destFile); + if (file.exists()){ + file.delete(); + } + } + } + + private DownloadJob generateDownloadJob() { + return new ScryfallSymbolsDownloadJob(); + } +}