diff --git a/app/src/main/java/com/github/libretube/MainActivity.kt b/app/src/main/java/com/github/libretube/MainActivity.kt index 67a322d42..3192af72b 100644 --- a/app/src/main/java/com/github/libretube/MainActivity.kt +++ b/app/src/main/java/com/github/libretube/MainActivity.kt @@ -45,6 +45,13 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) RetrofitInstance.url = sharedPreferences.getString("instance", "https://pipedapi.kavin.rocks/")!! + SponsorBlockSettings.sponsorBlockEnabled = sharedPreferences.getBoolean("sponsorblock_enabled_key", false) + SponsorBlockSettings.introEnabled = sharedPreferences.getBoolean("intro_category_key", false) + SponsorBlockSettings.selfPromoEnabled = sharedPreferences.getBoolean("selfpromo_category_key", false) + SponsorBlockSettings.interactionEnabled = sharedPreferences.getBoolean("interaction_category_key", false) + SponsorBlockSettings.sponsorsEnabled = sharedPreferences.getBoolean("sponsors_category_key", false) + SponsorBlockSettings.outroEnabled = sharedPreferences.getBoolean("outro_category_key", false) + DynamicColors.applyToActivitiesIfAvailable(application) val languageName = sharedPreferences.getString("language", "sys") if (languageName != "") { diff --git a/app/src/main/java/com/github/libretube/PipedApi.kt b/app/src/main/java/com/github/libretube/PipedApi.kt index 94c2a65b5..3332c8ca4 100644 --- a/app/src/main/java/com/github/libretube/PipedApi.kt +++ b/app/src/main/java/com/github/libretube/PipedApi.kt @@ -13,6 +13,9 @@ interface PipedApi { @GET("comments/{videoId}") suspend fun getComments(@Path("videoId") videoId: String): CommentsPage + @GET("sponsors/{videoId}") + suspend fun getSegments(@Path("videoId") videoId: String, @Query("category") category: String): Segments + @GET("nextpage/comments/{videoId}") suspend fun getCommentsNextPage(@Path("videoId") videoId: String, @Query("nextpage") nextPage: String): CommentsPage diff --git a/app/src/main/java/com/github/libretube/PlayerFragment.kt b/app/src/main/java/com/github/libretube/PlayerFragment.kt index ce00bfca7..b0b24bae0 100644 --- a/app/src/main/java/com/github/libretube/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/PlayerFragment.kt @@ -14,6 +14,7 @@ import android.os.Build.VERSION.SDK_INT import android.os.Bundle import android.os.Environment import android.text.Html +import android.text.TextUtils import android.util.Log import android.view.LayoutInflater import android.view.View @@ -35,6 +36,8 @@ import androidx.recyclerview.widget.RecyclerView import com.github.libretube.adapters.CommentsAdapter import com.github.libretube.adapters.TrendingAdapter import com.github.libretube.obj.PipedStream +import com.github.libretube.obj.Segment +import com.github.libretube.obj.Segments import com.github.libretube.obj.Subscribe import com.google.android.exoplayer2.C import com.google.android.exoplayer2.ExoPlayer @@ -87,6 +90,7 @@ class PlayerFragment : Fragment() { private lateinit var motionLayout: MotionLayout private lateinit var exoPlayer: ExoPlayer private lateinit var mediaSource: MediaSource + private lateinit var segmentData: Segments private lateinit var relDownloadVideo: LinearLayout @@ -270,6 +274,26 @@ class PlayerFragment : Fragment() { } } + private fun checkForSegments() + { + if (!exoPlayer.isPlaying || !SponsorBlockSettings.sponsorBlockEnabled) return + + exoPlayerView.postDelayed(this::checkForSegments, 100) + + if(segmentData.segments.isEmpty() ) + return + + segmentData.segments.forEach { segment: Segment -> + val segmentStart = (segment.segment!![0] * 1000.0f).toLong() + val segmentEnd = (segment.segment!![1] * 1000.0f).toLong() + val currentPosition = exoPlayer.currentPosition + if(currentPosition in segmentStart until segmentEnd) { + Toast.makeText(context,R.string.segment_skipped, Toast.LENGTH_SHORT).show() + exoPlayer.seekTo(segmentEnd); + } + } + } + private fun fetchJson(view: View) { fun run() { lifecycleScope.launchWhenCreated { @@ -297,6 +321,45 @@ class PlayerFragment : Fragment() { Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT).show() return@launchWhenCreated } + if(SponsorBlockSettings.sponsorBlockEnabled) { + val categories: ArrayList = arrayListOf() + if (SponsorBlockSettings.introEnabled) { + categories.add("intro") + } + if (SponsorBlockSettings.selfPromoEnabled) { + categories.add("selfpromo") + } + if (SponsorBlockSettings.interactionEnabled) { + categories.add("interaction") + } + if (SponsorBlockSettings.sponsorsEnabled) { + categories.add("sponsor") + } + if (SponsorBlockSettings.outroEnabled) { + categories.add("outro") + } + if(categories.size > 0) { + segmentData = try { + + RetrofitInstance.api.getSegments( + videoId!!, + "[\"" + TextUtils.join("\",\"", categories) + "\"]" + ) + } catch (e: IOException) { + println(e) + Log.e(TAG, "IOException, you might not have internet connection") + Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT) + .show() + return@launchWhenCreated + } catch (e: HttpException) { + Log.e(TAG, "HttpException, unexpected response") + Toast.makeText(context, R.string.server_error, Toast.LENGTH_SHORT) + .show() + return@launchWhenCreated + } + } + } + isLoading = false var videosNameArray: Array = arrayOf() videosNameArray += "HLS" @@ -502,6 +565,13 @@ class PlayerFragment : Fragment() { } // Listener for play and pause icon change exoPlayer!!.addListener(object : com.google.android.exoplayer2.Player.Listener { + override fun onIsPlayingChanged(isPlaying: Boolean) { + if(isPlaying && SponsorBlockSettings.sponsorBlockEnabled) + { + exoPlayerView.postDelayed(this@PlayerFragment::checkForSegments, 100) + } + } + override fun onPlayerStateChanged( playWhenReady: Boolean, playbackState: Int diff --git a/app/src/main/java/com/github/libretube/SettingsActivity.kt b/app/src/main/java/com/github/libretube/SettingsActivity.kt index 2ee7f5645..58384bc17 100644 --- a/app/src/main/java/com/github/libretube/SettingsActivity.kt +++ b/app/src/main/java/com/github/libretube/SettingsActivity.kt @@ -159,6 +159,15 @@ class SettingsActivity : AppCompatActivity(), true } + val sponsorblock = findPreference("sponsorblock") + sponsorblock?.setOnPreferenceClickListener { + val newFragment = SponsorBlockSettings() + parentFragmentManager.beginTransaction() + .replace(R.id.settings, newFragment) + .commitNow() + true + } + val importFromYt = findPreference("import_from_yt") importFromYt?.setOnPreferenceClickListener { val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE) diff --git a/app/src/main/java/com/github/libretube/SponsorBlockSettings.kt b/app/src/main/java/com/github/libretube/SponsorBlockSettings.kt new file mode 100644 index 000000000..813a3f472 --- /dev/null +++ b/app/src/main/java/com/github/libretube/SponsorBlockSettings.kt @@ -0,0 +1,55 @@ +package com.github.libretube + +import android.os.Bundle +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.SwitchPreferenceCompat + +class SponsorBlockSettings : PreferenceFragmentCompat() { + private val TAG = "SponsorBlockDialog" + companion object { + var sponsorBlockEnabled: Boolean = false + var sponsorsEnabled: Boolean = false + var selfPromoEnabled: Boolean = false + var interactionEnabled: Boolean = false + var introEnabled: Boolean = false + var outroEnabled: Boolean = false + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.sponsorblock_settings, rootKey) + val sponsorBlockToggle = findPreference("sponsorblock_enabled_key") + sponsorBlockToggle?.setOnPreferenceChangeListener { _, newValue -> + sponsorBlockEnabled = newValue as Boolean + true + } + + val sponsorToggle = findPreference("sponsors_category_key") + sponsorToggle?.setOnPreferenceChangeListener { _, newValue -> + sponsorsEnabled = newValue as Boolean + true + } + val selfPromoToggle = findPreference("selfpromo_category_key") + selfPromoToggle?.setOnPreferenceChangeListener { _, newValue -> + selfPromoEnabled = newValue as Boolean + true + } + + val interactionToggle = findPreference("interaction_category_key") + interactionToggle?.setOnPreferenceChangeListener { _, newValue -> + interactionEnabled = newValue as Boolean + true + } + + val introToggle = findPreference("intro_category_key") + introToggle?.setOnPreferenceChangeListener { _, newValue -> + introEnabled = newValue as Boolean + true + } + + val outroToggle = findPreference("outro_category_key") + outroToggle?.setOnPreferenceChangeListener { _, newValue -> + outroEnabled = newValue as Boolean + true + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/libretube/obj/Segment.kt b/app/src/main/java/com/github/libretube/obj/Segment.kt new file mode 100644 index 000000000..4608971b7 --- /dev/null +++ b/app/src/main/java/com/github/libretube/obj/Segment.kt @@ -0,0 +1,12 @@ +package com.github.libretube.obj + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties + +@JsonIgnoreProperties(ignoreUnknown = true) +data class Segment( + val actionType: String?, + val category: String?, + val segment: List? +){ + constructor(): this("", "", arrayListOf()) +} diff --git a/app/src/main/java/com/github/libretube/obj/Segments.kt b/app/src/main/java/com/github/libretube/obj/Segments.kt new file mode 100644 index 000000000..a6ed285bc --- /dev/null +++ b/app/src/main/java/com/github/libretube/obj/Segments.kt @@ -0,0 +1,10 @@ +package com.github.libretube.obj + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties + +@JsonIgnoreProperties(ignoreUnknown = true) +data class Segments( + val segments: MutableList = arrayListOf() +){ + constructor(): this(arrayListOf()) +} diff --git a/app/src/main/res/drawable/ic_sponsorblock.xml b/app/src/main/res/drawable/ic_sponsorblock.xml new file mode 100644 index 000000000..ffdc099ab --- /dev/null +++ b/app/src/main/res/drawable/ic_sponsorblock.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 82c632a45..bc68be368 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -73,4 +73,20 @@ Retry Comments Default Tab + SponsorBlock + Uses API from https://sponsor.ajay.app/ + Skipped segment. + SponsorBlock + Enabled + Segments + Sponsor + Paid promotion, paid referrals and direct advertisements. Not for self-promotion or free shoutouts to causes/creators/websites/products they like. + Unpaid/Self Promotion + Similar to \"sponsor\" except for unpaid or self promotion. This includes sections about merchandise, donations, or information about who they collaborated with. + Interaction Reminder (Subscribe) + When there is a short reminder to like, subscribe or follow them in the middle of content. If it is long or about something specific, it should be under self promotion instead. + Intermission/Intro Animation + An interval without actual content. Could be a pause, static frame, repeating animation. This should not be used for transitions containing information. + Endcards/Credits + Credits or when the YouTube endcards appear. Not for conclusions with information. diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml index dccf89391..18349aaa1 100644 --- a/app/src/main/res/xml/settings.xml +++ b/app/src/main/res/xml/settings.xml @@ -71,14 +71,21 @@ android:icon="@drawable/ic_theme" /> - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file