Yomiroll: fix no episode error when free episodes are available (#1498)
This commit is contained in:
@ -6,7 +6,7 @@ ext {
|
|||||||
extName = 'Yomiroll'
|
extName = 'Yomiroll'
|
||||||
pkgNameSuffix = 'all.kamyroll'
|
pkgNameSuffix = 'all.kamyroll'
|
||||||
extClass = '.Yomiroll'
|
extClass = '.Yomiroll'
|
||||||
extVersionCode = 20
|
extVersionCode = 21
|
||||||
libVersion = '13'
|
libVersion = '13'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.animeextension.all.kamyroll
|
package eu.kanade.tachiyomi.animeextension.all.kamyroll
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
|
import android.net.Uri
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
import eu.kanade.tachiyomi.network.POST
|
import eu.kanade.tachiyomi.network.POST
|
||||||
import kotlinx.serialization.decodeFromString
|
import kotlinx.serialization.decodeFromString
|
||||||
@ -18,6 +19,7 @@ import java.net.HttpURLConnection
|
|||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
import java.net.PasswordAuthentication
|
import java.net.PasswordAuthentication
|
||||||
import java.net.Proxy
|
import java.net.Proxy
|
||||||
|
import java.text.MessageFormat
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|
||||||
@ -25,6 +27,7 @@ class AccessTokenInterceptor(
|
|||||||
private val crUrl: String,
|
private val crUrl: String,
|
||||||
private val json: Json,
|
private val json: Json,
|
||||||
private val preferences: SharedPreferences,
|
private val preferences: SharedPreferences,
|
||||||
|
private val PREF_USE_LOCAL_Token: String,
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
override fun intercept(chain: Interceptor.Chain): Response {
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
@ -41,7 +44,7 @@ class AccessTokenInterceptor(
|
|||||||
if (accessTokenN != newAccessToken) {
|
if (accessTokenN != newAccessToken) {
|
||||||
return chain.proceed(newRequestWithAccessToken(request, newAccessToken))
|
return chain.proceed(newRequestWithAccessToken(request, newAccessToken))
|
||||||
}
|
}
|
||||||
val refreshedToken = refreshAccessToken(TOKEN_PREF_KEY)
|
val refreshedToken = getAccessToken(true)
|
||||||
// Retry the request
|
// Retry the request
|
||||||
return chain.proceed(
|
return chain.proceed(
|
||||||
newRequestWithAccessToken(chain.request(), refreshedToken),
|
newRequestWithAccessToken(chain.request(), refreshedToken),
|
||||||
@ -53,58 +56,69 @@ class AccessTokenInterceptor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun newRequestWithAccessToken(request: Request, tokenData: AccessToken): Request {
|
private fun newRequestWithAccessToken(request: Request, tokenData: AccessToken): Request {
|
||||||
return request.newBuilder()
|
return request.newBuilder().let {
|
||||||
.header("authorization", "${tokenData.token_type} ${tokenData.access_token}")
|
it.header("authorization", "${tokenData.token_type} ${tokenData.access_token}")
|
||||||
.build()
|
val requestUrl = Uri.decode(request.url.toString())
|
||||||
|
if (requestUrl.contains("/cms/v2")) {
|
||||||
|
it.url(
|
||||||
|
MessageFormat.format(
|
||||||
|
requestUrl,
|
||||||
|
tokenData.bucket,
|
||||||
|
tokenData.policy,
|
||||||
|
tokenData.signature,
|
||||||
|
tokenData.key_pair_id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
it.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAccessToken(): AccessToken {
|
fun getAccessToken(force: Boolean = false): AccessToken {
|
||||||
return preferences.getString(TOKEN_PREF_KEY, null)?.toAccessToken()
|
val token = preferences.getString(TOKEN_PREF_KEY, null)
|
||||||
?: refreshAccessToken(TOKEN_PREF_KEY)
|
return if (!force && token != null) {
|
||||||
}
|
token.toAccessToken()
|
||||||
|
} else {
|
||||||
fun getLocalToken(force: Boolean = false): AccessToken? {
|
synchronized(this) {
|
||||||
if (!preferences.getBoolean(PREF_FETCH_LOCAL_SUBS, false) && !force) return null
|
if (!preferences.getBoolean(PREF_USE_LOCAL_Token, false)) {
|
||||||
synchronized(this) {
|
refreshAccessToken()
|
||||||
val now = System.currentTimeMillis() + 1800000 // add 30 minutes for safety
|
} else {
|
||||||
val localToken = preferences.getString(LOCAL_TOKEN_PREF_KEY, null)?.toAccessToken()
|
refreshAccessToken(false)
|
||||||
return if (force || localToken == null || localToken.policyExpire!! < now) {
|
}
|
||||||
refreshAccessToken(LOCAL_TOKEN_PREF_KEY, false)
|
|
||||||
} else {
|
|
||||||
localToken
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeLocalToken() {
|
fun removeToken() {
|
||||||
preferences.edit().putString(LOCAL_TOKEN_PREF_KEY, null).apply()
|
preferences.edit().putString(TOKEN_PREF_KEY, null).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshAccessToken(PREF_KEY: String, useProxy: Boolean = true): AccessToken {
|
private fun refreshAccessToken(useProxy: Boolean = true): AccessToken {
|
||||||
val client = OkHttpClient().newBuilder().build()
|
removeToken()
|
||||||
Authenticator.setDefault(
|
val client = OkHttpClient().newBuilder().let {
|
||||||
object : Authenticator() {
|
if (useProxy) {
|
||||||
override fun getPasswordAuthentication(): PasswordAuthentication {
|
Authenticator.setDefault(
|
||||||
return PasswordAuthentication("crunblocker", "crunblocker".toCharArray())
|
object : Authenticator() {
|
||||||
}
|
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||||
},
|
return PasswordAuthentication("crunblocker", "crunblocker".toCharArray())
|
||||||
)
|
}
|
||||||
val usedClient = if (useProxy) {
|
},
|
||||||
client.newBuilder()
|
)
|
||||||
.proxy(
|
it.proxy(
|
||||||
Proxy(
|
Proxy(
|
||||||
Proxy.Type.SOCKS,
|
Proxy.Type.SOCKS,
|
||||||
InetSocketAddress("cr-unblocker.us.to", 1080),
|
InetSocketAddress("cr-unblocker.us.to", 1080),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
} else {
|
} else {
|
||||||
client
|
it.build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val response = usedClient.newCall(getRequest(client)).execute()
|
val response = client.newCall(getRequest()).execute()
|
||||||
val parsedJson = json.decodeFromString<AccessToken>(response.body.string())
|
val parsedJson = json.decodeFromString<AccessToken>(response.body.string())
|
||||||
|
|
||||||
val policy = usedClient.newCall(newRequestWithAccessToken(GET("$crUrl/index/v2"), parsedJson)).execute()
|
val policy = client.newCall(newRequestWithAccessToken(GET("$crUrl/index/v2"), parsedJson)).execute()
|
||||||
val policyJson = json.decodeFromString<Policy>(policy.body.string())
|
val policyJson = json.decodeFromString<Policy>(policy.body.string())
|
||||||
val allTokens = AccessToken(
|
val allTokens = AccessToken(
|
||||||
parsedJson.access_token,
|
parsedJson.access_token,
|
||||||
@ -115,19 +129,24 @@ class AccessTokenInterceptor(
|
|||||||
policyJson.cms.bucket,
|
policyJson.cms.bucket,
|
||||||
DateFormatter.parse(policyJson.cms.expires)?.time,
|
DateFormatter.parse(policyJson.cms.expires)?.time,
|
||||||
)
|
)
|
||||||
preferences.edit().putString(PREF_KEY, allTokens.toJsonString()).apply()
|
|
||||||
|
preferences.edit().putString(TOKEN_PREF_KEY, allTokens.toJsonString()).apply()
|
||||||
return allTokens
|
return allTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getRequest(client: OkHttpClient): Request {
|
private fun getRequest(): Request {
|
||||||
|
val client = OkHttpClient().newBuilder().build()
|
||||||
val refreshTokenResp = client.newCall(
|
val refreshTokenResp = client.newCall(
|
||||||
GET("https://raw.githubusercontent.com/Samfun75/File-host/main/aniyomi/refreshToken.txt"),
|
GET("https://raw.githubusercontent.com/Samfun75/File-host/main/aniyomi/refreshToken.txt"),
|
||||||
).execute()
|
).execute()
|
||||||
val refreshToken = refreshTokenResp.body.string().replace("[\n\r]".toRegex(), "")
|
val refreshToken = refreshTokenResp.body.string().replace("[\n\r]".toRegex(), "")
|
||||||
val headers = Headers.headersOf(
|
val headers = Headers.Builder()
|
||||||
"Content-Type", "application/x-www-form-urlencoded",
|
.add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
"Authorization", "Basic a3ZvcGlzdXZ6Yy0teG96Y21kMXk6R21JSTExenVPVnRnTjdlSWZrSlpibzVuLTRHTlZ0cU8="
|
.add(
|
||||||
)
|
"Authorization",
|
||||||
|
"Basic a3ZvcGlzdXZ6Yy0teG96Y21kMXk6R21JSTExenVPVnRnTjdlSWZrSlpibzVuLTRHTlZ0cU8=",
|
||||||
|
)
|
||||||
|
.build()
|
||||||
val postBody = "grant_type=refresh_token&refresh_token=$refreshToken&scope=offline_access".toRequestBody(
|
val postBody = "grant_type=refresh_token&refresh_token=$refreshToken&scope=offline_access".toRequestBody(
|
||||||
"application/x-www-form-urlencoded".toMediaType(),
|
"application/x-www-form-urlencoded".toMediaType(),
|
||||||
)
|
)
|
||||||
@ -144,8 +163,6 @@ class AccessTokenInterceptor(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TOKEN_PREF_KEY = "access_token_data"
|
private const val TOKEN_PREF_KEY = "access_token_data"
|
||||||
private const val LOCAL_TOKEN_PREF_KEY = "local_access_token_data"
|
|
||||||
private const val PREF_FETCH_LOCAL_SUBS = "preferred_local_subs"
|
|
||||||
|
|
||||||
private val DateFormatter by lazy {
|
private val DateFormatter by lazy {
|
||||||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH)
|
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ENGLISH)
|
||||||
|
@ -58,7 +58,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val tokenInterceptor = AccessTokenInterceptor(crUrl, json, preferences)
|
private val tokenInterceptor = AccessTokenInterceptor(crUrl, json, preferences, PREF_USE_LOCAL_Token)
|
||||||
|
|
||||||
override val client: OkHttpClient = OkHttpClient().newBuilder()
|
override val client: OkHttpClient = OkHttpClient().newBuilder()
|
||||||
.addInterceptor(tokenInterceptor).build()
|
.addInterceptor(tokenInterceptor).build()
|
||||||
@ -73,8 +73,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
private const val PREF_SUB = "preferred_sub"
|
private const val PREF_SUB = "preferred_sub"
|
||||||
private const val PREF_SUB_TYPE = "preferred_sub_type"
|
private const val PREF_SUB_TYPE = "preferred_sub_type"
|
||||||
|
|
||||||
// there is one in AccessTokenInterceptor too for below
|
private const val PREF_USE_LOCAL_Token = "preferred_local_Token"
|
||||||
private const val PREF_FETCH_LOCAL_SUBS = "preferred_local_subs"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================== Popular ===============================
|
// ============================== Popular ===============================
|
||||||
@ -203,14 +202,15 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val body = episodeResp.body.string()
|
val body = episodeResp.body.string()
|
||||||
val episodes =
|
val episodes =
|
||||||
json.decodeFromString<EpisodeResult>(body)
|
json.decodeFromString<EpisodeResult>(body)
|
||||||
episodes.data.sortedBy { it.episode_number }.parallelMap { ep ->
|
episodes.data.sortedBy { it.episode_number }.parallelMap EpisodeMap@{ ep ->
|
||||||
SEpisode.create().apply {
|
SEpisode.create().apply {
|
||||||
url = EpisodeData(
|
url = EpisodeData(
|
||||||
ep.versions?.map { Pair(it.mediaId, it.audio_locale) }
|
ep.versions?.map { Pair(it.mediaId, it.audio_locale) }
|
||||||
?: listOf(
|
?: listOf(
|
||||||
Pair(
|
Pair(
|
||||||
ep.streams_link!!.substringAfter("videos/")
|
ep.streams_link?.substringAfter("videos/")
|
||||||
.substringBefore("/streams"),
|
?.substringBefore("/streams")
|
||||||
|
?: return@EpisodeMap null,
|
||||||
ep.audio_locale,
|
ep.audio_locale,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -226,7 +226,7 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
?.joinToString { it.audio_locale.substringBefore("-") }
|
?.joinToString { it.audio_locale.substringBefore("-") }
|
||||||
?: ep.audio_locale.substringBefore("-")
|
?: ep.audio_locale.substringBefore("-")
|
||||||
}
|
}
|
||||||
}
|
}.filterNotNull()
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}.filterNotNull().flatten()
|
}.filterNotNull().flatten()
|
||||||
}.flatten().reversed()
|
}.flatten().reversed()
|
||||||
@ -247,13 +247,19 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
|
override fun fetchVideoList(episode: SEpisode): Observable<List<Video>> {
|
||||||
val urlJson = json.decodeFromString<EpisodeData>(episode.url)
|
val urlJson = json.decodeFromString<EpisodeData>(episode.url)
|
||||||
val dubLocale = preferences.getString("preferred_audio", "en-US")!!
|
val dubLocale = preferences.getString("preferred_audio", "en-US")!!
|
||||||
val proxyToken = tokenInterceptor.getAccessToken()
|
|
||||||
val localToken = tokenInterceptor.getLocalToken()
|
if (urlJson.ids.isEmpty()) throw Exception("No IDs found for episode")
|
||||||
|
val isUsingLocalToken = preferences.getBoolean(PREF_USE_LOCAL_Token, false)
|
||||||
|
|
||||||
val videoList = urlJson.ids.filter {
|
val videoList = urlJson.ids.filter {
|
||||||
it.second == dubLocale || it.second == "ja-JP" || it.second == "en-US" || it.second == ""
|
it.second == dubLocale ||
|
||||||
|
it.second == "ja-JP" ||
|
||||||
|
it.second == "en-US" ||
|
||||||
|
it.second == "" ||
|
||||||
|
if (isUsingLocalToken) it.second == urlJson.ids.first().second else false
|
||||||
}.parallelMap { media ->
|
}.parallelMap { media ->
|
||||||
runCatching {
|
runCatching {
|
||||||
extractVideo(media, proxyToken, localToken)
|
extractVideo(media)
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
}.filterNotNull().flatten()
|
}.filterNotNull().flatten()
|
||||||
|
|
||||||
@ -262,23 +268,11 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
// ============================= Utilities ==============================
|
// ============================= Utilities ==============================
|
||||||
|
|
||||||
private fun extractVideo(
|
private fun extractVideo(media: Pair<String, String>): List<Video> {
|
||||||
media: Pair<String, String>,
|
|
||||||
proxyToken: AccessToken,
|
|
||||||
localToken: AccessToken?,
|
|
||||||
): List<Video> {
|
|
||||||
val (mediaId, aud) = media
|
val (mediaId, aud) = media
|
||||||
val response = client.newCall(getVideoRequest(mediaId, proxyToken)).execute()
|
val response = client.newCall(getVideoRequest(mediaId)).execute()
|
||||||
val streams = json.decodeFromString<VideoStreams>(response.body.string())
|
val streams = json.decodeFromString<VideoStreams>(response.body.string())
|
||||||
|
|
||||||
val localStreams =
|
|
||||||
if (aud == "ja-JP" && preferences.getBoolean(PREF_FETCH_LOCAL_SUBS, false)) {
|
|
||||||
val localResponse = client.newCall(getVideoRequest(mediaId, localToken!!)).execute()
|
|
||||||
json.decodeFromString(localResponse.body.string())
|
|
||||||
} else {
|
|
||||||
VideoStreams()
|
|
||||||
}
|
|
||||||
|
|
||||||
var subsList = emptyList<Track>()
|
var subsList = emptyList<Track>()
|
||||||
val subLocale = preferences.getString("preferred_sub", "en-US")!!.getLocale()
|
val subLocale = preferences.getString("preferred_sub", "en-US")!!.getLocale()
|
||||||
try {
|
try {
|
||||||
@ -287,29 +281,17 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
val sub = json.decodeFromString<Subtitle>(value.jsonObject.toString())
|
val sub = json.decodeFromString<Subtitle>(value.jsonObject.toString())
|
||||||
tempSubs.add(Track(sub.url, sub.locale.getLocale()))
|
tempSubs.add(Track(sub.url, sub.locale.getLocale()))
|
||||||
}
|
}
|
||||||
localStreams.subtitles?.entries?.map { (_, value) ->
|
|
||||||
val sub = json.decodeFromString<Subtitle>(value.jsonObject.toString())
|
|
||||||
tempSubs.add(Track(sub.url, sub.locale.getLocale()))
|
|
||||||
}
|
|
||||||
subsList = tempSubs.sortedWith(
|
subsList = tempSubs.sortedWith(
|
||||||
compareBy(
|
compareBy(
|
||||||
{ it.lang },
|
{ it.lang },
|
||||||
{ it.lang.contains(subLocale) },
|
{ it.lang.contains(subLocale) },
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
} catch (_: Error) {
|
} catch (_: Error) {}
|
||||||
}
|
|
||||||
|
|
||||||
val audLang = aud.ifBlank { streams.audio_locale } ?: localStreams.audio_locale ?: "ja-JP"
|
val audLang = aud.ifBlank { streams.audio_locale } ?: "ja-JP"
|
||||||
val videoList = mutableListOf<Video>()
|
return getStreams(streams, audLang, subsList)
|
||||||
videoList.addAll(
|
|
||||||
getStreams(streams, audLang, subsList),
|
|
||||||
)
|
|
||||||
videoList.addAll(
|
|
||||||
getStreams(localStreams, audLang, subsList),
|
|
||||||
)
|
|
||||||
|
|
||||||
return videoList.distinctBy { it.quality }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getStreams(
|
private fun getStreams(
|
||||||
@ -348,8 +330,8 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
}?.filterNotNull()?.flatten() ?: emptyList()
|
}?.filterNotNull()?.flatten() ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getVideoRequest(mediaId: String, token: AccessToken): Request {
|
private fun getVideoRequest(mediaId: String): Request {
|
||||||
return GET("$crUrl/cms/v2${token.bucket}/videos/$mediaId/streams?Policy=${token.policy}&Signature=${token.signature}&Key-Pair-Id=${token.key_pair_id}")
|
return GET("$crUrl/cms/v2{0}/videos/$mediaId/streams?Policy={1}&Signature={2}&Key-Pair-Id={3}")
|
||||||
}
|
}
|
||||||
|
|
||||||
private val df = DecimalFormat("0.#")
|
private val df = DecimalFormat("0.#")
|
||||||
@ -552,29 +534,23 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
object : LocalSubsPreference(screen.context) {
|
object : LocalSubsPreference(screen.context) {
|
||||||
override fun reload() {
|
override fun reload() {
|
||||||
this.apply {
|
this.apply {
|
||||||
key = PREF_FETCH_LOCAL_SUBS
|
key = PREF_USE_LOCAL_Token
|
||||||
title = "Fetch Local Subs (Don't Spam this please!)"
|
title = "Use Local Token (Don't Spam this please!)"
|
||||||
runBlocking {
|
runBlocking {
|
||||||
withContext(Dispatchers.IO) { summary = getTokenDetail() }
|
withContext(Dispatchers.IO) { summary = getTokenDetail() }
|
||||||
}
|
}
|
||||||
setDefaultValue(false)
|
setDefaultValue(false)
|
||||||
setOnPreferenceChangeListener { _, newValue ->
|
setOnPreferenceChangeListener { _, newValue ->
|
||||||
val new = newValue as Boolean
|
val new = newValue as Boolean
|
||||||
Thread {
|
preferences.edit().putBoolean(key, new).commit().also {
|
||||||
runBlocking {
|
Thread {
|
||||||
if (new) {
|
runBlocking {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
summary = getTokenDetail(true)
|
summary = getTokenDetail(true)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
tokenInterceptor.removeLocalToken()
|
|
||||||
summary = """Token location:
|
|
||||||
|Expires:
|
|
||||||
""".trimMargin()
|
|
||||||
}
|
}
|
||||||
}
|
}.start()
|
||||||
}.start()
|
}
|
||||||
preferences.edit().putBoolean(key, new).commit()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -589,14 +565,14 @@ class Yomiroll : ConfigurableAnimeSource, AnimeHttpSource() {
|
|||||||
|
|
||||||
private fun getTokenDetail(force: Boolean = false): String {
|
private fun getTokenDetail(force: Boolean = false): String {
|
||||||
return try {
|
return try {
|
||||||
val storedToken = tokenInterceptor.getLocalToken(force)
|
val storedToken = tokenInterceptor.getAccessToken(force)
|
||||||
"""Token location: ${
|
"""Token location: ${
|
||||||
storedToken?.bucket?.substringAfter("/")?.substringBefore("/") ?: ""
|
storedToken.bucket?.substringAfter("/")?.substringBefore("/") ?: ""
|
||||||
}
|
}
|
||||||
|Expires: ${storedToken?.policyExpire?.let { DateFormatter.format(it) } ?: ""}
|
|
||||||
""".trimMargin()
|
""".trimMargin()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
""
|
tokenInterceptor.removeToken()
|
||||||
|
"Error: ${e.localizedMessage ?: "Something Went Wrong"}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user