mirror of
https://github.com/meowarex/rl-mobile.git
synced 2026-08-26 22:17:44 +10:00
@@ -32,11 +32,25 @@
|
|||||||
"order": 20,
|
"order": 20,
|
||||||
"fileNames": [
|
"fileNames": [
|
||||||
"lyrics-rl-api.patch",
|
"lyrics-rl-api.patch",
|
||||||
"lyrics-rl-api-observer.patch"
|
"lyrics-rl-api-observer.patch",
|
||||||
|
"lyrics-rl-api-isrc.patch"
|
||||||
],
|
],
|
||||||
"title": "Radiant Lyrics API - WIP",
|
"title": "Radiant Lyrics API - WIP",
|
||||||
"description": "Use Radiant Lyrics API to fetch Lyrics (Higher quality & More providers)",
|
"description": "Use Radiant Lyrics API to fetch Lyrics (Higher quality & More providers). Pick how the highlight moves: whole Line or word-by-word (Word).",
|
||||||
"default": false
|
"default": false,
|
||||||
|
"defaultVariantIndex": 0,
|
||||||
|
"variants": [
|
||||||
|
{
|
||||||
|
"title": "Line",
|
||||||
|
"fileNames": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Word",
|
||||||
|
"fileNames": [
|
||||||
|
"lyrics-rl-api-word.patch"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "PlayerBackdrop",
|
"id": "PlayerBackdrop",
|
||||||
@@ -144,7 +158,7 @@
|
|||||||
"type": "toggle",
|
"type": "toggle",
|
||||||
"key": "cover_everywhere",
|
"key": "cover_everywhere",
|
||||||
"title": "Cover Everywhere - WIP",
|
"title": "Cover Everywhere - WIP",
|
||||||
"description": "Applies TIDAL's player backdrop to every Compose based page (Home & Collection)",
|
"description": "Applies TIDAL's native blurred album-art backdrop to every Compose based page (Home & Collection)",
|
||||||
"default": false,
|
"default": false,
|
||||||
"inline": true,
|
"inline": true,
|
||||||
"fileNames": [
|
"fileNames": [
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ data class PatchOptions(
|
|||||||
if (spec.variants.isEmpty()) {
|
if (spec.variants.isEmpty()) {
|
||||||
if (!enabled) addAll(spec.fileNames)
|
if (!enabled) addAll(spec.fileNames)
|
||||||
} else {
|
} else {
|
||||||
|
// base fileNames apply for every variant while enabled; disable them when off
|
||||||
|
if (!enabled) addAll(spec.fileNames)
|
||||||
val selected = variantIndex(spec)
|
val selected = variantIndex(spec)
|
||||||
spec.variants.forEachIndexed { index, variant ->
|
spec.variants.forEachIndexed { index, variant ->
|
||||||
if (!enabled || index != selected) addAll(variant.fileNames)
|
if (!enabled || index != selected) addAll(variant.fileNames)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ data class VariantSpec(
|
|||||||
val title: String,
|
val title: String,
|
||||||
val fileNames: List<String> = emptyList(),
|
val fileNames: List<String> = emptyList(),
|
||||||
val extensionFiles: List<String> = emptyList(),
|
val extensionFiles: List<String> = emptyList(),
|
||||||
|
val enabled: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
/** A single advanced option. Discriminated by `"type"` in JSON. */
|
/** A single advanced option. Discriminated by `"type"` in JSON. */
|
||||||
@@ -173,7 +174,7 @@ fun builtinPatchSpecs(context: Context, json: Json): List<PatchSpec> = try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class EffectiveVariant(val originalIndex: Int, val title: String)
|
data class EffectiveVariant(val originalIndex: Int, val title: String, val enabled: Boolean = true)
|
||||||
|
|
||||||
fun PatchSpec.effectiveVariants(isOptionOn: (OptionSpec.Toggle) -> Boolean): List<EffectiveVariant> {
|
fun PatchSpec.effectiveVariants(isOptionOn: (OptionSpec.Toggle) -> Boolean): List<EffectiveVariant> {
|
||||||
if (variants.isEmpty()) return emptyList()
|
if (variants.isEmpty()) return emptyList()
|
||||||
@@ -186,19 +187,22 @@ fun PatchSpec.effectiveVariants(isOptionOn: (OptionSpec.Toggle) -> Boolean): Lis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return variants.mapIndexedNotNull { index, variant ->
|
return variants.mapIndexedNotNull { index, variant ->
|
||||||
if (index in hidden) null else EffectiveVariant(index, relabel[index] ?: variant.title)
|
if (index in hidden) null
|
||||||
|
else EffectiveVariant(index, relabel[index] ?: variant.title, variant.enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PatchSpec.resolveVariantIndex(stored: Int, isOptionOn: (OptionSpec.Toggle) -> Boolean): Int {
|
fun PatchSpec.resolveVariantIndex(stored: Int, isOptionOn: (OptionSpec.Toggle) -> Boolean): Int {
|
||||||
val visible = effectiveVariants(isOptionOn)
|
val visible = effectiveVariants(isOptionOn)
|
||||||
if (visible.isEmpty() || visible.any { it.originalIndex == stored }) return stored
|
// never resolve onto a greyed/disabled variant
|
||||||
|
if (visible.isEmpty() || visible.any { it.originalIndex == stored && it.enabled }) return stored
|
||||||
val relabelKeys = buildSet {
|
val relabelKeys = buildSet {
|
||||||
for (option in advancedOptions) {
|
for (option in advancedOptions) {
|
||||||
if (option is OptionSpec.Toggle && isOptionOn(option)) addAll(option.relabelVariants.keys)
|
if (option is OptionSpec.Toggle && isOptionOn(option)) addAll(option.relabelVariants.keys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return visible.firstOrNull { it.originalIndex in relabelKeys }?.originalIndex
|
return visible.firstOrNull { it.originalIndex in relabelKeys && it.enabled }?.originalIndex
|
||||||
|
?: visible.firstOrNull { it.enabled }?.originalIndex
|
||||||
?: visible.first().originalIndex
|
?: visible.first().originalIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-4
@@ -169,15 +169,18 @@ fun PatchSelectionAccordion(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(bottom = 8.dp),
|
.padding(bottom = 8.dp),
|
||||||
) {
|
) {
|
||||||
if (patch.variants.isNotEmpty()) {
|
val resolvedVariant =
|
||||||
val effective = patch.effectiveVariants { optionState.toggle(patch, it) }
|
if (patch.variants.isNotEmpty())
|
||||||
val resolved = patch.resolveVariantIndex(variantIndex(patch)) {
|
patch.resolveVariantIndex(variantIndex(patch)) {
|
||||||
optionState.toggle(patch, it)
|
optionState.toggle(patch, it)
|
||||||
}
|
}
|
||||||
|
else -1
|
||||||
|
if (patch.variants.isNotEmpty()) {
|
||||||
|
val effective = patch.effectiveVariants { optionState.toggle(patch, it) }
|
||||||
PatchVariantSelector(
|
PatchVariantSelector(
|
||||||
variants = effective,
|
variants = effective,
|
||||||
selectedIndex = effective
|
selectedIndex = effective
|
||||||
.indexOfFirst { it.originalIndex == resolved }
|
.indexOfFirst { it.originalIndex == resolvedVariant }
|
||||||
.coerceAtLeast(0),
|
.coerceAtLeast(0),
|
||||||
onSelect = { pos ->
|
onSelect = { pos ->
|
||||||
effective.getOrNull(pos)?.let { onSelectVariant(patch, it.originalIndex) }
|
effective.getOrNull(pos)?.let { onSelectVariant(patch, it.originalIndex) }
|
||||||
@@ -186,6 +189,9 @@ fun PatchSelectionAccordion(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (option in inlineToggles) key(option.key) {
|
for (option in inlineToggles) key(option.key) {
|
||||||
|
val show = option.requiresVariant == null ||
|
||||||
|
option.requiresVariant == resolvedVariant
|
||||||
|
if (show) {
|
||||||
InlineToggleRow(
|
InlineToggleRow(
|
||||||
title = option.title,
|
title = option.title,
|
||||||
description = option.description,
|
description = option.description,
|
||||||
@@ -193,6 +199,7 @@ fun PatchSelectionAccordion(
|
|||||||
onCheckedChange = { optionState.setToggle(patch, option, it) },
|
onCheckedChange = { optionState.setToggle(patch, option, it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (hasSheetOptions) {
|
if (hasSheetOptions) {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
+2
-1
@@ -22,7 +22,8 @@ fun PatchVariantSelector(
|
|||||||
variants.forEachIndexed { index, variant ->
|
variants.forEachIndexed { index, variant ->
|
||||||
SegmentedButton(
|
SegmentedButton(
|
||||||
selected = index == selectedIndex,
|
selected = index == selectedIndex,
|
||||||
onClick = { onSelect(index) },
|
enabled = variant.enabled,
|
||||||
|
onClick = { if (variant.enabled) onSelect(index) },
|
||||||
shape = SegmentedButtonDefaults.itemShape(
|
shape = SegmentedButtonDefaults.itemShape(
|
||||||
index = index,
|
index = index,
|
||||||
count = variants.size,
|
count = variants.size,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ Every Patch & Integration can be disabled/enabled & adjusted exactly how you wan
|
|||||||
- Keeps lyrics controls visible
|
- Keeps lyrics controls visible
|
||||||
- One-handed player controls
|
- One-handed player controls
|
||||||
- Redesigned mini-player with gestures
|
- Redesigned mini-player with gestures
|
||||||
|
- Word Level Lyrics (Word by Word) <3
|
||||||
- Syllable Level lyrics (SoonTM)
|
- Syllable Level lyrics (SoonTM)
|
||||||
|
|
||||||
### Integrations
|
### Integrations
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"tidalVersionCode": 10004,
|
"tidalVersionCode": 10004,
|
||||||
"tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk",
|
"tidalApkUrl": "https://github.com/meowarex/rl-mobile/releases/download/latest/tidal-stock.apk",
|
||||||
"patchesVersion": "0.11.1"
|
"patchesVersion": "0.12.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
.class public final Lradiant/IsrcCache;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
|
||||||
|
# Caches trackId(String) -> ISRC(String), captured from TIDAL's own track mapper
|
||||||
|
|
||||||
|
.field public static final map:Ljava/util/concurrent/ConcurrentHashMap;
|
||||||
|
|
||||||
|
|
||||||
|
.method static constructor <clinit>()V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
new-instance v0, Ljava/util/concurrent/ConcurrentHashMap;
|
||||||
|
|
||||||
|
invoke-direct {v0}, Ljava/util/concurrent/ConcurrentHashMap;-><init>()V
|
||||||
|
|
||||||
|
sput-object v0, Lradiant/IsrcCache;->map:Ljava/util/concurrent/ConcurrentHashMap;
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method private constructor <init>()V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static put(Ljava/lang/String;Ljava/lang/String;)V
|
||||||
|
.locals 1
|
||||||
|
|
||||||
|
if-eqz p0, :ret
|
||||||
|
|
||||||
|
if-eqz p1, :ret
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/IsrcCache;->map:Ljava/util/concurrent/ConcurrentHashMap;
|
||||||
|
|
||||||
|
invoke-virtual {v0, p0, p1}, Ljava/util/concurrent/ConcurrentHashMap;->put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
|
||||||
|
:ret
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public static get(Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
.locals 2
|
||||||
|
|
||||||
|
const/4 v0, 0x0
|
||||||
|
|
||||||
|
if-eqz p0, :none
|
||||||
|
|
||||||
|
sget-object v1, Lradiant/IsrcCache;->map:Ljava/util/concurrent/ConcurrentHashMap;
|
||||||
|
|
||||||
|
invoke-virtual {v1, p0}, Ljava/util/concurrent/ConcurrentHashMap;->get(Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
check-cast v0, Ljava/lang/String;
|
||||||
|
|
||||||
|
:none
|
||||||
|
return-object v0
|
||||||
|
.end method
|
||||||
@@ -156,13 +156,27 @@
|
|||||||
goto :invalid_track
|
goto :invalid_track
|
||||||
|
|
||||||
:title_ok
|
:title_ok
|
||||||
|
# match desktop: primary artist name (artist?.name), fall back to the joined list
|
||||||
|
invoke-virtual {p1}, Lcom/aspiro/wamp/model/MediaItem;->getArtist()Lcom/aspiro/wamp/model/Artist;
|
||||||
|
|
||||||
|
move-result-object v3
|
||||||
|
|
||||||
|
if-eqz v3, :use_artist_names
|
||||||
|
|
||||||
|
invoke-virtual {v3}, Lcom/aspiro/wamp/model/Artist;->getName()Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
if-nez v2, :artist_present
|
||||||
|
|
||||||
|
:use_artist_names
|
||||||
invoke-virtual {p1}, Lcom/aspiro/wamp/model/MediaItem;->getArtistNames()Ljava/lang/String;
|
invoke-virtual {p1}, Lcom/aspiro/wamp/model/MediaItem;->getArtistNames()Ljava/lang/String;
|
||||||
|
|
||||||
move-result-object v2
|
move-result-object v2
|
||||||
|
|
||||||
if-nez v2, :artist_present
|
if-nez v2, :artist_present
|
||||||
|
|
||||||
const-string v3, "bail: getArtistNames() returned null"
|
const-string v3, "bail: artist name null"
|
||||||
|
|
||||||
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
invoke-static {v3}, Lradiant/RLAPILyricsHook;->dlog(Ljava/lang/String;)V
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
.field public final lyricsId:Ljava/lang/String;
|
.field public final lyricsId:Ljava/lang/String;
|
||||||
|
|
||||||
|
.field public static volatile clientIp:Ljava/lang/String;
|
||||||
|
|
||||||
|
|
||||||
# direct methods
|
# direct methods
|
||||||
.method public constructor <init>(Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
.method public constructor <init>(Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
|
||||||
@@ -43,6 +45,8 @@
|
|||||||
|
|
||||||
if-eqz v0, :done
|
if-eqz v0, :done
|
||||||
|
|
||||||
|
invoke-static {}, Lradiant/WordLyrics;->clear()V
|
||||||
|
|
||||||
const/4 v0, 0x0
|
const/4 v0, 0x0
|
||||||
|
|
||||||
sput-object v0, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
|
sput-object v0, Lradiant/RLAPILyricsHook;->currentKey:Ljava/lang/String;
|
||||||
@@ -114,6 +118,99 @@
|
|||||||
return v3
|
return v3
|
||||||
.end method
|
.end method
|
||||||
|
|
||||||
|
# Public client IP (needed for debugging with ratelimits)
|
||||||
|
.method static clientIp()Ljava/lang/String;
|
||||||
|
.locals 6
|
||||||
|
|
||||||
|
sget-object v0, Lradiant/RLAPILyricsWorker;->clientIp:Ljava/lang/String;
|
||||||
|
|
||||||
|
if-eqz v0, :fetch
|
||||||
|
|
||||||
|
return-object v0
|
||||||
|
|
||||||
|
:fetch
|
||||||
|
:try_start
|
||||||
|
new-instance v0, Ljava/net/URL;
|
||||||
|
|
||||||
|
const-string v1, "https://api.ipify.org?format=text"
|
||||||
|
|
||||||
|
invoke-direct {v0, v1}, Ljava/net/URL;-><init>(Ljava/lang/String;)V
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Ljava/net/URL;->openConnection()Ljava/net/URLConnection;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
check-cast v0, Ljava/net/HttpURLConnection;
|
||||||
|
|
||||||
|
const/16 v1, 0x5dc
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setConnectTimeout(I)V
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setReadTimeout(I)V
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->getResponseCode()I
|
||||||
|
|
||||||
|
move-result v1
|
||||||
|
|
||||||
|
const/16 v2, 0xc8
|
||||||
|
|
||||||
|
if-eq v1, v2, :ok
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->disconnect()V
|
||||||
|
|
||||||
|
goto :fail
|
||||||
|
|
||||||
|
:ok
|
||||||
|
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->getInputStream()Ljava/io/InputStream;
|
||||||
|
|
||||||
|
move-result-object v1
|
||||||
|
|
||||||
|
new-instance v2, Ljava/io/InputStreamReader;
|
||||||
|
|
||||||
|
const-string v3, "UTF-8"
|
||||||
|
|
||||||
|
invoke-direct {v2, v1, v3}, Ljava/io/InputStreamReader;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
|
||||||
|
|
||||||
|
new-instance v3, Ljava/io/BufferedReader;
|
||||||
|
|
||||||
|
invoke-direct {v3, v2}, Ljava/io/BufferedReader;-><init>(Ljava/io/Reader;)V
|
||||||
|
|
||||||
|
invoke-virtual {v3}, Ljava/io/BufferedReader;->readLine()Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v4
|
||||||
|
|
||||||
|
invoke-virtual {v3}, Ljava/io/BufferedReader;->close()V
|
||||||
|
|
||||||
|
invoke-virtual {v0}, Ljava/net/HttpURLConnection;->disconnect()V
|
||||||
|
|
||||||
|
if-eqz v4, :fail
|
||||||
|
|
||||||
|
invoke-virtual {v4}, Ljava/lang/String;->trim()Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v4
|
||||||
|
|
||||||
|
invoke-virtual {v4}, Ljava/lang/String;->isEmpty()Z
|
||||||
|
|
||||||
|
move-result v5
|
||||||
|
|
||||||
|
if-nez v5, :fail
|
||||||
|
|
||||||
|
sput-object v4, Lradiant/RLAPILyricsWorker;->clientIp:Ljava/lang/String;
|
||||||
|
|
||||||
|
return-object v4
|
||||||
|
|
||||||
|
:try_end
|
||||||
|
.catchall {:try_start .. :try_end} :catch_all
|
||||||
|
|
||||||
|
:catch_all
|
||||||
|
move-exception v0
|
||||||
|
|
||||||
|
:fail
|
||||||
|
const-string v0, "null"
|
||||||
|
|
||||||
|
return-object v0
|
||||||
|
.end method
|
||||||
|
|
||||||
.method public static fetch(Ljava/lang/String;Z)Ljava/lang/String;
|
.method public static fetch(Ljava/lang/String;Z)Ljava/lang/String;
|
||||||
.locals 7
|
.locals 7
|
||||||
|
|
||||||
@@ -136,6 +233,8 @@
|
|||||||
|
|
||||||
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setConnectTimeout(I)V
|
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setConnectTimeout(I)V
|
||||||
|
|
||||||
|
const v1, 0xafc8
|
||||||
|
|
||||||
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setReadTimeout(I)V
|
invoke-virtual {v0, v1}, Ljava/net/URLConnection;->setReadTimeout(I)V
|
||||||
|
|
||||||
if-eqz p1, :no_auth
|
if-eqz p1, :no_auth
|
||||||
@@ -154,7 +253,9 @@
|
|||||||
|
|
||||||
const-string v1, "x-client-ip"
|
const-string v1, "x-client-ip"
|
||||||
|
|
||||||
const-string v2, "null"
|
invoke-static {}, Lradiant/RLAPILyricsWorker;->clientIp()Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
invoke-virtual {v0, v1, v2}, Ljava/net/URLConnection;->setRequestProperty(Ljava/lang/String;Ljava/lang/String;)V
|
invoke-virtual {v0, v1, v2}, Ljava/net/URLConnection;->setRequestProperty(Ljava/lang/String;Ljava/lang/String;)V
|
||||||
|
|
||||||
@@ -393,20 +494,168 @@
|
|||||||
return-object v1
|
return-object v1
|
||||||
.end method
|
.end method
|
||||||
|
|
||||||
|
# Returns "&isrc=<enc>" for the current track
|
||||||
|
.method private isrcParam()Ljava/lang/String;
|
||||||
|
.locals 6
|
||||||
|
|
||||||
|
iget-object v0, p0, Lradiant/RLAPILyricsWorker;->key:Ljava/lang/String;
|
||||||
|
|
||||||
|
const/4 v1, 0x0
|
||||||
|
|
||||||
|
:loop
|
||||||
|
invoke-static {v0}, Lradiant/IsrcCache;->get(Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
if-eqz v2, :retry
|
||||||
|
|
||||||
|
invoke-virtual {v2}, Ljava/lang/String;->trim()Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
invoke-virtual {v2}, Ljava/lang/String;->isEmpty()Z
|
||||||
|
|
||||||
|
move-result v3
|
||||||
|
|
||||||
|
if-nez v3, :retry
|
||||||
|
|
||||||
|
:try_enc_start
|
||||||
|
invoke-static {v2}, Lradiant/RLAPILyricsWorker;->uric(Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
:try_enc_end
|
||||||
|
.catchall {:try_enc_start .. :try_enc_end} :enc_fail
|
||||||
|
|
||||||
|
:enc_done
|
||||||
|
new-instance v3, Ljava/lang/StringBuilder;
|
||||||
|
|
||||||
|
const-string v4, "&isrc="
|
||||||
|
|
||||||
|
invoke-direct {v3, v4}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V
|
||||||
|
|
||||||
|
invoke-virtual {v3, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||||
|
|
||||||
|
invoke-virtual {v3}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v2
|
||||||
|
|
||||||
|
return-object v2
|
||||||
|
|
||||||
|
:enc_fail
|
||||||
|
move-exception v3
|
||||||
|
|
||||||
|
goto :enc_done
|
||||||
|
|
||||||
|
:retry
|
||||||
|
# keep waiting for TIDAL's mapper to cache the ISRC while this track is still current
|
||||||
|
invoke-direct {p0}, Lradiant/RLAPILyricsWorker;->isCurrent()Z
|
||||||
|
|
||||||
|
move-result v3
|
||||||
|
|
||||||
|
if-eqz v3, :giveup
|
||||||
|
|
||||||
|
const/16 v3, 0x28
|
||||||
|
|
||||||
|
if-ge v1, v3, :giveup
|
||||||
|
|
||||||
|
:try_sleep_start
|
||||||
|
const-wide/16 v3, 0x64
|
||||||
|
|
||||||
|
invoke-static {v3, v4}, Ljava/lang/Thread;->sleep(J)V
|
||||||
|
|
||||||
|
:try_sleep_end
|
||||||
|
.catchall {:try_sleep_start .. :try_sleep_end} :sleep_catch
|
||||||
|
|
||||||
|
:sleep_done
|
||||||
|
add-int/lit8 v1, v1, 0x1
|
||||||
|
|
||||||
|
goto :loop
|
||||||
|
|
||||||
|
:sleep_catch
|
||||||
|
move-exception v3
|
||||||
|
|
||||||
|
goto :sleep_done
|
||||||
|
|
||||||
|
:giveup
|
||||||
|
const-string v0, ""
|
||||||
|
|
||||||
|
return-object v0
|
||||||
|
.end method
|
||||||
|
|
||||||
|
# URL-encode like JS encodeURIComponent (so requests are byte identical to the desktop plugin)
|
||||||
|
.method static uric(Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
.locals 3
|
||||||
|
|
||||||
|
const-string v0, "UTF-8"
|
||||||
|
|
||||||
|
invoke-static {p0, v0}, Ljava/net/URLEncoder;->encode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
const-string v1, "+"
|
||||||
|
|
||||||
|
const-string v2, "%20"
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
const-string v1, "%21"
|
||||||
|
|
||||||
|
const-string v2, "!"
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
const-string v1, "%27"
|
||||||
|
|
||||||
|
const-string v2, "\'"
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
const-string v1, "%28"
|
||||||
|
|
||||||
|
const-string v2, "("
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
const-string v1, "%29"
|
||||||
|
|
||||||
|
const-string v2, ")"
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
const-string v1, "%7E"
|
||||||
|
|
||||||
|
const-string v2, "~"
|
||||||
|
|
||||||
|
invoke-virtual {v0, v1, v2}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
return-object v0
|
||||||
|
.end method
|
||||||
|
|
||||||
.method private runImpl()V
|
.method private runImpl()V
|
||||||
.locals 11
|
.locals 13
|
||||||
|
|
||||||
iget-object v0, p0, Lradiant/RLAPILyricsWorker;->title:Ljava/lang/String;
|
iget-object v0, p0, Lradiant/RLAPILyricsWorker;->title:Ljava/lang/String;
|
||||||
|
|
||||||
const-string v1, "UTF-8"
|
invoke-static {v0}, Lradiant/RLAPILyricsWorker;->uric(Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
|
||||||
invoke-static {v0, v1}, Ljava/net/URLEncoder;->encode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
|
|
||||||
|
|
||||||
move-result-object v0
|
move-result-object v0
|
||||||
|
|
||||||
iget-object v2, p0, Lradiant/RLAPILyricsWorker;->artist:Ljava/lang/String;
|
iget-object v2, p0, Lradiant/RLAPILyricsWorker;->artist:Ljava/lang/String;
|
||||||
|
|
||||||
invoke-static {v2, v1}, Ljava/net/URLEncoder;->encode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
|
invoke-static {v2}, Lradiant/RLAPILyricsWorker;->uric(Ljava/lang/String;)Ljava/lang/String;
|
||||||
|
|
||||||
move-result-object v1
|
move-result-object v1
|
||||||
|
|
||||||
@@ -424,6 +673,12 @@
|
|||||||
|
|
||||||
invoke-virtual {v2, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
invoke-virtual {v2, v1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||||
|
|
||||||
|
invoke-direct {p0}, Lradiant/RLAPILyricsWorker;->isrcParam()Ljava/lang/String;
|
||||||
|
|
||||||
|
move-result-object v0
|
||||||
|
|
||||||
|
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||||
|
|
||||||
const-string v0, "&platform=rl-mobile"
|
const-string v0, "&platform=rl-mobile"
|
||||||
|
|
||||||
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
|
||||||
@@ -479,6 +734,8 @@
|
|||||||
goto :request_failed
|
goto :request_failed
|
||||||
|
|
||||||
:got_body
|
:got_body
|
||||||
|
move-object v11, v3
|
||||||
|
|
||||||
invoke-static {v3}, Lradiant/RLAPILyricsWorker;->parseLines(Ljava/lang/String;)Ljava/util/ArrayList;
|
invoke-static {v3}, Lradiant/RLAPILyricsWorker;->parseLines(Ljava/lang/String;)Ljava/util/ArrayList;
|
||||||
|
|
||||||
move-result-object v3
|
move-result-object v3
|
||||||
@@ -536,6 +793,8 @@
|
|||||||
return-void
|
return-void
|
||||||
|
|
||||||
:key_ok
|
:key_ok
|
||||||
|
invoke-static {v11}, Lradiant/WordLyrics;->ingest(Ljava/lang/String;)V
|
||||||
|
|
||||||
iget-object v4, p0, Lradiant/RLAPILyricsWorker;->vm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
iget-object v4, p0, Lradiant/RLAPILyricsWorker;->vm:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||||
|
|
||||||
iget-object v5, v4, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->O:Lkotlinx/coroutines/flow/MutableStateFlow;
|
iget-object v5, v4, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;->O:Lkotlinx/coroutines/flow/MutableStateFlow;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
|||||||
|
.class public final Lradiant/WordLyricsTick;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.implements Ljava/lang/Runnable;
|
||||||
|
|
||||||
|
# 50ms clock tick driver for word level lyrics highlighting
|
||||||
|
|
||||||
|
.method public constructor <init>()V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
|
|
||||||
|
.method public run()V
|
||||||
|
.locals 0
|
||||||
|
|
||||||
|
invoke-static {}, Lradiant/WordLyrics;->tick()V
|
||||||
|
|
||||||
|
return-void
|
||||||
|
.end method
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
--- a/cg0/h.smali
|
||||||
|
+++ b/cg0/h.smali
|
||||||
|
@@ -363,6 +363,23 @@
|
||||||
|
.method public static final d(Lcom/tidal/sdk/tidalapi/generated/models/TracksResourceObject;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/Map;)Lcom/tidal/android/tidalapi/domain/model/o;
|
||||||
|
.locals 23
|
||||||
|
|
||||||
|
+ invoke-virtual/range {p0 .. p0}, Lcom/tidal/sdk/tidalapi/generated/models/TracksResourceObject;->getAttributes()Lcom/tidal/sdk/tidalapi/generated/models/TracksAttributes;
|
||||||
|
+
|
||||||
|
+ move-result-object v1
|
||||||
|
+
|
||||||
|
+ if-eqz v1, :rl_isrc_skip
|
||||||
|
+
|
||||||
|
+ invoke-virtual/range {p0 .. p0}, Lcom/tidal/sdk/tidalapi/generated/models/TracksResourceObject;->getId()Ljava/lang/String;
|
||||||
|
+
|
||||||
|
+ move-result-object v0
|
||||||
|
+
|
||||||
|
+ invoke-virtual {v1}, Lcom/tidal/sdk/tidalapi/generated/models/TracksAttributes;->getIsrc()Ljava/lang/String;
|
||||||
|
+
|
||||||
|
+ move-result-object v1
|
||||||
|
+
|
||||||
|
+ invoke-static {v0, v1}, Lradiant/IsrcCache;->put(Ljava/lang/String;Ljava/lang/String;)V
|
||||||
|
+
|
||||||
|
+ :rl_isrc_skip
|
||||||
|
move-object/from16 v0, p4
|
||||||
|
|
||||||
|
move-object/from16 v1, p7
|
||||||
@@ -1,14 +1,23 @@
|
|||||||
--- a/com/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a.smali
|
--- a/com/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a.smali
|
||||||
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a.smali
|
+++ b/com/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a.smali
|
||||||
@@ -191,6 +191,13 @@
|
@@ -58,6 +58,8 @@
|
||||||
|
.line 5
|
||||||
|
int-to-long p1, p1
|
||||||
|
|
||||||
|
+ invoke-static {p1, p2}, Lradiant/WordLyrics;->onProgress(J)V
|
||||||
|
+
|
||||||
|
.line 6
|
||||||
|
iget-object v0, p0, Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel$observeLyricsProgress$1$a;->a:Lcom/tidal/android/feature/playerscreen/ui/PlayerViewModel;
|
||||||
|
|
||||||
|
@@ -191,6 +193,13 @@
|
||||||
|
|
||||||
.line 67
|
.line 67
|
||||||
:cond_3
|
:cond_3
|
||||||
+ sget-boolean v6, Lradiant/RLAPILyricsHook;->isRlState:Z # read RL flag
|
+ sget-boolean v6, Lradiant/RLAPILyricsHook;->isRlState:Z
|
||||||
+
|
+
|
||||||
+ if-eqz v6, :radiant_skip # skip if not RL
|
+ if-eqz v6, :radiant_skip
|
||||||
+
|
+
|
||||||
+ const/4 v5, -0x1 # RL no-match default = -1
|
+ const/4 v5, -0x1
|
||||||
+
|
+
|
||||||
+ :radiant_skip
|
+ :radiant_skip
|
||||||
if-gez v4, :cond_4
|
if-gez v4, :cond_4
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
--- a/com/tidal/android/feature/playerscreen/ui/composables/f2.smali
|
||||||
|
+++ b/com/tidal/android/feature/playerscreen/ui/composables/f2.smali
|
||||||
|
@@ -87,7 +87,7 @@
|
||||||
|
|
||||||
|
# virtual methods
|
||||||
|
.method public final invoke(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
|
||||||
|
- .locals 28
|
||||||
|
+ .locals 40
|
||||||
|
|
||||||
|
.line 1
|
||||||
|
move-object/from16 v0, p0
|
||||||
|
@@ -313,6 +313,8 @@
|
||||||
|
.line 100
|
||||||
|
check-cast v1, Lcom/tidal/android/feature/playerscreen/ui/f;
|
||||||
|
|
||||||
|
+ move/16 v33, v2
|
||||||
|
+
|
||||||
|
.line 101
|
||||||
|
.line 102
|
||||||
|
const v3, 0x6b82e849
|
||||||
|
@@ -801,6 +803,26 @@
|
||||||
|
.line 331
|
||||||
|
iget-object v3, v1, Lcom/tidal/android/feature/playerscreen/ui/f;->a:Ljava/lang/String;
|
||||||
|
|
||||||
|
+ iget v4, v0, Lcom/tidal/android/feature/playerscreen/ui/composables/f2;->b:I
|
||||||
|
+
|
||||||
|
+ move/16 v34, v4
|
||||||
|
+
|
||||||
|
+ iget-object v4, v1, Lcom/tidal/android/feature/playerscreen/ui/f;->a:Ljava/lang/String;
|
||||||
|
+
|
||||||
|
+ move-object/16 v35, v4
|
||||||
|
+
|
||||||
|
+ iget-wide v4, v0, Lcom/tidal/android/feature/playerscreen/ui/composables/f2;->d:J
|
||||||
|
+
|
||||||
|
+ move-wide/16 v36, v4
|
||||||
|
+
|
||||||
|
+ iget-wide v4, v0, Lcom/tidal/android/feature/playerscreen/ui/composables/f2;->e:J
|
||||||
|
+
|
||||||
|
+ move-wide/16 v38, v4
|
||||||
|
+
|
||||||
|
+ invoke-static/range {v33 .. v39}, Lradiant/WordLyrics;->render(IILjava/lang/String;JJ)Landroidx/compose/ui/text/AnnotatedString;
|
||||||
|
+
|
||||||
|
+ move-result-object v3
|
||||||
|
+
|
||||||
|
.line 332
|
||||||
|
.line 333
|
||||||
|
invoke-interface {v2}, Landroidx/compose/runtime/State;->getValue()Ljava/lang/Object;
|
||||||
|
@@ -898,7 +920,7 @@
|
||||||
|
|
||||||
|
.line 377
|
||||||
|
.line 378
|
||||||
|
- invoke-static/range {v3 .. v27}, Landroidx/compose/material3/TextKt;->Text--4IGK_g(Ljava/lang/String;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZIILam0/l;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V
|
||||||
|
+ invoke-static/range {v3 .. v27}, Landroidx/compose/material3/TextKt;->Text--4IGK_g(Landroidx/compose/ui/text/AnnotatedString;Landroidx/compose/ui/Modifier;JJLandroidx/compose/ui/text/font/FontStyle;Landroidx/compose/ui/text/font/FontWeight;Landroidx/compose/ui/text/font/FontFamily;JLandroidx/compose/ui/text/style/TextDecoration;Landroidx/compose/ui/text/style/TextAlign;JIZILjava/util/Map;Lam0/l;Landroidx/compose/ui/text/TextStyle;Landroidx/compose/runtime/Composer;III)V
|
||||||
|
|
||||||
|
.line 379
|
||||||
|
.line 380
|
||||||
+17
-3
@@ -32,11 +32,25 @@
|
|||||||
"order": 20,
|
"order": 20,
|
||||||
"fileNames": [
|
"fileNames": [
|
||||||
"lyrics-rl-api.patch",
|
"lyrics-rl-api.patch",
|
||||||
"lyrics-rl-api-observer.patch"
|
"lyrics-rl-api-observer.patch",
|
||||||
|
"lyrics-rl-api-isrc.patch"
|
||||||
],
|
],
|
||||||
"title": "Radiant Lyrics API - WIP",
|
"title": "Radiant Lyrics API - WIP",
|
||||||
"description": "Use Radiant Lyrics API to fetch Lyrics (Higher quality & More providers)",
|
"description": "Use Radiant Lyrics API to fetch Lyrics (Higher quality & More providers). Pick how the highlight moves: whole Line or word-by-word (Word).",
|
||||||
"default": false
|
"default": false,
|
||||||
|
"defaultVariantIndex": 0,
|
||||||
|
"variants": [
|
||||||
|
{
|
||||||
|
"title": "Line",
|
||||||
|
"fileNames": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Word",
|
||||||
|
"fileNames": [
|
||||||
|
"lyrics-rl-api-word.patch"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "PlayerBackdrop",
|
"id": "PlayerBackdrop",
|
||||||
|
|||||||
Reference in New Issue
Block a user