Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73f6dbba75 | ||
|
|
f6f6a2499f |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -43,9 +43,6 @@ jobs:
|
|||||||
"platforms;android-35" "build-tools;35.0.0" "platform-tools" > /dev/null
|
"platforms;android-35" "build-tools;35.0.0" "platform-tools" > /dev/null
|
||||||
echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties
|
echo "sdk.dir=$ANDROID_SDK_ROOT" > local.properties
|
||||||
|
|
||||||
- name: Fetch club crests
|
|
||||||
run: python3 tools/fetch-crests.py || true
|
|
||||||
|
|
||||||
- name: Unit tests
|
- name: Unit tests
|
||||||
run: ./gradlew testDebugUnitTest --no-daemon --stacktrace
|
run: ./gradlew testDebugUnitTest --no-daemon --stacktrace
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ android {
|
|||||||
applicationId = "dev.castarr.tv"
|
applicationId = "dev.castarr.tv"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 31
|
versionCode = 32
|
||||||
versionName = "0.10.5"
|
versionName = "0.10.6"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release signing from environment (see ~/.keys/castarr-release.env on the
|
// Release signing from environment (see ~/.keys/castarr-release.env on the
|
||||||
|
|||||||
@@ -200,8 +200,14 @@ class AppState(
|
|||||||
result.fold(
|
result.fold(
|
||||||
onSuccess = { appError = AppError.NONE },
|
onSuccess = { appError = AppError.NONE },
|
||||||
onFailure = { throwable ->
|
onFailure = { throwable ->
|
||||||
// Cached channels keep the app usable; only surface a
|
// An expired login must always surface: cached channels
|
||||||
// fullscreen state when there is nothing to show.
|
// would otherwise sit there without EPG or favourites and
|
||||||
|
// nothing would say why.
|
||||||
|
if (throwable.message == "not logged in") {
|
||||||
|
appError = AppError.RELOGIN
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Otherwise cached channels keep the app usable.
|
||||||
if (activeChannels().isNotEmpty()) return
|
if (activeChannels().isNotEmpty()) return
|
||||||
appError = when {
|
appError = when {
|
||||||
throwable.message == "not logged in" -> AppError.RELOGIN
|
throwable.message == "not logged in" -> AppError.RELOGIN
|
||||||
|
|||||||
@@ -49,13 +49,20 @@ class DispatcharrRepository(context: Context, private val auth: DeviceAuth) {
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
val result = runCatching {
|
val result = runCatching {
|
||||||
status.value = "loading_channels"
|
status.value = "loading_channels"
|
||||||
|
Log.i(TAG, "refresh start (loggedIn=${auth.isLoggedIn})")
|
||||||
val token = auth.accessToken() ?: error("not logged in")
|
val token = auth.accessToken() ?: error("not logged in")
|
||||||
val groups = fetchGroups(token)
|
val groups = fetchGroups(token)
|
||||||
val list = fetchChannels(token, groups)
|
val list = fetchChannels(token, groups)
|
||||||
channels.value = list
|
channels.value = list
|
||||||
prefs.edit().putString("channels_cache", Channel.listToJson(list).toString()).apply()
|
prefs.edit().putString("channels_cache", Channel.listToJson(list).toString()).apply()
|
||||||
launch { runCatching { refreshFavorites(token) } }
|
launch {
|
||||||
launch { runCatching { refreshProfiles(token) } }
|
runCatching { refreshFavorites(token) }
|
||||||
|
.onFailure { Log.w(TAG, "favorites failed: ${it.javaClass.simpleName}: ${it.message?.take(120)}") }
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
runCatching { refreshProfiles(token) }
|
||||||
|
.onFailure { Log.w(TAG, "profiles failed: ${it.javaClass.simpleName}: ${it.message?.take(120)}") }
|
||||||
|
}
|
||||||
launch {
|
launch {
|
||||||
runCatching { refreshEpg(token) }
|
runCatching { refreshEpg(token) }
|
||||||
.onFailure { Log.w(TAG, "epg failed: ${it.javaClass.simpleName}: ${it.message?.take(160)}") }
|
.onFailure { Log.w(TAG, "epg failed: ${it.javaClass.simpleName}: ${it.message?.take(160)}") }
|
||||||
|
|||||||
@@ -251,6 +251,10 @@ fun SettingsScreen(state: AppState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SettingRow(
|
||||||
|
"Senderliste neu laden",
|
||||||
|
subtitle = "Holt Sender, Programm und Favoriten erneut",
|
||||||
|
) { state.refreshActive() }
|
||||||
SettingRow(
|
SettingRow(
|
||||||
"Erweitert",
|
"Erweitert",
|
||||||
subtitle = "M3U/EPG-Adressen von Hand eintragen",
|
subtitle = "M3U/EPG-Adressen von Hand eintragen",
|
||||||
|
|||||||
58
tools/crest-sources.json
Normal file
58
tools/crest-sources.json
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"aachen": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/94/Alemannia_Aachen_2010.svg/langde-330px-Alemannia_Aachen_2010.svg.png",
|
||||||
|
"aue": "https://thumb.wikimedia.org/wikipedia/de/thumb/1/13/Fc_erzgebirge_aue.svg/langde-330px-Fc_erzgebirge_aue.svg.png",
|
||||||
|
"augsburg": "https://upload.wikimedia.org/wikipedia/de/thumb/b/b5/Logo_FC_Augsburg.svg/langde-330px-Logo_FC_Augsburg.svg.png",
|
||||||
|
"bayern": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/FC_Bayern_M%C3%BCnchen_logo_%282024%29.svg/langde-330px-FC_Bayern_M%C3%BCnchen_logo_%282024%29.svg.png",
|
||||||
|
"bielefeld": "https://thumb.wikimedia.org/wikipedia/commons/thumb/f/fd/Arminia_Bielefeld_Logo_2021%E2%80%93.svg/langde-330px-Arminia_Bielefeld_Logo_2021%E2%80%93.svg.png",
|
||||||
|
"bochum": "https://thumb.wikimedia.org/wikipedia/commons/thumb/7/72/VfL_Bochum_logo.svg/langde-330px-VfL_Bochum_logo.svg.png",
|
||||||
|
"braunschweig": "https://thumb.wikimedia.org/wikipedia/de/thumb/4/45/Logo_Eintracht_Braunschweig.svg/langde-330px-Logo_Eintracht_Braunschweig.svg.png",
|
||||||
|
"bremen": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/SV-Werder-Bremen-Logo.svg/langde-330px-SV-Werder-Bremen-Logo.svg.png",
|
||||||
|
"bvb": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/67/Borussia_Dortmund_logo.svg/langde-330px-Borussia_Dortmund_logo.svg.png",
|
||||||
|
"cottbus": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Logo_Energie_Cottbus.svg/langde-330px-Logo_Energie_Cottbus.svg.png",
|
||||||
|
"darmstadt": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e5/SV_Darmstadt_98_Logo.svg/langde-330px-SV_Darmstadt_98_Logo.svg.png",
|
||||||
|
"dresden": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e1/Logo_SG_Dynamo_Dresden_neu.svg/langde-330px-Logo_SG_Dynamo_Dresden_neu.svg.png",
|
||||||
|
"duesseldorf": "https://thumb.wikimedia.org/wikipedia/commons/thumb/9/94/Fortuna_D%C3%BCsseldorf.svg/langde-330px-Fortuna_D%C3%BCsseldorf.svg.png",
|
||||||
|
"duisburg": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Msv_duisburg_%282017%29.svg/langde-330px-Msv_duisburg_%282017%29.svg.png",
|
||||||
|
"elversberg": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/SV_Elversberg_Logo_2021.svg/langde-330px-SV_Elversberg_Logo_2021.svg.png",
|
||||||
|
"essen": "https://upload.wikimedia.org/wikipedia/de/thumb/8/8a/Logo_Rot-Weiss_Essen.svg/langde-330px-Logo_Rot-Weiss_Essen.svg.png",
|
||||||
|
"frankfurt": "https://upload.wikimedia.org/wikipedia/de/thumb/3/32/Logo_Eintracht_Frankfurt_1998.svg/langde-330px-Logo_Eintracht_Frankfurt_1998.svg.png",
|
||||||
|
"freiburg": "https://upload.wikimedia.org/wikipedia/de/thumb/b/bf/SC_Freiburg_Logo.svg/langde-330px-SC_Freiburg_Logo.svg.png",
|
||||||
|
"fuerth": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/SpVgg_Greuther_F%C3%BCrth_2017.svg/langde-330px-SpVgg_Greuther_F%C3%BCrth_2017.svg.png",
|
||||||
|
"gladbach": "https://thumb.wikimedia.org/wikipedia/commons/thumb/8/81/Borussia_M%C3%B6nchengladbach_logo.svg/langde-330px-Borussia_M%C3%B6nchengladbach_logo.svg.png",
|
||||||
|
"hannover": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/cd/Hannover_96_Logo.svg/langde-330px-Hannover_96_Logo.svg.png",
|
||||||
|
"hansa": "https://thumb.wikimedia.org/wikipedia/commons/thumb/8/8f/F.C._Hansa_Rostock_Logo.svg/langde-330px-F.C._Hansa_Rostock_Logo.svg.png",
|
||||||
|
"havelse": "https://thumb.wikimedia.org/wikipedia/commons/thumb/8/89/TSV_Havelse_logo.svg/langde-330px-TSV_Havelse_logo.svg.png",
|
||||||
|
"heidenheim": "https://thumb.wikimedia.org/wikipedia/commons/thumb/9/9d/1._FC_Heidenheim_1846.svg/langde-330px-1._FC_Heidenheim_1846.svg.png",
|
||||||
|
"hertha": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/81/Hertha_BSC_Logo_2012.svg/langde-330px-Hertha_BSC_Logo_2012.svg.png",
|
||||||
|
"hoffenheim": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Logo_TSG_Hoffenheim.svg/langde-330px-Logo_TSG_Hoffenheim.svg.png",
|
||||||
|
"hoffenheim-ii": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e7/Logo_TSG_Hoffenheim.svg/langde-330px-Logo_TSG_Hoffenheim.svg.png",
|
||||||
|
"holstein": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/30/Holstein_Kiel_Logo.svg/langde-330px-Holstein_Kiel_Logo.svg.png",
|
||||||
|
"hsv": "https://thumb.wikimedia.org/wikipedia/commons/thumb/f/f7/Hamburger_SV_logo.svg/langde-330px-Hamburger_SV_logo.svg.png",
|
||||||
|
"ingolstadt": "https://thumb.wikimedia.org/wikipedia/de/thumb/5/55/FC-Ingolstadt_logo.svg/langde-330px-FC-Ingolstadt_logo.svg.png",
|
||||||
|
"kaiserslautern": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d3/Logo_1_FC_Kaiserslautern.svg/langde-330px-Logo_1_FC_Kaiserslautern.svg.png",
|
||||||
|
"karlsruhe": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Karlsruher_SC_Logo_2.svg/langde-330px-Karlsruher_SC_Logo_2.svg.png",
|
||||||
|
"koeln": "https://thumb.wikimedia.org/wikipedia/commons/thumb/0/01/1._FC_Koeln_Logo_2014%E2%80%93.svg/langde-330px-1._FC_Koeln_Logo_2014%E2%80%93.svg.png",
|
||||||
|
"leipzig": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/ab/2019-07-12_Fu%C3%9Fball%3B_Freundschaftsspiel_RB_Leipzig_-_FC_Z%C3%BCrich_1DX_0881_by_Stepro_2.png/330px-2019-07-12_Fu%C3%9Fball%3B_Freundschaftsspiel_RB_Leipzig_-_FC_Z%C3%BCrich_1DX_0881_by_Stepro_2.png",
|
||||||
|
"leverkusen": "https://upload.wikimedia.org/wikipedia/de/thumb/f/f7/Bayer_Leverkusen_Logo.svg/langde-330px-Bayer_Leverkusen_Logo.svg.png",
|
||||||
|
"magdeburg": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/1._FC_Magdeburg.svg/langde-330px-1._FC_Magdeburg.svg.png",
|
||||||
|
"mainz": "https://thumb.wikimedia.org/wikipedia/commons/thumb/9/9e/Logo_Mainz_05.svg/langde-330px-Logo_Mainz_05.svg.png",
|
||||||
|
"mannheim": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/17/Svwaldhof.svg/langde-330px-Svwaldhof.svg.png",
|
||||||
|
"muenster": "https://thumb.wikimedia.org/wikipedia/de/thumb/7/7e/SC_Preussen_Muenster_Logo_2018.svg/langde-330px-SC_Preussen_Muenster_Logo_2018.svg.png",
|
||||||
|
"nuernberg": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/1._FC_N%C3%BCrnberg_logo.svg/langde-330px-1._FC_N%C3%BCrnberg_logo.svg.png",
|
||||||
|
"osnabrueck": "https://thumb.wikimedia.org/wikipedia/commons/thumb/4/4e/VfL_Osnabrueck_Logo_2021%E2%80%93.svg/langde-330px-VfL_Osnabrueck_Logo_2021%E2%80%93.svg.png",
|
||||||
|
"paderborn": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/67/SC_Paderborn_07_Logo_new.svg/langde-330px-SC_Paderborn_07_Logo_new.svg.png",
|
||||||
|
"regensburg": "https://thumb.wikimedia.org/wikipedia/commons/thumb/3/3d/Jahn_Regensburg_logo2014.svg/langde-330px-Jahn_Regensburg_logo2014.svg.png",
|
||||||
|
"saarbruecken": "https://thumb.wikimedia.org/wikipedia/de/thumb/f/ff/1._FC_Saarbr%C3%BCcken.svg/langde-330px-1._FC_Saarbr%C3%BCcken.svg.png",
|
||||||
|
"schalke": "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/FC_Schalke_04_Logo.svg/langde-330px-FC_Schalke_04_Logo.svg.png",
|
||||||
|
"schweinfurt": "https://thumb.wikimedia.org/wikipedia/de/thumb/7/7c/1._FC_Schweinfurt_05.svg/langde-330px-1._FC_Schweinfurt_05.svg.png",
|
||||||
|
"st-pauli": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b3/Fc_st_pauli_logo.svg/langde-330px-Fc_st_pauli_logo.svg.png",
|
||||||
|
"stuttgart": "https://thumb.wikimedia.org/wikipedia/commons/thumb/e/eb/VfB_Stuttgart_1893_Logo.svg/langde-330px-VfB_Stuttgart_1893_Logo.svg.png",
|
||||||
|
"stuttgart-ii": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/eb/VfB_Stuttgart_1893_Logo.svg/langde-330px-VfB_Stuttgart_1893_Logo.svg.png",
|
||||||
|
"ulm": "https://thumb.wikimedia.org/wikipedia/commons/thumb/6/6c/SSV_Ulm_1846_Fussball.svg/langde-330px-SSV_Ulm_1846_Fussball.svg.png",
|
||||||
|
"union": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/44/1._FC_Union_Berlin_Logo.svg/langde-330px-1._FC_Union_Berlin_Logo.svg.png",
|
||||||
|
"verl": "https://thumb.wikimedia.org/wikipedia/commons/thumb/c/ce/SC_Verl_Logo.svg/langde-330px-SC_Verl_Logo.svg.png",
|
||||||
|
"viktoria": "https://thumb.wikimedia.org/wikipedia/commons/thumb/d/dc/FC_Viktoria_K%C3%B6ln_1904_Logo.svg/langde-330px-FC_Viktoria_K%C3%B6ln_1904_Logo.svg.png",
|
||||||
|
"wehen": "https://upload.wikimedia.org/wikipedia/de/e/e0/SV_Wehen_Logo.png",
|
||||||
|
"wolfsburg": "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/VfL_Wolfsburg_logo_2026.svg/langde-330px-VfL_Wolfsburg_logo_2026.svg.png",
|
||||||
|
"wuppertal": "https://thumb.wikimedia.org/wikipedia/de/thumb/9/9b/WuppertalerSVLogo.svg/langde-330px-WuppertalerSVLogo.svg.png"
|
||||||
|
}
|
||||||
@@ -14,11 +14,16 @@ import urllib.parse
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
|
|
||||||
OUT = sys.argv[1] if len(sys.argv) > 1 else "app/src/main/assets/crests"
|
OUT = sys.argv[1] if len(sys.argv) > 1 else "app/src/main/assets/crests"
|
||||||
|
URLS = "tools/crest-sources.json"
|
||||||
SRC = "app/src/main/java/dev/castarr/tv/data/TeamFilters.kt"
|
SRC = "app/src/main/java/dev/castarr/tv/data/TeamFilters.kt"
|
||||||
SUMMARY = "https://de.wikipedia.org/api/rest_v1/page/summary/"
|
SUMMARY = "https://de.wikipedia.org/api/rest_v1/page/summary/"
|
||||||
UA = {"User-Agent": "Castarr build script (private use)"}
|
UA = {"User-Agent": "Castarr build script (private use)"}
|
||||||
|
|
||||||
os.makedirs(OUT, exist_ok=True)
|
os.makedirs(OUT, exist_ok=True)
|
||||||
|
# Image URLs are plain text and safe to commit; the artwork itself is not.
|
||||||
|
sources = {}
|
||||||
|
if os.path.exists(URLS):
|
||||||
|
sources = json.load(open(URLS, encoding="utf-8"))
|
||||||
kotlin = open(SRC, encoding="utf-8").read()
|
kotlin = open(SRC, encoding="utf-8").read()
|
||||||
entries = re.findall(r'club\((.*?)\)\s*,\s*(?://.*)?$', kotlin, re.M | re.S)
|
entries = re.findall(r'club\((.*?)\)\s*,\s*(?://.*)?$', kotlin, re.M | re.S)
|
||||||
clubs = []
|
clubs = []
|
||||||
@@ -35,11 +40,15 @@ for key, full_name in clubs:
|
|||||||
skipped += 1
|
skipped += 1
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
url = SUMMARY + urllib.parse.quote(full_name)
|
thumb = sources.get(key)
|
||||||
with urllib.request.urlopen(urllib.request.Request(url, headers=UA), timeout=20) as r:
|
|
||||||
thumb = json.load(r).get("thumbnail", {}).get("source")
|
|
||||||
if not thumb:
|
if not thumb:
|
||||||
raise ValueError("no thumbnail")
|
url = SUMMARY + urllib.parse.quote(full_name)
|
||||||
|
with urllib.request.urlopen(urllib.request.Request(url, headers=UA), timeout=20) as r:
|
||||||
|
thumb = json.load(r).get("thumbnail", {}).get("source")
|
||||||
|
if not thumb:
|
||||||
|
raise ValueError("no thumbnail")
|
||||||
|
sources[key] = thumb.split("?")[0]
|
||||||
|
thumb = sources[key]
|
||||||
with urllib.request.urlopen(urllib.request.Request(thumb, headers=UA), timeout=20) as r:
|
with urllib.request.urlopen(urllib.request.Request(thumb, headers=UA), timeout=20) as r:
|
||||||
data = r.read()
|
data = r.read()
|
||||||
with open(target, "wb") as f:
|
with open(target, "wb") as f:
|
||||||
@@ -51,4 +60,8 @@ for key, full_name in clubs:
|
|||||||
# Wikipedia rate-limits bursts; this runs rarely and caches.
|
# Wikipedia rate-limits bursts; this runs rarely and caches.
|
||||||
time.sleep(1.2)
|
time.sleep(1.2)
|
||||||
|
|
||||||
|
with open(URLS, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(dict(sorted(sources.items())), f, indent=2, ensure_ascii=False)
|
||||||
|
f.write("\n")
|
||||||
|
|
||||||
print(f"crests: {fetched} geladen, {skipped} vorhanden, {failed} fehlgeschlagen")
|
print(f"crests: {fetched} geladen, {skipped} vorhanden, {failed} fehlgeschlagen")
|
||||||
|
|||||||
Reference in New Issue
Block a user