mtg-decks-downloader

Tool to download Magic: The Gathering decklists from the Internet
git clone https://kevincorvisier.fr/git/mtg-decks-downloader.git
Log | Files | Refs | README

commit 336aac35dbf1dc91fbf66b9c40d3edd5f43e2f1f
parent 624db49c8be3f4a38d9aeeff0f006790672911c6
Author: Kevin Corvisier <git@kevincorvisier.fr>
Date:   Fri,  6 Jun 2025 05:58:27 +0900

Code clean-up
Diffstat:
Msrc/main/java/fr/kevincorvisier/mtg/dd/Cache.java | 2+-
Msrc/main/java/fr/kevincorvisier/mtg/dd/Main.java | 3++-
Msrc/main/java/fr/kevincorvisier/mtg/dd/downloaders/DecklistDownloader.java | 3++-
Msrc/main/java/fr/kevincorvisier/mtg/dd/downloaders/HareruyaDecklistDownloader.java | 10++++++----
Msrc/main/java/fr/kevincorvisier/mtg/dd/downloaders/TcdecksDecklistDownloader.java | 42++++++++++++++++++++++++------------------
Msrc/main/java/fr/kevincorvisier/mtg/dd/model/DeckComparator.java | 2+-
Msrc/test/java/fr/kevincorvisier/mtg/dd/consumers/DefaultDecklistConsumerTest.java | 7++++---
Msrc/test/java/fr/kevincorvisier/mtg/dd/downloaders/HareruyaDecklistDownloaderTest.java | 10++++++----
8 files changed, 46 insertions(+), 33 deletions(-)

diff --git a/src/main/java/fr/kevincorvisier/mtg/dd/Cache.java b/src/main/java/fr/kevincorvisier/mtg/dd/Cache.java @@ -36,7 +36,7 @@ public class Cache public Cache(@Value("${cache.database}") final String file) throws SQLException { if (file == null) - throw new RuntimeException("Property cache.database must be present and have a value"); + throw new NullPointerException("Property cache.database must be present and have a value"); new File(file).getParentFile().mkdirs(); diff --git a/src/main/java/fr/kevincorvisier/mtg/dd/Main.java b/src/main/java/fr/kevincorvisier/mtg/dd/Main.java @@ -2,6 +2,7 @@ package fr.kevincorvisier.mtg.dd; import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.Collection; @@ -28,7 +29,7 @@ public class Main @Value("#{'${sources}'.split('\\|')}") private final Collection<URL> sources; - public void run() throws IOException + public void run() throws MalformedURLException, URISyntaxException { try { diff --git a/src/main/java/fr/kevincorvisier/mtg/dd/downloaders/DecklistDownloader.java b/src/main/java/fr/kevincorvisier/mtg/dd/downloaders/DecklistDownloader.java @@ -1,11 +1,12 @@ package fr.kevincorvisier.mtg.dd.downloaders; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; public interface DecklistDownloader { boolean accept(final URL url); - void download(final URL url) throws MalformedURLException; + void download(final URL url) throws MalformedURLException, URISyntaxException; } diff --git a/src/main/java/fr/kevincorvisier/mtg/dd/downloaders/HareruyaDecklistDownloader.java b/src/main/java/fr/kevincorvisier/mtg/dd/downloaders/HareruyaDecklistDownloader.java @@ -2,6 +2,8 @@ package fr.kevincorvisier.mtg.dd.downloaders; import java.io.IOException; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.time.LocalDate; @@ -33,7 +35,7 @@ public class HareruyaDecklistDownloader implements DecklistDownloader } @Override - public void download(final URL url) throws MalformedURLException + public void download(final URL url) throws MalformedURLException, URISyntaxException { URL next = url; @@ -59,7 +61,7 @@ public class HareruyaDecklistDownloader implements DecklistDownloader try { - next = new URL(crawler.findElement(By.xpath("//div[@class='searchResultPagenation']/a[text()='>']")).getAttribute("href")); + next = new URI(crawler.findElement(By.xpath("//div[@class='searchResultPagenation']/a[text()='>']")).getAttribute("href")).toURL(); } catch (final NoSuchElementException e) { @@ -68,7 +70,7 @@ public class HareruyaDecklistDownloader implements DecklistDownloader } while (stopCondition.capacity() > 0 && next != null); } - private DeckMetadata processDeckElement(final WebElement element) throws IOException + private DeckMetadata processDeckElement(final WebElement element) throws MalformedURLException, URISyntaxException { final String playerName = element.findElement(By.className("deckSearch-searchResult__item__playerName__link")).getText(); final String deckName = element.findElement(By.xpath(".//li[@class='deckSearch-searchResult__item__deckName']/span[2]")).getText(); @@ -80,7 +82,7 @@ public class HareruyaDecklistDownloader implements DecklistDownloader final int beginIndex = "https://www.hareruyamtg.com/en/deck/".length(); final int endIndex = href.length() - "/show/".length(); final long id = Long.parseLong(href.substring(beginIndex, endIndex)); - final URL url = new URL("https://www.hareruyamtg.com/en/deck/download/" + id); + final URL url = new URI("https://www.hareruyamtg.com/en/deck/download/" + id).toURL(); return metadataFactory.create(url, playerName, deckName, LocalDate.parse(date)); } diff --git a/src/main/java/fr/kevincorvisier/mtg/dd/downloaders/TcdecksDecklistDownloader.java b/src/main/java/fr/kevincorvisier/mtg/dd/downloaders/TcdecksDecklistDownloader.java @@ -1,6 +1,8 @@ package fr.kevincorvisier.mtg.dd.downloaders; import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -40,7 +42,11 @@ public class TcdecksDecklistDownloader implements DecklistDownloader private static final Pattern pattern = Pattern.compile("Date: (?<dayOfMonth>\\d{2})\\/(?<month>\\d{2})\\/(?<year>\\d{4})"); private static final Pattern PATTERN_PLAYERS = Pattern.compile("(?<players>\\d+) Players"); - private static final String FORMAT_DOWNLOAD_URL = "/download.php?ext=txt&id=%1$s&iddeck=%2$s"; + private static final String FORMAT_DOWNLOAD_URL = "https://www.tcdecks.net/download.php?ext=txt&id=%1$s&iddeck=%2$s"; + + private static final By XPATH_COL_PLAYER = By.xpath("td[@data-th='Player']/a"); + private static final By XPATH_NEXT = By.xpath("//a[text()='Next']"); + private static final By XPATH_RESULT_ROW = By.xpath("//table[@class='tourney_list']/tbody/tr"); private final Crawler crawler; private final DeckMetadataFactory metadataFactory; @@ -57,7 +63,7 @@ public class TcdecksDecklistDownloader implements DecklistDownloader } @Override - public void download(final URL url) throws MalformedURLException + public void download(final URL url) throws MalformedURLException, URISyntaxException { final String urlStr = url.toString(); @@ -76,7 +82,7 @@ public class TcdecksDecklistDownloader implements DecklistDownloader downloadSearch(url); } - private void downloadFormat(final URL url) throws MalformedURLException + private void downloadFormat(final URL url) throws MalformedURLException, URISyntaxException { URL next = url; @@ -86,7 +92,7 @@ public class TcdecksDecklistDownloader implements DecklistDownloader final List<URL> tournaments = new ArrayList<>(); - for (final WebElement tr : crawler.findElements(By.xpath("//table[@class='tourney_list']/tbody/tr"))) + for (final WebElement tr : crawler.findElements(XPATH_RESULT_ROW)) { if (hasElement(tr, By.xpath("th"))) continue; // Header row @@ -111,12 +117,12 @@ public class TcdecksDecklistDownloader implements DecklistDownloader continue; // Skip small tournaments } - tournaments.add(new URL(tournamentName.getAttribute("href"))); + tournaments.add(new URI(tournamentName.getAttribute("href")).toURL()); } try { - next = new URL(crawler.findElement(By.xpath("//a[text()='Next']")).getAttribute("href")); + next = new URI(crawler.findElement(XPATH_NEXT).getAttribute("href")).toURL(); } catch (final NoSuchElementException e) { @@ -128,7 +134,7 @@ public class TcdecksDecklistDownloader implements DecklistDownloader } while (stopCondition.capacity() > 0 && next != null); } - private void downloadArchetype(final URL url, final String archetype) throws MalformedURLException + private void downloadArchetype(final URL url, final String archetype) throws MalformedURLException, URISyntaxException { URL next = url; @@ -136,13 +142,13 @@ public class TcdecksDecklistDownloader implements DecklistDownloader { crawler.navigateTo(next); - for (final WebElement tr : crawler.findElements(By.xpath("//table[@class='tourney_list']/tbody/tr"))) + for (final WebElement tr : crawler.findElements(XPATH_RESULT_ROW)) { if (hasElement(tr, By.xpath("th"))) continue; // Header row final WebElement deckName = tr.findElement(By.xpath("td[@data-th='Deck Name']/a")); - final WebElement player = tr.findElement(By.xpath("td[@data-th='Player']/a")); + final WebElement player = tr.findElement(XPATH_COL_PLAYER); final WebElement date = tr.findElement(By.xpath("td[@data-th='Date']/a")); final WebElement position = tr.findElement(By.xpath("td[@data-th='Position']/a")); final URL downloadUrl = toDownloadUrl(deckName.getAttribute("href")); @@ -164,7 +170,7 @@ public class TcdecksDecklistDownloader implements DecklistDownloader try { - next = new URL(crawler.findElement(By.xpath("//a[text()='Next']")).getAttribute("href")); + next = new URI(crawler.findElement(XPATH_NEXT).getAttribute("href")).toURL(); } catch (final NoSuchElementException e) { @@ -195,13 +201,13 @@ public class TcdecksDecklistDownloader implements DecklistDownloader Integer.parseInt(matcher.group("month")), // Integer.parseInt(matcher.group("dayOfMonth"))); - for (final WebElement tr : crawler.findElements(By.xpath("//table[@class='tourney_list']/tbody/tr"))) + for (final WebElement tr : crawler.findElements(XPATH_RESULT_ROW)) { if (hasElement(tr, By.xpath("th"))) continue; // Header row final WebElement archetype = tr.findElement(By.xpath("td[@data-th='Archetype']/a")); - final WebElement player = tr.findElement(By.xpath("td[@data-th='Player']/a")); + final WebElement player = tr.findElement(XPATH_COL_PLAYER); final URL downloadUrl = toDownloadUrl(archetype.getAttribute("href")); consumers.process(metadataFactory.create(downloadUrl, player.getText(), archetype.getText(), date)); @@ -213,7 +219,7 @@ public class TcdecksDecklistDownloader implements DecklistDownloader } } - private void downloadSearch(final URL url) throws MalformedURLException + private void downloadSearch(final URL url) throws MalformedURLException, URISyntaxException { URL next = url; @@ -227,7 +233,7 @@ public class TcdecksDecklistDownloader implements DecklistDownloader continue; // Header row final WebElement archetype = tr.findElement(By.xpath("td[@data-th='Archetype']/a")); - final WebElement player = tr.findElement(By.xpath("td[@data-th='Player']/a")); + final WebElement player = tr.findElement(XPATH_COL_PLAYER); final WebElement date = tr.findElement(By.xpath("td[@data-th='Date']")); final URL downloadUrl = toDownloadUrl(archetype.getAttribute("href")); @@ -236,7 +242,7 @@ public class TcdecksDecklistDownloader implements DecklistDownloader try { - next = new URL(crawler.findElement(By.xpath("//a[text()='Next']")).getAttribute("href")); + next = new URI(crawler.findElement(XPATH_NEXT).getAttribute("href")).toURL(); } catch (final NoSuchElementException e) { @@ -260,12 +266,12 @@ public class TcdecksDecklistDownloader implements DecklistDownloader } @NotNull - private URL toDownloadUrl(final String deckUrl) throws MalformedURLException + private URL toDownloadUrl(final String deckUrl) throws MalformedURLException, URISyntaxException { final Matcher matcher = PATTERN_URL_DECK.matcher(deckUrl); if (!matcher.matches()) - throw new RuntimeException("Cannot parse deck url: " + deckUrl); + throw new IllegalArgumentException("Cannot parse deck url: " + deckUrl); - return new URL("https", "www.tcdecks.net", String.format(FORMAT_DOWNLOAD_URL, matcher.group("id"), matcher.group("iddeck"))); + return new URI(String.format(FORMAT_DOWNLOAD_URL, matcher.group("id"), matcher.group("iddeck"))).toURL(); } } diff --git a/src/main/java/fr/kevincorvisier/mtg/dd/model/DeckComparator.java b/src/main/java/fr/kevincorvisier/mtg/dd/model/DeckComparator.java @@ -10,7 +10,7 @@ public class DeckComparator implements Comparator<Deck> public int compare(final Deck o1, final Deck o2) { // More recent first - int res = -o1.getMetadata().getDate().compareTo(o2.getMetadata().getDate()); + int res = o2.getMetadata().getDate().compareTo(o1.getMetadata().getDate()); if (res != 0) return res; diff --git a/src/test/java/fr/kevincorvisier/mtg/dd/consumers/DefaultDecklistConsumerTest.java b/src/test/java/fr/kevincorvisier/mtg/dd/consumers/DefaultDecklistConsumerTest.java @@ -3,7 +3,8 @@ package fr.kevincorvisier.mtg.dd.consumers; import static org.junit.jupiter.api.Assertions.assertEquals; import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.time.LocalDate; import org.junit.jupiter.api.Test; @@ -42,9 +43,9 @@ class DefaultDecklistConsumerTest { try { - return new DeckMetadata(new URL("https://localhost"), player, archetype, LocalDate.parse(date)); + return new DeckMetadata(new URI("https://localhost").toURL(), player, archetype, LocalDate.parse(date)); } - catch (final MalformedURLException e) + catch (final MalformedURLException | URISyntaxException e) { throw new RuntimeException(e); } diff --git a/src/test/java/fr/kevincorvisier/mtg/dd/downloaders/HareruyaDecklistDownloaderTest.java b/src/test/java/fr/kevincorvisier/mtg/dd/downloaders/HareruyaDecklistDownloaderTest.java @@ -3,18 +3,20 @@ package fr.kevincorvisier.mtg.dd.downloaders; import static org.junit.jupiter.api.Assertions.assertEquals; import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import org.junit.jupiter.api.Test; class HareruyaDecklistDownloaderTest { + private static final String URL_DECK_SEARCH = "https://www.hareruyamtg.com/en/deck/result?pageSize=100&formats[11]=11&eventName=middle&public_status=public&grades=champion"; + @Test - void accept() throws MalformedURLException + void accept() throws MalformedURLException, URISyntaxException { final HareruyaDecklistDownloader tested = new HareruyaDecklistDownloader(null, null, null, null); - assertEquals(true, tested.accept( - new URL("https://www.hareruyamtg.com/en/deck/result?pageSize=100&formats[11]=11&eventName=middle&public_status=public&grades=champion"))); + assertEquals(true, tested.accept(new URI(URL_DECK_SEARCH).toURL())); } }