Skip to content

Commit

Permalink
add trackers support (#720)
Browse files Browse the repository at this point in the history
* add trackers support

* Cleanup Tracker Code

* Add GraphQL support for Tracking

* Fix lint and deprecation errors

* remove password from logs

* Fixes after merge

* Disable tracking for now

* More disabled

---------

Co-authored-by: Syer10 <[email protected]>
  • Loading branch information
tachimanga and Syer10 authored Jan 7, 2024
1 parent 230427e commit 5a178ad
Show file tree
Hide file tree
Showing 44 changed files with 3,726 additions and 10 deletions.
45 changes: 45 additions & 0 deletions AndroidCompat/src/main/java/androidx/core/net/Uri.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

@file:Suppress("NOTHING_TO_INLINE") // Aliases to public API.

package androidx.core.net

import android.net.Uri
import java.io.File

/**
* Creates a Uri from the given encoded URI string.
*
* @see Uri.parse
*/
public inline fun String.toUri(): Uri = Uri.parse(this)

/**
* Creates a Uri from the given file.
*
* @see Uri.fromFile
*/
public inline fun File.toUri(): Uri = Uri.fromFile(this)

/**
* Creates a [File] from the given [Uri]. Note that this will throw an
* [IllegalArgumentException] when invoked on a [Uri] that lacks `file` scheme.
*/
public fun Uri.toFile(): File {
require(scheme == "file") { "Uri lacks 'file' scheme: $this" }
return File(requireNotNull(path) { "Uri path is null: $this" })
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import kotlinx.serialization.json.okio.decodeFromBufferedSource
import kotlinx.serialization.serializer
import okhttp3.Call
import okhttp3.Callback
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
Expand All @@ -19,6 +20,8 @@ import java.io.IOException
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.coroutines.resumeWithException

val jsonMime = "application/json; charset=utf-8".toMediaType()

fun Call.asObservable(): Observable<Response> {
return Observable.unsafeCreate { subscriber ->
// Since Call is a one-shot type, clone it for each new subscriber.
Expand Down
14 changes: 14 additions & 0 deletions server/src/main/kotlin/eu/kanade/tachiyomi/util/PkceUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package eu.kanade.tachiyomi.util

import android.util.Base64
import java.security.SecureRandom

object PkceUtil {
private const val PKCE_BASE64_ENCODE_SETTINGS = Base64.NO_WRAP or Base64.NO_PADDING or Base64.URL_SAFE

fun generateCodeVerifier(): String {
val codeVerifier = ByteArray(50)
SecureRandom().nextBytes(codeVerifier)
return Base64.encodeToString(codeVerifier, PKCE_BASE64_ENCODE_SETTINGS)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package eu.kanade.tachiyomi.util.lang

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
*
* **Possible replacements**
* - suspend function
* - custom scope like view or presenter scope
*/
@DelicateCoroutinesApi
fun launchUI(block: suspend CoroutineScope.() -> Unit): Job = GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT, block)

/**
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
*
* **Possible replacements**
* - suspend function
* - custom scope like view or presenter scope
*/
@DelicateCoroutinesApi
fun launchIO(block: suspend CoroutineScope.() -> Unit): Job = GlobalScope.launch(Dispatchers.IO, CoroutineStart.DEFAULT, block)

/**
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
*
* **Possible replacements**
* - suspend function
* - custom scope like view or presenter scope
*/
@DelicateCoroutinesApi
fun launchNow(block: suspend CoroutineScope.() -> Unit): Job = GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED, block)

fun CoroutineScope.launchUI(block: suspend CoroutineScope.() -> Unit): Job = launch(Dispatchers.Main, block = block)

fun CoroutineScope.launchIO(block: suspend CoroutineScope.() -> Unit): Job = launch(Dispatchers.IO, block = block)

fun CoroutineScope.launchNonCancellable(block: suspend CoroutineScope.() -> Unit): Job = launchIO { withContext(NonCancellable, block) }

suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T) =
withContext(
Dispatchers.Main,
block,
)

suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) =
withContext(
Dispatchers.IO,
block,
)

suspend fun <T> withNonCancellableContext(block: suspend CoroutineScope.() -> T) = withContext(NonCancellable, block)
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (C) Contributors to the Suwayomi project
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

package suwayomi.tachidesk.graphql.dataLoaders

import com.expediagroup.graphql.dataloader.KotlinDataLoader
import org.dataloader.DataLoader
import org.dataloader.DataLoaderFactory
import org.jetbrains.exposed.sql.Slf4jSqlDebugLogger
import org.jetbrains.exposed.sql.addLogger
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.graphql.types.TrackRecordNodeList
import suwayomi.tachidesk.graphql.types.TrackRecordNodeList.Companion.toNodeList
import suwayomi.tachidesk.graphql.types.TrackRecordType
import suwayomi.tachidesk.graphql.types.TrackerType
import suwayomi.tachidesk.manga.impl.track.tracker.TrackerManager
import suwayomi.tachidesk.manga.impl.track.tracker.model.toTrack
import suwayomi.tachidesk.manga.model.table.TrackRecordTable
import suwayomi.tachidesk.server.JavalinSetup.future

class TrackerDataLoader : KotlinDataLoader<Int, TrackerType> {
override val dataLoaderName = "TrackerDataLoader"

override fun getDataLoader(): DataLoader<Int, TrackerType> =
DataLoaderFactory.newDataLoader { ids ->
future {
ids.map { id ->
TrackerManager.getTracker(id)?.let { TrackerType(it) }
}
}
}
}

class TrackRecordsForMangaIdDataLoader : KotlinDataLoader<Int, TrackRecordNodeList> {
override val dataLoaderName = "TrackRecordsForMangaIdDataLoader"

override fun getDataLoader(): DataLoader<Int, TrackRecordNodeList> =
DataLoaderFactory.newDataLoader { ids ->
future {
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecordsByMangaId =
TrackRecordTable.select { TrackRecordTable.mangaId inList ids }
.map { TrackRecordType(it) }
.groupBy { it.mangaId }
ids.map { (trackRecordsByMangaId[it] ?: emptyList()).toNodeList() }
}
}
}
}

class DisplayScoreForTrackRecordDataLoader : KotlinDataLoader<Int, String> {
override val dataLoaderName = "DisplayScoreForTrackRecordDataLoader"

override fun getDataLoader(): DataLoader<Int, String> =
DataLoaderFactory.newDataLoader<Int, String> { ids ->
future {
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecords =
TrackRecordTable.select { TrackRecordTable.id inList ids }
.toList()
.map { it.toTrack() }
.associateBy { it.id!! }
.mapValues { TrackerManager.getTracker(it.value.sync_id)?.displayScore(it.value) }

ids.map { trackRecords[it] }
}
}
}
}

class TrackRecordsForTrackerIdDataLoader : KotlinDataLoader<Int, TrackRecordNodeList> {
override val dataLoaderName = "TrackRecordsForTrackerIdDataLoader"

override fun getDataLoader(): DataLoader<Int, TrackRecordNodeList> =
DataLoaderFactory.newDataLoader { ids ->
future {
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecordsBySyncId =
TrackRecordTable.select { TrackRecordTable.syncId inList ids }
.map { TrackRecordType(it) }
.groupBy { it.mangaId }
ids.map { (trackRecordsBySyncId[it] ?: emptyList()).toNodeList() }
}
}
}
}

class TrackRecordDataLoader : KotlinDataLoader<Int, TrackRecordType> {
override val dataLoaderName = "TrackRecordDataLoader"

override fun getDataLoader(): DataLoader<Int, TrackRecordType> =
DataLoaderFactory.newDataLoader { ids ->
future {
transaction {
addLogger(Slf4jSqlDebugLogger)
val trackRecordsId =
TrackRecordTable.select { TrackRecordTable.id inList ids }
.map { TrackRecordType(it) }
.associateBy { it.id }
ids.map { trackRecordsId[it] }
}
}
}
}
Loading

0 comments on commit 5a178ad

Please sign in to comment.