mirror of
https://github.com/magefree/mage.git
synced 2025-12-28 06:22:01 -08:00
Scryfall small images download (#12282)
- Add new option for downloading small images from scryfall - Refactor/simplify CardImageUrls --------- Co-authored-by: xenohedron <xenohedron@users.noreply.github.com>
This commit is contained in:
parent
af59ff2c5c
commit
f253777f7c
6 changed files with 122 additions and 75 deletions
|
|
@ -1,80 +1,45 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class CardImageUrls {
|
||||
|
||||
public String baseUrl;
|
||||
public List<String> alternativeUrls;
|
||||
|
||||
public CardImageUrls() {
|
||||
this.baseUrl = null;
|
||||
this.alternativeUrls = new ArrayList<>();
|
||||
}
|
||||
private final List<String> urls = new ArrayList<>();
|
||||
|
||||
public CardImageUrls(String baseUrl) {
|
||||
this(baseUrl, null);
|
||||
addUrl(baseUrl);
|
||||
}
|
||||
|
||||
public CardImageUrls(String baseUrl, String alternativeUrl) {
|
||||
this();
|
||||
|
||||
this.baseUrl = baseUrl;
|
||||
|
||||
if (alternativeUrl != null
|
||||
&& !alternativeUrl.isEmpty()
|
||||
&& !Objects.equals(baseUrl, alternativeUrl)) {
|
||||
this.alternativeUrls.add(alternativeUrl);
|
||||
public CardImageUrls(String... urls) {
|
||||
for (String url : urls) {
|
||||
addUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
public CardImageUrls(String baseUrl, String alternativeUrl , String nextaltUrl) {
|
||||
this();
|
||||
|
||||
this.baseUrl = baseUrl;
|
||||
|
||||
if (alternativeUrl != null
|
||||
&& !alternativeUrl.isEmpty()
|
||||
&& !Objects.equals(baseUrl, alternativeUrl)) {
|
||||
this.alternativeUrls.add(alternativeUrl);
|
||||
}
|
||||
|
||||
if (nextaltUrl != null
|
||||
&& !nextaltUrl.isEmpty()
|
||||
&& !Objects.equals(baseUrl, nextaltUrl)) {
|
||||
this.alternativeUrls.add(nextaltUrl);
|
||||
public CardImageUrls(Collection<String> urls) {
|
||||
for (String url : urls) {
|
||||
addUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getDownloadList() {
|
||||
List<String> downloadUrls = new ArrayList<>();
|
||||
|
||||
if (this.baseUrl != null && !this.baseUrl.isEmpty()) {
|
||||
downloadUrls.add(this.baseUrl);
|
||||
}
|
||||
|
||||
// no needs in base url duplicate
|
||||
if (this.alternativeUrls != null) {
|
||||
for (String url : this.alternativeUrls) {
|
||||
if (!url.equals(this.baseUrl)) {
|
||||
downloadUrls.add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return downloadUrls;
|
||||
return urls;
|
||||
}
|
||||
|
||||
public void addAlternativeUrl(String url) {
|
||||
if (url != null && !url.isEmpty()) {
|
||||
this.alternativeUrls.add(url);
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
// for tests
|
||||
public String getBaseUrl() {
|
||||
return urls.stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public void addUrl(String url) {
|
||||
// ignore nulls and duplicates
|
||||
if (url != null && !url.isEmpty() && !urls.contains(url)) {
|
||||
this.urls.add(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,16 +21,20 @@ import java.util.*;
|
|||
/**
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum ScryfallImageSource implements CardImageSource {
|
||||
public class ScryfallImageSource implements CardImageSource {
|
||||
|
||||
instance;
|
||||
private static final ScryfallImageSource instance = new ScryfallImageSource();
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ScryfallImageSource.class);
|
||||
|
||||
private final Map<CardLanguage, String> languageAliases;
|
||||
private CardLanguage currentLanguage = CardLanguage.ENGLISH; // working language
|
||||
private final Map<CardDownloadData, String> preparedUrls = new HashMap<>();
|
||||
private final int DOWNLOAD_TIMEOUT_MS = 100;
|
||||
private static final int DOWNLOAD_TIMEOUT_MS = 100;
|
||||
|
||||
public static ScryfallImageSource getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
ScryfallImageSource() {
|
||||
// LANGUAGES
|
||||
|
|
@ -52,7 +56,7 @@ public enum ScryfallImageSource implements CardImageSource {
|
|||
private CardImageUrls innerGenerateURL(CardDownloadData card, boolean isToken) {
|
||||
String prepared = preparedUrls.getOrDefault(card, null);
|
||||
if (prepared != null) {
|
||||
return new CardImageUrls(prepared, null);
|
||||
return new CardImageUrls(prepared);
|
||||
}
|
||||
|
||||
String defaultCode = CardLanguage.ENGLISH.getCode();
|
||||
|
|
@ -144,11 +148,11 @@ public enum 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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import org.mage.plugins.card.images.CardDownloadData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author tiera3
|
||||
*/
|
||||
public class ScryfallImageSourceSmall extends ScryfallImageSource {
|
||||
|
||||
private static final ScryfallImageSourceSmall instanceSmall = new ScryfallImageSourceSmall();
|
||||
|
||||
public static ScryfallImageSource getInstance() {
|
||||
return instanceSmall;
|
||||
}
|
||||
|
||||
private static String innerModifyUrlString(String oneUrl) {
|
||||
return oneUrl.replaceFirst("/large/","/small/").replaceFirst("format=image","format=image&version=small");
|
||||
}
|
||||
|
||||
private static CardImageUrls innerModifyUrl(CardImageUrls cardUrls) {
|
||||
List<String> downloadUrls = cardUrls.getDownloadList().stream()
|
||||
.map(ScryfallImageSourceSmall::innerModifyUrlString)
|
||||
.collect(Collectors.toList());
|
||||
return new CardImageUrls(downloadUrls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
|
||||
return innerModifyUrl(super.generateCardUrl(card));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CardImageUrls generateTokenUrl(CardDownloadData card) throws Exception {
|
||||
return innerModifyUrl(super.generateTokenUrl(card));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAverageSize() {
|
||||
return 13; // initial estimate - TODO calculate a more accurate number
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -76,7 +76,8 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
enum DownloadSources {
|
||||
WIZARDS("1. wizards.com - low quality CARDS, multi-language, slow download", WizardCardsImageSource.instance),
|
||||
TOKENS("2. tokens.mtg.onl - high quality TOKENS", TokensMtgImageSource.instance),
|
||||
SCRYFALL("3. scryfall.com - high quality CARDS and TOKENS, multi-language", ScryfallImageSource.instance),
|
||||
SCRYFALL("3. scryfall.com - high quality CARDS and TOKENS, multi-language", ScryfallImageSource.getInstance()),
|
||||
SCRYFALL_SMALL("3a. scryfall.com small images - low quality CARDS and TOKENS, multi-language", ScryfallImageSourceSmall.getInstance()),
|
||||
MAGIDEX("4. magidex.com - high quality CARDS", MagidexImageSource.instance),
|
||||
GRAB_BAG("5. GrabBag - STAR WARS cards and tokens", GrabbagImageSource.instance),
|
||||
MYTHICSPOILER("6. mythicspoiler.com", MythicspoilerComSource.instance),
|
||||
|
|
@ -164,7 +165,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
// SOURCES - scryfall is default source
|
||||
uiDialog.getSourcesCombo().setModel(new DefaultComboBoxModel(DownloadSources.values()));
|
||||
uiDialog.getSourcesCombo().setSelectedItem(DownloadSources.SCRYFALL);
|
||||
selectedSource = ScryfallImageSource.instance;
|
||||
selectedSource = ScryfallImageSource.getInstance();
|
||||
uiDialog.getSourcesCombo().addItemListener((ItemEvent event) -> {
|
||||
if (event.getStateChange() == ItemEvent.SELECTED) {
|
||||
comboboxSourceSelected(event);
|
||||
|
|
@ -729,7 +730,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
|
||||
DownloadTask(CardDownloadData card, String baseUrl, String actualFilename, int count) {
|
||||
this.card = card;
|
||||
this.urls = new CardImageUrls(baseUrl, null);
|
||||
this.urls = new CardImageUrls(baseUrl);
|
||||
this.count = count;
|
||||
this.actualFilename = actualFilename;
|
||||
this.useSpecifiedPaths = true;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import org.junit.Test;
|
|||
import org.mage.plugins.card.dl.sources.CardImageSource;
|
||||
import org.mage.plugins.card.dl.sources.CardImageUrls;
|
||||
import org.mage.plugins.card.dl.sources.ScryfallImageSource;
|
||||
import org.mage.plugins.card.dl.sources.ScryfallImageSourceSmall;
|
||||
import org.mage.plugins.card.images.CardDownloadData;
|
||||
|
||||
/**
|
||||
|
|
@ -15,25 +16,25 @@ public class ScryfallImagesDownloadTest {
|
|||
|
||||
@Test
|
||||
public void test_Cards_DownloadLinks() throws Exception {
|
||||
CardImageSource imageSource = ScryfallImageSource.instance;
|
||||
CardImageSource imageSource = ScryfallImageSource.getInstance();
|
||||
|
||||
// normal card
|
||||
CardImageUrls urls = imageSource.generateCardUrl(new CardDownloadData("Grizzly Bears", "10E", "268", false, 0));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/10e/268/en?format=image", urls.baseUrl);
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/10e/268/en?format=image", urls.getBaseUrl());
|
||||
|
||||
// various card
|
||||
urls = imageSource.generateCardUrl(new CardDownloadData("Grizzly Bears", "30A", "195", true, 1));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/30a/195/en?format=image", urls.baseUrl);
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/30a/195/en?format=image", urls.getBaseUrl());
|
||||
urls = imageSource.generateCardUrl(new CardDownloadData("Grizzly Bears", "30A", "492", true, 2));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/30a/492/en?format=image", urls.baseUrl);
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/30a/492/en?format=image", urls.getBaseUrl());
|
||||
|
||||
// api link
|
||||
urls = imageSource.generateCardUrl(new CardDownloadData("Ajani, the Greathearted", "WAR", "184*", false, 0));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/war/184★/en?format=image", urls.baseUrl);
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/war/184★/en?format=image", urls.getBaseUrl());
|
||||
|
||||
// direct api link
|
||||
urls = imageSource.generateCardUrl(new CardDownloadData("Command Tower", "REX", "26b", false, 0));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/rex/26/en?format=image&face=back", urls.baseUrl);
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/rex/26/en?format=image&face=back", urls.getBaseUrl());
|
||||
|
||||
// the one ring
|
||||
Assert.assertTrue("LTR must use The One Ring with 001 number, not 0", TheLordOfTheRingsTalesOfMiddleEarth.getInstance().getSetCardInfo()
|
||||
|
|
@ -42,6 +43,37 @@ public class ScryfallImagesDownloadTest {
|
|||
.anyMatch(c -> c.getCardNumber().equals("001"))
|
||||
);
|
||||
urls = imageSource.generateCardUrl(new CardDownloadData("The One Ring", "LTR", "001", false, 0));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/ltr/0/en?format=image", urls.baseUrl);
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/ltr/0/en?format=image", urls.getBaseUrl());
|
||||
|
||||
|
||||
// added same tests for small images
|
||||
CardImageSource imageSourceSmall = ScryfallImageSourceSmall.getInstance();
|
||||
|
||||
// normal card
|
||||
urls = imageSourceSmall.generateCardUrl(new CardDownloadData("Grizzly Bears", "10E", "268", false, 0));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/10e/268/en?format=image&version=small", urls.getBaseUrl());
|
||||
|
||||
// various card
|
||||
urls = imageSourceSmall.generateCardUrl(new CardDownloadData("Grizzly Bears", "30A", "195", true, 1));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/30a/195/en?format=image&version=small", urls.getBaseUrl());
|
||||
urls = imageSourceSmall.generateCardUrl(new CardDownloadData("Grizzly Bears", "30A", "492", true, 2));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/30a/492/en?format=image&version=small", urls.getBaseUrl());
|
||||
|
||||
// api link
|
||||
urls = imageSourceSmall.generateCardUrl(new CardDownloadData("Ajani, the Greathearted", "WAR", "184*", false, 0));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/war/184★/en?format=image&version=small", urls.getBaseUrl());
|
||||
|
||||
// direct api link
|
||||
urls = imageSourceSmall.generateCardUrl(new CardDownloadData("Command Tower", "REX", "26b", false, 0));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/rex/26/en?format=image&version=small&face=back", urls.getBaseUrl());
|
||||
|
||||
// the one ring
|
||||
Assert.assertTrue("LTR must use The One Ring with 001 number, not 0", TheLordOfTheRingsTalesOfMiddleEarth.getInstance().getSetCardInfo()
|
||||
.stream()
|
||||
.filter(c -> c.getName().equals("The One Ring"))
|
||||
.anyMatch(c -> c.getCardNumber().equals("001"))
|
||||
);
|
||||
urls = imageSourceSmall.generateCardUrl(new CardDownloadData("The One Ring", "LTR", "001", false, 0));
|
||||
Assert.assertEquals("https://api.scryfall.com/cards/ltr/0/en?format=image&version=small", urls.getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,15 +19,15 @@ public class TokensMtgImageSourceTest {
|
|||
CardImageSource imageSource = TokensMtgImageSource.instance;
|
||||
|
||||
CardImageUrls url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 1));
|
||||
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.baseUrl);
|
||||
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.getBaseUrl());
|
||||
|
||||
url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 2));
|
||||
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.baseUrl);
|
||||
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.getBaseUrl());
|
||||
|
||||
url = imageSource.generateTokenUrl(new CardDownloadData("Ashaya, the Awoken World", "ORI", "0", false, 0));
|
||||
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.baseUrl);
|
||||
Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.getBaseUrl());
|
||||
|
||||
url = imageSource.generateTokenUrl(new CardDownloadData("Emblem Gideon, Ally of Zendikar", "BFZ", "0", false, 0));
|
||||
Assert.assertEquals("https://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.baseUrl);
|
||||
Assert.assertEquals("https://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.getBaseUrl());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue