feat(MediaService/Local): implement SponsorBlock support

Allows users to use SponsorBlock when using full local mode.

Closes: https://github.com/libre-tube/LibreTube/issues/7193
This commit is contained in:
FineFindus 2025-03-13 15:55:54 +01:00
parent 2497fa8702
commit 31613c8f84
No known key found for this signature in database
GPG Key ID: 64873EE210FF8E6B
2 changed files with 26 additions and 3 deletions

View File

@ -3,12 +3,14 @@ package com.github.libretube.api
import com.github.libretube.api.obj.DeArrowBody import com.github.libretube.api.obj.DeArrowBody
import com.github.libretube.api.obj.PipedConfig import com.github.libretube.api.obj.PipedConfig
import com.github.libretube.api.obj.PipedInstance import com.github.libretube.api.obj.PipedInstance
import com.github.libretube.api.obj.SegmentData
import com.github.libretube.api.obj.SubmitSegmentResponse import com.github.libretube.api.obj.SubmitSegmentResponse
import com.github.libretube.api.obj.VoteInfo import com.github.libretube.api.obj.VoteInfo
import com.github.libretube.obj.update.UpdateInfo import com.github.libretube.obj.update.UpdateInfo
import retrofit2.http.Body import retrofit2.http.Body
import retrofit2.http.GET import retrofit2.http.GET
import retrofit2.http.POST import retrofit2.http.POST
import retrofit2.http.Path
import retrofit2.http.Query import retrofit2.http.Query
import retrofit2.http.Url import retrofit2.http.Url
@ -43,6 +45,13 @@ interface ExternalApi {
@Query("description") description: String = "" @Query("description") description: String = ""
): List<SubmitSegmentResponse> ): List<SubmitSegmentResponse>
@GET("$SB_API_URL/api/skipSegments/{videoId}")
suspend fun getSegments(
@Path("videoId") videoId: String,
@Query("category") category: List<String>,
@Query("actionType") actionType: List<String>? = null
): List<SegmentData>
@POST("$SB_API_URL/api/branding") @POST("$SB_API_URL/api/branding")
suspend fun submitDeArrow(@Body body: DeArrowBody) suspend fun submitDeArrow(@Body body: DeArrowBody)

View File

@ -48,6 +48,7 @@ import org.schabi.newpipe.extractor.stream.AudioStream
import org.schabi.newpipe.extractor.stream.StreamInfo import org.schabi.newpipe.extractor.stream.StreamInfo
import org.schabi.newpipe.extractor.stream.StreamInfoItem import org.schabi.newpipe.extractor.stream.StreamInfoItem
import org.schabi.newpipe.extractor.stream.VideoStream import org.schabi.newpipe.extractor.stream.VideoStream
import java.security.MessageDigest
private fun VideoStream.toPipedStream() = PipedStream( private fun VideoStream.toPipedStream() = PipedStream(
@ -319,11 +320,24 @@ class NewPipeMediaServiceRepository : MediaServiceRepository {
) )
} }
@OptIn(ExperimentalStdlibApi::class)
override suspend fun getSegments( override suspend fun getSegments(
videoId: String, videoId: String,
category: String, category: List<String>,
actionType: String? actionType: List<String>?
): SegmentData = SegmentData() ): SegmentData {
// use hashed video id for privacy
// https://wiki.sponsor.ajay.app/w/API_Docs#GET_/api/skipSegments/:sha256HashPrefix
val hashedId = MessageDigest.getInstance("SHA-256")
.digest(videoId.toByteArray())
.toHexString()
return RetrofitInstance.externalApi.getSegments(
hashedId.substring(0..4),
category,
actionType
).first { it.videoID == videoId }
}
override suspend fun getDeArrowContent(videoIds: String): Map<String, DeArrowContent> = override suspend fun getDeArrowContent(videoIds: String): Map<String, DeArrowContent> =
emptyMap() emptyMap()