mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-29 00:10:32 +05:30
Merge pull request #4590 from Bnyro/submit-sb-segments
feat: support for submitting SponsorBlock segments
This commit is contained in:
commit
fc1260ce4d
@ -1,9 +1,13 @@
|
|||||||
package com.github.libretube.api
|
package com.github.libretube.api
|
||||||
|
|
||||||
import com.github.libretube.api.obj.Instances
|
import com.github.libretube.api.obj.Instances
|
||||||
|
import com.github.libretube.api.obj.SubmitSegmentResponse
|
||||||
import com.github.libretube.constants.GITHUB_API_URL
|
import com.github.libretube.constants.GITHUB_API_URL
|
||||||
|
import com.github.libretube.constants.SB_SUBMIT_API_URL
|
||||||
import com.github.libretube.obj.update.UpdateInfo
|
import com.github.libretube.obj.update.UpdateInfo
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.Query
|
||||||
import retrofit2.http.Url
|
import retrofit2.http.Url
|
||||||
|
|
||||||
interface ExternalApi {
|
interface ExternalApi {
|
||||||
@ -14,4 +18,16 @@ interface ExternalApi {
|
|||||||
// fetch latest version info
|
// fetch latest version info
|
||||||
@GET(GITHUB_API_URL)
|
@GET(GITHUB_API_URL)
|
||||||
suspend fun getUpdateInfo(): UpdateInfo
|
suspend fun getUpdateInfo(): UpdateInfo
|
||||||
|
|
||||||
|
@POST(SB_SUBMIT_API_URL)
|
||||||
|
suspend fun submitSegment(
|
||||||
|
@Query("videoID") videoId: String,
|
||||||
|
@Query("startTime") startTime: Float,
|
||||||
|
@Query("endTime") endTime: Float,
|
||||||
|
@Query("category") category: String,
|
||||||
|
@Query("userAgent") userAgent: String,
|
||||||
|
@Query("userID") userID: String,
|
||||||
|
@Query("duration") duration: Float? = null,
|
||||||
|
@Query("description") description: String = ""
|
||||||
|
): List<SubmitSegmentResponse>
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
package com.github.libretube.api.obj
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class DeArrowTitle(
|
data class DeArrowTitle(
|
||||||
val UUID: String,
|
@SerialName("UUID") val uuid: String,
|
||||||
val locked: Boolean,
|
val locked: Boolean,
|
||||||
val original: Boolean,
|
val original: Boolean,
|
||||||
val title: String,
|
val title: String,
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.github.libretube.api.obj
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SubmitSegmentResponse(
|
||||||
|
@SerialName("UUID") val uuid: String,
|
||||||
|
val category: String,
|
||||||
|
val segment: List<Float>
|
||||||
|
)
|
@ -4,6 +4,7 @@ package com.github.libretube.constants
|
|||||||
* API link for the update checker
|
* API link for the update checker
|
||||||
*/
|
*/
|
||||||
const val GITHUB_API_URL = "https://api.github.com/repos/libre-tube/LibreTube/releases/latest"
|
const val GITHUB_API_URL = "https://api.github.com/repos/libre-tube/LibreTube/releases/latest"
|
||||||
|
const val SB_SUBMIT_API_URL = "https://sponsor.ajay.app/api/skipSegments"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Links for the about fragment
|
* Links for the about fragment
|
||||||
|
@ -154,4 +154,9 @@ object PreferenceKeys {
|
|||||||
* Error logs
|
* Error logs
|
||||||
*/
|
*/
|
||||||
const val ERROR_LOG = "error_log"
|
const val ERROR_LOG = "error_log"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SponsorBlock UUID
|
||||||
|
*/
|
||||||
|
const val SB_USER_ID = "sb_user_id"
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import androidx.media3.exoplayer.ExoPlayer
|
|||||||
import androidx.media3.exoplayer.LoadControl
|
import androidx.media3.exoplayer.LoadControl
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
|
||||||
import androidx.media3.ui.CaptionStyleCompat
|
import androidx.media3.ui.CaptionStyleCompat
|
||||||
|
import com.github.libretube.LibreTubeApp
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.api.obj.ChapterSegment
|
import com.github.libretube.api.obj.ChapterSegment
|
||||||
import com.github.libretube.api.obj.Segment
|
import com.github.libretube.api.obj.Segment
|
||||||
@ -41,17 +42,6 @@ import kotlinx.coroutines.runBlocking
|
|||||||
object PlayerHelper {
|
object PlayerHelper {
|
||||||
private const val ACTION_MEDIA_CONTROL = "media_control"
|
private const val ACTION_MEDIA_CONTROL = "media_control"
|
||||||
const val CONTROL_TYPE = "control_type"
|
const val CONTROL_TYPE = "control_type"
|
||||||
private val SPONSOR_CATEGORIES =
|
|
||||||
arrayOf(
|
|
||||||
"intro",
|
|
||||||
"selfpromo",
|
|
||||||
"interaction",
|
|
||||||
"sponsor",
|
|
||||||
"outro",
|
|
||||||
"filler",
|
|
||||||
"music_offtopic",
|
|
||||||
"preview"
|
|
||||||
)
|
|
||||||
const val SPONSOR_HIGHLIGHT_CATEGORY = "poi_highlight"
|
const val SPONSOR_HIGHLIGHT_CATEGORY = "poi_highlight"
|
||||||
const val ROLE_FLAG_AUTO_GEN_SUBTITLE = C.ROLE_FLAG_SUPPLEMENTARY
|
const val ROLE_FLAG_AUTO_GEN_SUBTITLE = C.ROLE_FLAG_SUPPLEMENTARY
|
||||||
|
|
||||||
@ -463,7 +453,9 @@ object PlayerHelper {
|
|||||||
fun getSponsorBlockCategories(): MutableMap<String, SbSkipOptions> {
|
fun getSponsorBlockCategories(): MutableMap<String, SbSkipOptions> {
|
||||||
val categories: MutableMap<String, SbSkipOptions> = mutableMapOf()
|
val categories: MutableMap<String, SbSkipOptions> = mutableMapOf()
|
||||||
|
|
||||||
for (category in SPONSOR_CATEGORIES) {
|
for (category in LibreTubeApp.instance.resources.getStringArray(
|
||||||
|
R.array.sponsorBlockSegments
|
||||||
|
)) {
|
||||||
val state = PreferenceHelper.getString(category + "_category", "off").uppercase()
|
val state = PreferenceHelper.getString(category + "_category", "off").uppercase()
|
||||||
if (SbSkipOptions.valueOf(state) != SbSkipOptions.OFF) {
|
if (SbSkipOptions.valueOf(state) != SbSkipOptions.OFF) {
|
||||||
categories[category] = SbSkipOptions.valueOf(state)
|
categories[category] = SbSkipOptions.valueOf(state)
|
||||||
@ -496,7 +488,7 @@ object PlayerHelper {
|
|||||||
if (sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC ||
|
if (sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC ||
|
||||||
(
|
(
|
||||||
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
|
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
|
||||||
segment.skipped == false
|
!segment.skipped
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (sponsorBlockNotifications) {
|
if (sponsorBlockNotifications) {
|
||||||
@ -510,7 +502,7 @@ object PlayerHelper {
|
|||||||
} else if (sponsorBlockConfig[segment.category] == SbSkipOptions.MANUAL ||
|
} else if (sponsorBlockConfig[segment.category] == SbSkipOptions.MANUAL ||
|
||||||
(
|
(
|
||||||
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
|
sponsorBlockConfig[segment.category] == SbSkipOptions.AUTOMATIC_ONCE &&
|
||||||
segment.skipped == true
|
segment.skipped
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return segment
|
return segment
|
||||||
|
@ -6,6 +6,7 @@ import androidx.core.content.edit
|
|||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
object PreferenceHelper {
|
object PreferenceHelper {
|
||||||
/**
|
/**
|
||||||
@ -18,6 +19,11 @@ object PreferenceHelper {
|
|||||||
*/
|
*/
|
||||||
private lateinit var authSettings: SharedPreferences
|
private lateinit var authSettings: SharedPreferences
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible chars to use for the SB User ID
|
||||||
|
*/
|
||||||
|
private const val USER_ID_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the context that is being used to access the shared preferences
|
* set the context that is being used to access the shared preferences
|
||||||
*/
|
*/
|
||||||
@ -129,6 +135,16 @@ object PreferenceHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getSponsorBlockUserID(): String {
|
||||||
|
var uuid = getString(PreferenceKeys.SB_USER_ID, "")
|
||||||
|
if (uuid.isEmpty()) {
|
||||||
|
// generate a new user id to use for submitting SponsorBlock segments
|
||||||
|
uuid = (0 until 30).map { USER_ID_CHARS.random() }.joinToString("")
|
||||||
|
putString(PreferenceKeys.SB_USER_ID, uuid)
|
||||||
|
}
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
private fun getDefaultSharedPreferences(context: Context): SharedPreferences {
|
private fun getDefaultSharedPreferences(context: Context): SharedPreferences {
|
||||||
return PreferenceManager.getDefaultSharedPreferences(context)
|
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package com.github.libretube.ui.dialogs
|
package com.github.libretube.ui.dialogs
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
|
import android.content.DialogInterface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.InputFilter
|
import android.text.InputFilter
|
||||||
import android.text.format.Formatter
|
import android.text.format.Formatter
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@ -65,7 +65,7 @@ class DownloadDialog(
|
|||||||
.setPositiveButton(R.string.download, null)
|
.setPositiveButton(R.string.download, null)
|
||||||
.show()
|
.show()
|
||||||
.apply {
|
.apply {
|
||||||
getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
|
||||||
onDownloadConfirm.invoke()
|
onDownloadConfirm.invoke()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,97 @@
|
|||||||
|
package com.github.libretube.ui.dialogs
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import com.github.libretube.BuildConfig
|
||||||
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.api.RetrofitInstance
|
||||||
|
import com.github.libretube.databinding.DialogSubmitSegmentBinding
|
||||||
|
import com.github.libretube.extensions.TAG
|
||||||
|
import com.github.libretube.extensions.toastFromMainDispatcher
|
||||||
|
import com.github.libretube.helpers.PreferenceHelper
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import java.lang.Exception
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class SubmitSegmentDialog(
|
||||||
|
private val videoId: String,
|
||||||
|
private val currentPosition: Long,
|
||||||
|
private val duration: Long?
|
||||||
|
) : DialogFragment() {
|
||||||
|
private var _binding: DialogSubmitSegmentBinding? = null
|
||||||
|
private val binding = _binding!!
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
_binding = DialogSubmitSegmentBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
|
binding.startTime.setText((currentPosition.toFloat() / 1000).toString())
|
||||||
|
|
||||||
|
val categoryNames = resources.getStringArray(R.array.sponsorBlockSegmentNames)
|
||||||
|
binding.segmentCategory.adapter = ArrayAdapter(
|
||||||
|
requireContext(),
|
||||||
|
R.layout.dropdown_item,
|
||||||
|
categoryNames
|
||||||
|
)
|
||||||
|
|
||||||
|
return MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(getString(R.string.sb_create_segment))
|
||||||
|
.setView(binding.root)
|
||||||
|
.setPositiveButton(R.string.okay, null)
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
.apply {
|
||||||
|
getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
|
||||||
|
requireDialog().hide()
|
||||||
|
|
||||||
|
lifecycleScope.launch {
|
||||||
|
submitSegment()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun submitSegment() {
|
||||||
|
val context = requireContext().applicationContext
|
||||||
|
|
||||||
|
val startTime = binding.startTime.text.toString().toFloatOrNull()
|
||||||
|
var endTime = binding.endTime.text.toString().toFloatOrNull()
|
||||||
|
if (endTime == null || startTime == null || startTime > endTime) {
|
||||||
|
context.toastFromMainDispatcher(R.string.sb_invalid_segment)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duration != null) {
|
||||||
|
// the end time can't be greater than the video duration
|
||||||
|
endTime = minOf(endTime, duration.toFloat())
|
||||||
|
}
|
||||||
|
|
||||||
|
val categories = resources.getStringArray(R.array.sponsorBlockSegments)
|
||||||
|
val category = categories[binding.segmentCategory.selectedItemPosition]
|
||||||
|
val userAgent = "${context.packageName}/${BuildConfig.VERSION_NAME}"
|
||||||
|
val uuid = PreferenceHelper.getSponsorBlockUserID()
|
||||||
|
val duration = duration?.let { it.toFloat() / 1000 }
|
||||||
|
|
||||||
|
try {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
RetrofitInstance.externalApi
|
||||||
|
.submitSegment(videoId, startTime, endTime, category, userAgent, uuid, duration)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG(), e.toString())
|
||||||
|
context.toastFromMainDispatcher(e.localizedMessage.orEmpty())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroyView() {
|
||||||
|
super.onDestroyView()
|
||||||
|
_binding = null
|
||||||
|
}
|
||||||
|
}
|
@ -5,16 +5,22 @@ import android.content.res.Configuration
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.media3.common.C
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelector
|
import androidx.media3.exoplayer.trackselection.TrackSelector
|
||||||
import androidx.media3.ui.PlayerView.ControllerVisibilityListener
|
import androidx.media3.ui.PlayerView.ControllerVisibilityListener
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.extensions.toID
|
||||||
import com.github.libretube.helpers.PlayerHelper
|
import com.github.libretube.helpers.PlayerHelper
|
||||||
import com.github.libretube.helpers.WindowHelper
|
import com.github.libretube.helpers.WindowHelper
|
||||||
import com.github.libretube.obj.BottomSheetItem
|
import com.github.libretube.obj.BottomSheetItem
|
||||||
|
import com.github.libretube.ui.base.BaseActivity
|
||||||
|
import com.github.libretube.ui.dialogs.SubmitSegmentDialog
|
||||||
import com.github.libretube.ui.extensions.toggleSystemBars
|
import com.github.libretube.ui.extensions.toggleSystemBars
|
||||||
import com.github.libretube.ui.interfaces.OnlinePlayerOptions
|
import com.github.libretube.ui.interfaces.OnlinePlayerOptions
|
||||||
import com.github.libretube.ui.models.PlayerViewModel
|
import com.github.libretube.ui.models.PlayerViewModel
|
||||||
|
import com.github.libretube.util.PlayingQueue
|
||||||
|
|
||||||
class OnlinePlayerView(
|
class OnlinePlayerView(
|
||||||
context: Context,
|
context: Context,
|
||||||
@ -157,6 +163,15 @@ class OnlinePlayerView(
|
|||||||
binding.autoPlay.setOnCheckedChangeListener { _, isChecked ->
|
binding.autoPlay.setOnCheckedChangeListener { _, isChecked ->
|
||||||
PlayerHelper.autoPlayEnabled = isChecked
|
PlayerHelper.autoPlayEnabled = isChecked
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.sbSubmit.isVisible = PlayerHelper.sponsorBlockEnabled
|
||||||
|
binding.sbSubmit.setOnClickListener {
|
||||||
|
val currentPosition = player?.currentPosition?.takeIf { it != C.TIME_UNSET }
|
||||||
|
val duration = player?.duration?.takeIf { it != C.TIME_UNSET }
|
||||||
|
val videoId = PlayingQueue.getCurrent()?.url?.toID() ?: return@setOnClickListener
|
||||||
|
SubmitSegmentDialog(videoId, currentPosition ?: 0, duration)
|
||||||
|
.show((context as BaseActivity).supportFragmentManager, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hideController() {
|
override fun hideController() {
|
||||||
|
13
app/src/main/res/drawable/ic_upload_segment.xml
Normal file
13
app/src/main/res/drawable/ic_upload_segment.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24.121029dp"
|
||||||
|
android:tint="@android:color/white"
|
||||||
|
android:viewportWidth="565.15"
|
||||||
|
android:viewportHeight="568">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="m282.58,568a65,65 0,0 1,-34.14 -9.66C95.41,463.94 2.54,300.46 0,121a64.91,64.91 0,0 1,34 -58.09,522.56 522.56,0 0,1 497.16,0 64.91,64.91 0,0 1,34 58.12c-2.53,179.43 -95.4,342.91 -248.42,437.3A65,65 0,0 1,282.58 568ZM282.58,19.69A502.24,502.24 0,0 0,43.4 80.22,45.27 45.27,0 0,0 19.7,120.75c2.44,172.67 91.81,330 239.07,420.83a46.19,46.19 0,0 0,47.61 0C453.64,450.73 543,293.42 545.45,120.75A45.26,45.26 0,0 0,521.75 80.21,502.26 502.26,0 0,0 282.58,19.69Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M284.71,42.69A479.9,479.9 0,0 0,54.37 100.42A22.53,22.53 0,0 0,42.67 120.42C45.07,290.26 135.67,438.64 270.83,522.01A22.48,22.48 0,0 0,294.32 522.01C429.48,438.64 520.08,290.26 522.48,120.42A22.53,22.53 0,0 0,510.78 100.42A479.9,479.9 0,0 0,284.71 42.69zM282.57,112.11L282.87,112.11L423.76,365.75L330.3,365.75L330.3,409.21L234.85,409.21L234.85,365.75L141.39,365.75L282.57,112.11z" />
|
||||||
|
</vector>
|
50
app/src/main/res/layout/dialog_submit_segment.xml
Normal file
50
app/src/main/res/layout/dialog_submit_segment.xml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:baselineAligned="false">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/CustomDialogTextInputLayout"
|
||||||
|
android:hint="@string/start_time"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/start_time"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="numberDecimal" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/CustomDialogTextInputLayout"
|
||||||
|
android:hint="@string/end_time"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/end_time"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="numberDecimal" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.github.libretube.ui.views.DropdownMenu
|
||||||
|
android:id="@+id/segment_category"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="15dp"
|
||||||
|
app:hint="@string/segment_type"
|
||||||
|
app:icon="@drawable/ic_frame" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -81,6 +81,15 @@
|
|||||||
app:track="@drawable/player_switch_track"
|
app:track="@drawable/player_switch_track"
|
||||||
app:trackTint="#88ffffff" />
|
app:trackTint="#88ffffff" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/sb_submit"
|
||||||
|
android:tooltipText="@string/sb_create_segment"
|
||||||
|
style="@style/PlayerControlTop"
|
||||||
|
android:layout_marginEnd="2dp"
|
||||||
|
android:src="@drawable/ic_upload_segment"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:tint="@android:color/white" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/sb_toggle"
|
android:id="@+id/sb_toggle"
|
||||||
android:tooltipText="@string/tooltip_sponsorblock"
|
android:tooltipText="@string/tooltip_sponsorblock"
|
||||||
|
@ -469,4 +469,25 @@
|
|||||||
<item>auto</item>
|
<item>auto</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="sponsorBlockSegments">
|
||||||
|
<item>sponsor</item>
|
||||||
|
<item>intro</item>
|
||||||
|
<item>selfpromo</item>
|
||||||
|
<item>interaction</item>
|
||||||
|
<item>outro</item>
|
||||||
|
<item>filler</item>
|
||||||
|
<item>music_offtopic</item>
|
||||||
|
<item>preview</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="sponsorBlockSegmentNames">
|
||||||
|
<item>@string/category_sponsor</item>
|
||||||
|
<item>@string/category_intro</item>
|
||||||
|
<item>@string/category_selfpromo</item>
|
||||||
|
<item>@string/category_interaction</item>
|
||||||
|
<item>@string/category_outro</item>
|
||||||
|
<item>@string/category_filler</item>
|
||||||
|
<item>@string/category_music_offtopic</item>
|
||||||
|
<item>@string/category_preview</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -468,6 +468,10 @@
|
|||||||
<string name="auto_generated">auto-generated</string>
|
<string name="auto_generated">auto-generated</string>
|
||||||
<string name="resolution_limited">limited</string>
|
<string name="resolution_limited">limited</string>
|
||||||
<string name="registration_disabled">Registration disabled</string>
|
<string name="registration_disabled">Registration disabled</string>
|
||||||
|
<string name="sb_create_segment">Create segment</string>
|
||||||
|
<string name="segment_type">Segment type</string>
|
||||||
|
<string name="sb_invalid_segment">Invalid segment start or end</string>
|
||||||
|
|
||||||
<!-- Notification channel strings -->
|
<!-- Notification channel strings -->
|
||||||
<string name="download_channel_name">Download Service</string>
|
<string name="download_channel_name">Download Service</string>
|
||||||
<string name="download_channel_description">Shows a notification when downloading media.</string>
|
<string name="download_channel_description">Shows a notification when downloading media.</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user