mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
fix: update check via GitHub web endpoint, avoiding API 60 req/hour rate limit
This commit is contained in:
1 parent
5c73d5ed7a
commit
7abbeb6eb3
1 file changed
+27
-22
+27
-22
@@ -5,7 +5,6 @@ import com.rtbishop.look4sat.core.domain.repository.IUpdateRepository
|
||||
import com.rtbishop.look4sat.core.domain.source.IRemoteSource
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
|
||||
class UpdateRepository(
|
||||
@@ -13,11 +12,15 @@ class UpdateRepository(
|
||||
) : IUpdateRepository {
|
||||
|
||||
override suspend fun getLatestRelease(): LatestRelease? = withContext(Dispatchers.IO) {
|
||||
// Use the GitHub releases web page instead of the REST API endpoint:
|
||||
// the API endpoint is rate-limited to 60 requests/hour per IP, which is
|
||||
// quickly exhausted on shared egress IPs (e.g. VPN proxies), causing 403
|
||||
// failures. The web endpoint redirects to the latest tag with no such limit.
|
||||
val result = remoteSource.getNetworkStream(LATEST_RELEASE_URL)
|
||||
val stream = result.stream ?: return@withContext null
|
||||
try {
|
||||
val json = stream.bufferedReader().use { it.readText() }
|
||||
parseRelease(json)
|
||||
val html = stream.bufferedReader().use { it.readText() }
|
||||
parseRelease(html)
|
||||
} catch (e: Exception) {
|
||||
println("UpdateRepository parse failure: $e")
|
||||
null
|
||||
@@ -37,31 +40,33 @@ class UpdateRepository(
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseRelease(json: String): LatestRelease? {
|
||||
val obj = JSONObject(json)
|
||||
val tag = obj.optString("tag_name")
|
||||
if (tag.isBlank()) return null
|
||||
val assets = obj.optJSONArray("assets")
|
||||
var apkUrl: String? = null
|
||||
if (assets != null) {
|
||||
for (i in 0 until assets.length()) {
|
||||
val asset = assets.optJSONObject(i) ?: continue
|
||||
val name = asset.optString("name", "")
|
||||
if (name.endsWith(".apk")) {
|
||||
apkUrl = asset.optString("browser_download_url", "").ifBlank { null }
|
||||
if (apkUrl != null) break
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun parseRelease(html: String): LatestRelease? {
|
||||
// The redirect target is the latest release's tag page. Extract the tag
|
||||
// from the og:url meta tag ("…/releases/tag/v4.4.6-ba7opf.8") — stable and
|
||||
// unambiguous. The <title> also holds the release name, but that is free
|
||||
// text and cannot be used to build the asset URL.
|
||||
val ogUrl = Regex("<meta\\s+property=\"og:url\"\\s+content=\"([^\"]*)\"").find(html)
|
||||
?.groupValues?.get(1) ?: return null
|
||||
val tag = Regex("/releases/tag/(v[0-9][0-9A-Za-z.\\-]*)$").find(ogUrl)
|
||||
?.groupValues?.get(1) ?: return null
|
||||
val title = Regex("<title>(.*?)</title>", RegexOption.DOT_MATCHES_ALL)
|
||||
.find(html)?.groupValues?.get(1)?.trim()
|
||||
?.substringBefore("·")?.removePrefix("Release")?.trim() ?: tag
|
||||
// The release APK asset follows the fixed naming scheme used by the build:
|
||||
// Look4Sat-<tag without leading v>-release.apk
|
||||
val apkUrl = "$DOWNLOAD_BASE_URL/$tag/Look4Sat-${tag.removePrefix("v")}-release.apk"
|
||||
return LatestRelease(
|
||||
versionTag = tag,
|
||||
title = obj.optString("name", ""),
|
||||
body = obj.optString("body", ""),
|
||||
title = title,
|
||||
body = "",
|
||||
apkUrl = apkUrl
|
||||
)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val LATEST_RELEASE_URL = "https://api.github.com/repos/atsunatsu/Look4Sat/releases/latest"
|
||||
// Web pages are used instead of api.github.com to avoid the 60 req/hour
|
||||
// anonymous rate limit (see getLatestRelease above).
|
||||
const val LATEST_RELEASE_URL = "https://github.com/atsunatsu/Look4Sat/releases/latest"
|
||||
const val DOWNLOAD_BASE_URL = "https://github.com/atsunatsu/Look4Sat/releases/download"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user