add crunchyroll login and browse (no parsing for now)
This commit is contained in:
parent
eafefd9a51
commit
e7d057bfb8
|
@ -1,6 +1,9 @@
|
||||||
apply plugin: 'com.android.application'
|
plugins {
|
||||||
apply plugin: 'kotlin-android'
|
id 'com.android.application'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
id 'kotlin-android'
|
||||||
|
id 'kotlin-android-extensions'
|
||||||
|
id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlin_version"
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 30
|
||||||
|
@ -43,6 +46,7 @@ dependencies {
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1")
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.6.0'
|
implementation 'androidx.core:core-ktx:1.6.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
|
@ -68,6 +72,10 @@ dependencies {
|
||||||
implementation 'com.afollestad.material-dialogs:core:3.3.0'
|
implementation 'com.afollestad.material-dialogs:core:3.3.0'
|
||||||
implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
|
implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
|
||||||
|
|
||||||
|
implementation 'com.github.kittinunf.fuel:fuel:2.3.1'
|
||||||
|
implementation 'com.github.kittinunf.fuel:fuel-android:2.3.1'
|
||||||
|
implementation 'com.github.kittinunf.fuel:fuel-json:2.3.1'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
package org.mosad.teapod.parser.crunchyroll
|
||||||
|
|
||||||
|
import com.github.kittinunf.fuel.Fuel
|
||||||
|
import com.github.kittinunf.fuel.core.FuelError
|
||||||
|
import com.github.kittinunf.fuel.json.FuelJson
|
||||||
|
import com.github.kittinunf.fuel.json.responseJson
|
||||||
|
import com.github.kittinunf.result.Result
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import kotlinx.serialization.decodeFromString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
private val json = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
|
class Cruncyroll {
|
||||||
|
|
||||||
|
private val baseUrl = "https://beta-api.crunchyroll.com"
|
||||||
|
|
||||||
|
private var accessToken = ""
|
||||||
|
private var tokenType = ""
|
||||||
|
|
||||||
|
fun login(username: String, password: String): Boolean = runBlocking {
|
||||||
|
val tokenEndpoint = "/auth/v1/token"
|
||||||
|
|
||||||
|
val formData = listOf(
|
||||||
|
"username" to username,
|
||||||
|
"password" to password,
|
||||||
|
"grant_type" to "password",
|
||||||
|
"scope" to "offline_access"
|
||||||
|
)
|
||||||
|
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val (request, response, result) = Fuel.post("$baseUrl$tokenEndpoint", parameters = formData)
|
||||||
|
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
.appendHeader(
|
||||||
|
"Authorization",
|
||||||
|
"Basic "
|
||||||
|
)
|
||||||
|
.responseJson()
|
||||||
|
|
||||||
|
result.component1()?.obj()?.let {
|
||||||
|
accessToken = it.get("access_token").toString()
|
||||||
|
tokenType = it.get("token_type").toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// println("request: $request")
|
||||||
|
// println("response: $response")
|
||||||
|
// println("response: $result")
|
||||||
|
|
||||||
|
println("login complete with code ${response.statusCode}")
|
||||||
|
|
||||||
|
return@withContext response.statusCode == 200
|
||||||
|
}
|
||||||
|
|
||||||
|
return@runBlocking false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO get/post difference
|
||||||
|
private suspend fun requestA(endpoint: String): Result<FuelJson, FuelError> = coroutineScope {
|
||||||
|
return@coroutineScope (Dispatchers.IO) {
|
||||||
|
val (request, response, result) = Fuel.get("$baseUrl$endpoint")
|
||||||
|
.header("Authorization", "$tokenType $accessToken")
|
||||||
|
.responseJson()
|
||||||
|
|
||||||
|
// println("request request: $request")
|
||||||
|
// println("request response: $response")
|
||||||
|
// println("request result: $result")
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TESTING
|
||||||
|
@Serializable
|
||||||
|
data class Test(val total: Int, val items: List<Item>)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class Item(val channel_id: String, val description: String)
|
||||||
|
|
||||||
|
// TODO sort_by, default alphabetical, n, locale de-DE
|
||||||
|
suspend fun browse() {
|
||||||
|
val browseEndpoint = "/content/v1/browse"
|
||||||
|
|
||||||
|
val result = requestA(browseEndpoint)
|
||||||
|
|
||||||
|
println("${result.component1()?.obj()?.get("total")}")
|
||||||
|
|
||||||
|
val test = json.decodeFromString<Test>(result.component1()?.obj()?.toString()!!)
|
||||||
|
println(test)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun search() {
|
||||||
|
val searchEndpoint = "/content/v1/search"
|
||||||
|
|
||||||
|
val result = requestA(searchEndpoint)
|
||||||
|
|
||||||
|
println("${result.component1()?.obj()?.get("total")}")
|
||||||
|
|
||||||
|
val test = json.decodeFromString<Test>(result.component1()?.obj()?.toString()!!)
|
||||||
|
println(test)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ import kotlinx.coroutines.*
|
||||||
import org.mosad.teapod.R
|
import org.mosad.teapod.R
|
||||||
import org.mosad.teapod.databinding.ActivityMainBinding
|
import org.mosad.teapod.databinding.ActivityMainBinding
|
||||||
import org.mosad.teapod.parser.AoDParser
|
import org.mosad.teapod.parser.AoDParser
|
||||||
|
import org.mosad.teapod.parser.crunchyroll.Cruncyroll
|
||||||
import org.mosad.teapod.preferences.EncryptedPreferences
|
import org.mosad.teapod.preferences.EncryptedPreferences
|
||||||
import org.mosad.teapod.preferences.Preferences
|
import org.mosad.teapod.preferences.Preferences
|
||||||
import org.mosad.teapod.ui.activity.main.fragments.AccountFragment
|
import org.mosad.teapod.ui.activity.main.fragments.AccountFragment
|
||||||
|
@ -150,26 +151,38 @@ class MainActivity : AppCompatActivity(), NavigationBarView.OnItemSelectedListen
|
||||||
EncryptedPreferences.readCredentials(this)
|
EncryptedPreferences.readCredentials(this)
|
||||||
StorageController.load(this)
|
StorageController.load(this)
|
||||||
|
|
||||||
// show onboarding
|
// show onboarding TODO rework
|
||||||
if (EncryptedPreferences.password.isEmpty()) {
|
if (EncryptedPreferences.password.isEmpty()) {
|
||||||
showOnboarding()
|
showOnboarding()
|
||||||
} else {
|
} else {
|
||||||
try {
|
val crunchy = Cruncyroll()
|
||||||
if (!AoDParser.login()) {
|
crunchy.login(EncryptedPreferences.login, EncryptedPreferences.password)
|
||||||
showLoginDialog()
|
println("after login")
|
||||||
}
|
|
||||||
} catch (ex: SocketTimeoutException) {
|
|
||||||
Log.w(javaClass.name, "Timeout during login!")
|
|
||||||
|
|
||||||
// show waring dialog before finishing
|
runBlocking { crunchy.browse() }
|
||||||
MaterialDialog(this).show {
|
|
||||||
title(R.string.dialog_timeout_head)
|
|
||||||
message(R.string.dialog_timeout_desc)
|
|
||||||
onDismiss { exitAndRemoveTask() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// if (EncryptedPreferences.password.isEmpty()) {
|
||||||
|
// showOnboarding()
|
||||||
|
// } else {
|
||||||
|
// try {
|
||||||
|
// if (!AoDParser.login()) {
|
||||||
|
// showLoginDialog()
|
||||||
|
// }
|
||||||
|
// } catch (ex: SocketTimeoutException) {
|
||||||
|
// Log.w(javaClass.name, "Timeout during login!")
|
||||||
|
//
|
||||||
|
// // show waring dialog before finishing
|
||||||
|
// MaterialDialog(this).show {
|
||||||
|
// title(R.string.dialog_timeout_head)
|
||||||
|
// message(R.string.dialog_timeout_desc)
|
||||||
|
// onDismiss { exitAndRemoveTask() }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
runBlocking { loadingJob.await() } // wait for initial loading to finish
|
runBlocking { loadingJob.await() } // wait for initial loading to finish
|
||||||
}
|
}
|
||||||
Log.i(javaClass.name, "loading and login in $time ms")
|
Log.i(javaClass.name, "loading and login in $time ms")
|
||||||
|
|
Loading…
Reference in New Issue