diff --git a/src/main/java/i18nupdatemod/util/AssetUtil.java b/src/main/java/i18nupdatemod/util/AssetUtil.java index d75c840..2868978 100644 --- a/src/main/java/i18nupdatemod/util/AssetUtil.java +++ b/src/main/java/i18nupdatemod/util/AssetUtil.java @@ -23,12 +23,12 @@ public class AssetUtil { private static final String CFPA_ASSET_ROOT = "http://downloader1.meitangdehulu.com:22943/"; + private static final String GITHUB = "https://raw.githubusercontent.com/"; private static final List MIRRORS; static { // 镜像地址可以改成服务器下发 MIRRORS = new ArrayList<>(); - MIRRORS.add("https://raw.githubusercontent.com/"); // 此镜像源维护者:502y MIRRORS.add("http://8.137.167.65:64684/"); } @@ -45,34 +45,38 @@ public static String getString(String url) throws IOException, URISyntaxExceptio } public static String getFastestUrl() { - List urls = new ArrayList<>(MIRRORS); - urls.add(CFPA_ASSET_ROOT); + List urls = new ArrayList<>(); + + // 根据地理位置选择源列表 + if (LocationDetectUtil.isMainlandChina()) { + // 中国大陆:测速选择最快的国内源 + urls.addAll(MIRRORS); + urls.add(CFPA_ASSET_ROOT); + Log.info("Inside mainland China: Testing mirrors..."); + } else { + // 海外用户:直接使用 GitHub 源 + urls.add(GITHUB); + Log.info("Outside mainland China: Using GitHub source..."); + } ExecutorService executor = Executors.newFixedThreadPool(Math.max(urls.size(), 10)); try { List> futures = new ArrayList<>(); for (String url : urls) { - CompletableFuture future = CompletableFuture.supplyAsync(() -> { + futures.add(CompletableFuture.supplyAsync(() -> { try { return testUrlConnection(url); } catch (IOException e) { - return null; // 表示失败 + return null; } - }, executor); - futures.add(future); + }, executor)); } - // 阻塞等待最快完成且成功的任务 String fastest = null; while (!futures.isEmpty()) { - CompletableFuture first = CompletableFuture.anyOf(futures.toArray(new CompletableFuture[0])); - fastest = (String) first.join(); - - // 移除已完成的 future + fastest = (String) CompletableFuture.anyOf(futures.toArray(new CompletableFuture[0])).join(); futures.removeIf(CompletableFuture::isDone); - if (fastest != null) { - // 成功,取消其他任务 for (CompletableFuture f : futures) { f.cancel(true); } @@ -81,8 +85,7 @@ public static String getFastestUrl() { } } - // 全部失败,返回默认 URL - Log.info("All urls are unreachable, using CFPA_ASSET_ROOT"); + Log.info("All sources unreachable, using CFPA_ASSET_ROOT as fallback"); return CFPA_ASSET_ROOT; } finally { diff --git a/src/main/java/i18nupdatemod/util/LocationDetectUtil.java b/src/main/java/i18nupdatemod/util/LocationDetectUtil.java new file mode 100644 index 0000000..bd942b4 --- /dev/null +++ b/src/main/java/i18nupdatemod/util/LocationDetectUtil.java @@ -0,0 +1,68 @@ +package i18nupdatemod.util; + +import org.apache.commons.io.IOUtils; + +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +public class LocationDetectUtil { + private static Boolean cached = null; + + private static final String[][] GEO_APIS = { + {"Kugou", "https://mips.kugou.com/check/iscn?&format=json"}, + {"IP.SB", "https://api.ip.sb/geoip"} + }; + + public static boolean isMainlandChina() { + if (cached != null) { + return cached; + } + + for (String[] api : GEO_APIS) { + Boolean result = tryApi(api[0], api[1]); + if (result != null) { + cached = result; + return cached; + } + } + + cached = false; + return false; + } + + private static Boolean tryApi(String name, String url) { + try { + HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); + conn.setConnectTimeout(5000); + conn.setReadTimeout(5000); + + if (conn.getResponseCode() == 200) { + String response = IOUtils.toString(conn.getInputStream(), StandardCharsets.UTF_8); + boolean isChina = parseResponse(response); + Log.info("Location Detected (" + name + " API): " + (isChina ? "Inside mainland China" : "Outside mainland China")); + return isChina; + } + } catch (Exception e) { + Log.debug(name + " API detection failed: " + e.getMessage()); + } + return null; + } + + private static boolean parseResponse(String response) { + response = response.trim(); + // Kugou API JSON: {"flag": 1} 或 {"flag": true} + if (response.contains("\"flag\":1") || response.contains("\"flag\": 1") ||response.contains("\"flag\":true") || response.contains("\"flag\": true")) { + return true; + } + // IP.SB API JSON: {"country_code": "CN"} + if (response.contains("\"country_code\":\"CN\"") || response.contains("\"country_code\": \"CN\"")) { + return true; + } + return false; + } + + public static void resetCache() { + cached = null; + } +}