Skip to content

Commit 69c4ab8

Browse files
authored
Merge pull request #7195 from FineFindus/feat/local-sb
feat(local): implement support for SponsorBlock and DeArrow
2 parents 8f04fee + e91404b commit 69c4ab8

File tree

7 files changed

+67
-13
lines changed

7 files changed

+67
-13
lines changed

app/src/main/java/com/github/libretube/api/ExternalApi.kt

+13
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package com.github.libretube.api
22

33
import com.github.libretube.api.obj.DeArrowBody
4+
import com.github.libretube.api.obj.DeArrowContent
45
import com.github.libretube.api.obj.PipedConfig
56
import com.github.libretube.api.obj.PipedInstance
7+
import com.github.libretube.api.obj.SegmentData
68
import com.github.libretube.api.obj.SubmitSegmentResponse
79
import com.github.libretube.api.obj.VoteInfo
810
import com.github.libretube.obj.update.UpdateInfo
911
import retrofit2.http.Body
1012
import retrofit2.http.GET
1113
import retrofit2.http.POST
14+
import retrofit2.http.Path
1215
import retrofit2.http.Query
1316
import retrofit2.http.Url
1417

@@ -43,6 +46,13 @@ interface ExternalApi {
4346
@Query("description") description: String = ""
4447
): List<SubmitSegmentResponse>
4548

49+
@GET("$SB_API_URL/api/skipSegments/{videoId}")
50+
suspend fun getSegments(
51+
@Path("videoId") videoId: String,
52+
@Query("category") category: List<String>,
53+
@Query("actionType") actionType: List<String>? = null
54+
): List<SegmentData>
55+
4656
@POST("$SB_API_URL/api/branding")
4757
suspend fun submitDeArrow(@Body body: DeArrowBody)
4858

@@ -55,4 +65,7 @@ interface ExternalApi {
5565
@Query("userID") userID: String,
5666
@Query("type") score: Int
5767
)
68+
69+
@GET("$SB_API_URL/api/branding/{videoId}")
70+
suspend fun getDeArrowContent(@Path("videoId") videoId: String): Map<String, DeArrowContent>
5871
}

app/src/main/java/com/github/libretube/api/MediaServiceRepository.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ interface MediaServiceRepository {
1717
suspend fun getComments(videoId: String): CommentsPage
1818
suspend fun getSegments(
1919
videoId: String,
20-
category: String,
21-
actionType: String? = null
20+
category: List<String>,
21+
actionType: List<String>? = null
2222
): SegmentData
2323

2424
suspend fun getDeArrowContent(videoIds: String): Map<String, DeArrowContent>

app/src/main/java/com/github/libretube/api/NewPipeMediaServiceRepository.kt

+30-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import com.github.libretube.api.obj.StreamItem.Companion.TYPE_PLAYLIST
2020
import com.github.libretube.api.obj.StreamItem.Companion.TYPE_STREAM
2121
import com.github.libretube.api.obj.Streams
2222
import com.github.libretube.api.obj.Subtitle
23+
import com.github.libretube.extensions.parallelMap
24+
import com.github.libretube.extensions.sha256Sum
2325
import com.github.libretube.extensions.toID
2426
import com.github.libretube.helpers.NewPipeExtractorInstance
2527
import com.github.libretube.helpers.PlayerHelper
@@ -321,13 +323,32 @@ class NewPipeMediaServiceRepository : MediaServiceRepository {
321323
}
322324

323325
override suspend fun getSegments(
324-
videoId: String,
325-
category: String,
326-
actionType: String?
327-
): SegmentData = SegmentData()
326+
videoId: String, category: List<String>, actionType: List<String>?
327+
): SegmentData = RetrofitInstance.externalApi.getSegments(
328+
// use hashed video id for privacy
329+
// https://wiki.sponsor.ajay.app/w/API_Docs#GET_/api/skipSegments/:sha256HashPrefix
330+
videoId.sha256Sum().substring(0, 4), category, actionType
331+
).first { it.videoID == videoId }
328332

329333
override suspend fun getDeArrowContent(videoIds: String): Map<String, DeArrowContent> =
330-
emptyMap()
334+
videoIds.split(',').chunked(25).flatMap {
335+
it.parallelMap { videoId ->
336+
runCatching {
337+
RetrofitInstance.externalApi.getDeArrowContent(
338+
// use hashed video id for privacy
339+
// https://wiki.sponsor.ajay.app/w/API_Docs/DeArrow#GET_/api/branding/:sha256HashPrefix
340+
videoId.sha256Sum().substring(0, 4)
341+
)
342+
}.getOrNull()
343+
}
344+
}.filterNotNull().reduce { acc, map -> acc + map }.mapValues { (videoId, value) ->
345+
value.copy(
346+
thumbnails = value.thumbnails.map { thumbnail ->
347+
thumbnail.takeIf { it.original } ?: thumbnail.copy(
348+
thumbnail = "${DEARROW_THUMBNAIL_URL}?videoID=$videoId&time=${thumbnail.timestamp}"
349+
)
350+
})
351+
}
331352

332353
override suspend fun getSearchResults(searchQuery: String, filter: String): SearchResult {
333354
val queryHandler = NewPipeExtractorInstance.extractor.searchQHFactory.fromQuery(
@@ -481,4 +502,8 @@ class NewPipeMediaServiceRepository : MediaServiceRepository {
481502
comments = commentsInfo.items.map { it.toComment() }
482503
)
483504
}
505+
506+
companion object {
507+
private const val DEARROW_THUMBNAIL_URL = "https://dearrow-thumb.ajay.app/api/v1/getThumbnail"
508+
}
484509
}

app/src/main/java/com/github/libretube/api/PipedMediaServiceRepository.kt

+8-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.github.libretube.api.obj.StreamItem
1313
import com.github.libretube.api.obj.Streams
1414
import com.github.libretube.constants.PreferenceKeys
1515
import com.github.libretube.helpers.PreferenceHelper
16+
import kotlinx.serialization.encodeToString
1617
import retrofit2.HttpException
1718

1819
open class PipedMediaServiceRepository : MediaServiceRepository {
@@ -36,9 +37,13 @@ open class PipedMediaServiceRepository : MediaServiceRepository {
3637

3738
override suspend fun getSegments(
3839
videoId: String,
39-
category: String,
40-
actionType: String?
41-
): SegmentData = api.getSegments(videoId, category, actionType)
40+
category: List<String>,
41+
actionType: List<String>?
42+
): SegmentData = api.getSegments(
43+
videoId,
44+
JsonHelper.json.encodeToString(category),
45+
JsonHelper.json.encodeToString(actionType)
46+
)
4247

4348
override suspend fun getDeArrowContent(videoIds: String): Map<String, DeArrowContent> =
4449
api.getDeArrowContent(videoIds)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.github.libretube.extensions
2+
3+
import java.security.MessageDigest
4+
5+
/**
6+
* Calculates the SHA-256 hash of the String and returns the result in hexadecimal.
7+
*/
8+
@OptIn(ExperimentalStdlibApi::class)
9+
fun String.sha256Sum(): String = MessageDigest.getInstance("SHA-256")
10+
.digest(this.toByteArray())
11+
.toHexString()

app/src/main/java/com/github/libretube/services/OnlinePlayerService.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,8 @@ open class OnlinePlayerService : AbstractPlayerService() {
209209
if (sponsorBlockConfig.isEmpty()) return@runCatching
210210
sponsorBlockSegments = MediaServiceRepository.instance.getSegments(
211211
videoId,
212-
JsonHelper.json.encodeToString(sponsorBlockConfig.keys),
213-
"""["skip","mute","full","poi","chapter"]"""
212+
sponsorBlockConfig.keys.toList(),
213+
listOf("skip","mute","full","poi","chapter")
214214
).segments
215215

216216
withContext(Dispatchers.Main) {

app/src/main/java/com/github/libretube/ui/dialogs/SubmitSegmentDialog.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ class SubmitSegmentDialog : DialogFragment() {
147147
private suspend fun fetchSegments() {
148148
val categories = resources.getStringArray(R.array.sponsorBlockSegments).toList()
149149
segments = try {
150-
MediaServiceRepository.instance.getSegments(videoId, JsonHelper.json.encodeToString(categories)).segments
150+
MediaServiceRepository.instance.getSegments(videoId, categories).segments
151151
} catch (e: Exception) {
152152
Log.e(TAG(), e.toString())
153153
return

0 commit comments

Comments
 (0)