chore: Merge branch dev to main (#2957)

This commit is contained in:
oSumAtrIX 2024-04-02 02:23:06 +02:00 committed by GitHub
commit 8f2a03ac58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 222 additions and 110 deletions

34
.github/workflows/pull_strings.yml vendored Normal file
View file

@ -0,0 +1,34 @@
name: Pull strings
on:
workflow_dispatch:
schedule:
- cron: 0 * 1 * *
jobs:
pull:
name: Pull strings
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Pull strings
uses: crowdin/github-action@v1
with:
config: crowdin.yml
download_translations: true
localization_branch_name: feat/translations
create_pull_request: true
pull_request_title: "chore: Sync translations"
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
pull_request_base_branch_name: "dev"
commit_message: "chore: Sync translations"
github_user_name: revanced-bot
github_user_email: github@revanced.app
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

27
.github/workflows/push_strings.yml vendored Normal file
View file

@ -0,0 +1,27 @@
name: Push strings
on:
workflow_dispatch:
push:
paths:
- /src/main/resources/addresources/values/strings.xml
jobs:
push:
name: Push strings
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Push strings
uses: crowdin/github-action@v1
with:
config: crowdin.yml
upload_sources: true
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View file

@ -1,39 +0,0 @@
name: Sync Crowdin
on:
workflow_dispatch:
schedule:
- cron: 0 * 1 * *
push:
paths:
- /src/main/resources/addresources/values/strings.xml
jobs:
sync:
name: Sync translations
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Sync translations
uses: crowdin/github-action@v1
with:
config: crowdin.yml
upload_sources: true
upload_translations: false
download_translations: true
localization_branch_name: feat/translations
create_pull_request: true
pull_request_title: "chore: Sync translations"
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
pull_request_base_branch_name: "dev"
commit_message: "chore: Sync translations"
github_user_name: revanced-bot
github_user_email: github@revanced.app
env:
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}

View file

@ -1,3 +1,24 @@
# [4.6.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.5.1-dev.2...v4.6.0-dev.1) (2024-03-31)
### Features
* **Tumblr:** Add `Fix old versions` patch ([#2954](https://github.com/ReVanced/revanced-patches/issues/2954)) ([2fde60e](https://github.com/ReVanced/revanced-patches/commit/2fde60eceb0a96fa857c32cd55c1fd7fe776a679))
## [4.5.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v4.5.1-dev.1...v4.5.1-dev.2) (2024-03-30)
### Bug Fixes
* **Mi Fitness - Fix login:** Patch correct register ([#2942](https://github.com/ReVanced/revanced-patches/issues/2942)) ([dc96942](https://github.com/ReVanced/revanced-patches/commit/dc969422b5d50f21e6ea7a64b67dfc650fee6e36))
## [4.5.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v4.5.0...v4.5.1-dev.1) (2024-03-30)
### Bug Fixes
* **Tumblr:** Restore compatibility with latest versions ([#2955](https://github.com/ReVanced/revanced-patches/issues/2955)) ([2954ba7](https://github.com/ReVanced/revanced-patches/commit/2954ba78d21d77308404961f79234bbec606d42e))
# [4.5.0](https://github.com/ReVanced/revanced-patches/compare/v4.4.0...v4.5.0) (2024-03-30)

View file

@ -1056,6 +1056,16 @@ public final class app/revanced/patches/tumblr/featureflags/OverrideFeatureFlags
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/tumblr/fixes/FixOldVersionsPatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/tumblr/fixes/FixOldVersionsPatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
}
public final class app/revanced/patches/tumblr/fixes/fingerprints/HttpPathParserFingerprint : app/revanced/patcher/fingerprint/MethodFingerprint {
public static final field INSTANCE Lapp/revanced/patches/tumblr/fixes/fingerprints/HttpPathParserFingerprint;
}
public final class app/revanced/patches/tumblr/live/DisableTumblrLivePatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/tumblr/live/DisableTumblrLivePatch;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V

View file

@ -1,4 +1,4 @@
org.gradle.parallel = true
org.gradle.caching = true
kotlin.code.style = official
version = 4.5.0
version = 4.6.0-dev.1

View file

@ -2,13 +2,11 @@ package app.revanced.patches.mifitness.misc.login
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.mifitness.misc.login.fingerprints.XiaomiAccountManagerConstructorFingerprint
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Patch(
name = "Fix login",
@ -20,16 +18,9 @@ object FixLoginPatch : BytecodePatch(
setOf(XiaomiAccountManagerConstructorFingerprint),
) {
override fun execute(context: BytecodeContext) {
XiaomiAccountManagerConstructorFingerprint.result?.let {
it.mutableMethod.apply {
val isCertifiedIndex = it.scanResult.patternScanResult!!.startIndex
val isCertifiedRegister = getInstruction<OneRegisterInstruction>(isCertifiedIndex).registerA
addInstruction(
isCertifiedIndex,
"const/4 p$isCertifiedRegister, 0x0",
)
}
} ?: throw XiaomiAccountManagerConstructorFingerprint.exception
XiaomiAccountManagerConstructorFingerprint.result?.mutableMethod?.addInstruction(
0,
"const/16 p2, 0x0",
) ?: throw XiaomiAccountManagerConstructorFingerprint.exception
}
}

View file

@ -3,12 +3,14 @@ package app.revanced.patches.mifitness.misc.login.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal object XiaomiAccountManagerConstructorFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PRIVATE or AccessFlags.CONSTRUCTOR,
customFingerprint = { methodDef, _ ->
methodDef.definingClass == "Lcom/xiaomi/passport/accountmanager/XiaomiAccountManager;"
},
opcodes = listOf(Opcode.IF_NEZ),
parameters = listOf(
"Landroid/content/Context;",
"Z",
),
)

View file

@ -1,9 +1,12 @@
package app.revanced.patches.tumblr.annoyances.popups.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
// This method is responsible for loading and displaying the visual Layout of the Gift Message Popup.
internal object ShowGiftMessagePopupFingerprint : MethodFingerprint(
strings = listOf("activity", "anchorView"),
customFingerprint = { methodDef, _ -> methodDef.definingClass.endsWith("GiftMessagePopup;") }
strings = listOf("activity", "anchorView", "textMessage"),
returnType = "V",
accessFlags = AccessFlags.FINAL or AccessFlags.PUBLIC
)

View file

@ -28,6 +28,9 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
internal lateinit var addOverride: (name: String, value: String) -> Unit private set
override fun execute(context: BytecodeContext) = GetFeatureValueFingerprint.result?.let {
val configurationClass = it.method.definingClass
val featureClass = it.method.parameterTypes[0].toString()
// The method we want to inject into does not have enough registers, so we inject a helper method
// and inject more instructions into it later, see addOverride.
// This is not in an integration since the unused variable would get compiled away and the method would
@ -35,7 +38,7 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
val helperMethod = ImmutableMethod(
it.method.definingClass,
"getValueOverride",
listOf(ImmutableMethodParameter("Lcom/tumblr/configuration/Feature;", null, "feature")),
listOf(ImmutableMethodParameter(featureClass, null, "feature")),
"Ljava/lang/String;",
AccessFlags.PUBLIC or AccessFlags.FINAL,
null,
@ -50,7 +53,7 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
0,
"""
# toString() the enum value
invoke-virtual {p1}, Lcom/tumblr/configuration/Feature;->toString()Ljava/lang/String;
invoke-virtual {p1}, $featureClass->toString()Ljava/lang/String;
move-result-object v0
# !!! If you add more instructions above this line, update helperInsertIndex below!
@ -75,7 +78,7 @@ object OverrideFeatureFlagsPatch : BytecodePatch(
getFeatureIndex,
"""
# Call the Helper Method with the Feature
invoke-virtual {p0, p1}, Lcom/tumblr/configuration/Configuration;->getValueOverride(Lcom/tumblr/configuration/Feature;)Ljava/lang/String;
invoke-virtual {p0, p1}, $configurationClass->getValueOverride($featureClass)Ljava/lang/String;
move-result-object v0
# If it returned null, skip
if-eqz v0, :is_null

View file

@ -1,6 +1,8 @@
package app.revanced.patches.tumblr.featureflags.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
// This fingerprint targets the method to get the value of a Feature in the class "com.tumblr.configuration.Feature".
@ -19,5 +21,7 @@ internal object GetFeatureValueFingerprint : MethodFingerprint(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT
),
customFingerprint = { method, _ -> method.definingClass == "Lcom/tumblr/configuration/Configuration;" }
returnType = "Ljava/lang/String;",
parameters = listOf("L", "Z"),
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL
)

View file

@ -0,0 +1,38 @@
package app.revanced.patches.tumblr.fixes
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.tumblr.fixes.fingerprints.HttpPathParserFingerprint
import app.revanced.util.exception
@Patch(
name = "Fix old versions",
description = "Fixes old versions of the app (v33.2 and earlier) breaking due to Tumblr removing remnants of Tumblr" +
" Live from the API, which causes many requests to fail. This patch has no effect on newer versions of the app.",
compatiblePackages = [CompatiblePackage("com.tumblr")],
use = false,
)
@Suppress("unused")
object FixOldVersionsPatch : BytecodePatch(
setOf(HttpPathParserFingerprint),
) {
override fun execute(context: BytecodeContext) =
HttpPathParserFingerprint.result?.let {
val endIndex = it.scanResult.patternScanResult!!.endIndex
it.mutableMethod.addInstructions(
endIndex + 1,
"""
# Remove "?live_now" from the request path p2.
# p2 = p2.replace(p1, p3)
const-string p1, ",?live_now"
const-string p3, ""
invoke-virtual {p2, p1, p3}, Ljava/lang/String;->replace(Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
move-result-object p2
""",
)
} ?: throw HttpPathParserFingerprint.exception
}

View file

@ -0,0 +1,15 @@
package app.revanced.patches.tumblr.fixes.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
// Fingerprint for the parseHttpMethodAndPath from retrofit2
// https://github.com/square/retrofit/blob/ebf87b10997e2136af4d335276fa950221852c64/retrofit/src/main/java/retrofit2/RequestFactory.java#L270-L302
// Injecting here allows modifying the path/query params of API endpoints defined via annotations
object HttpPathParserFingerprint : MethodFingerprint(
strings = listOf("Only one HTTP method is allowed. Found: %s and %s."),
opcodes = listOf(
Opcode.IPUT_OBJECT,
Opcode.IPUT_BOOLEAN
)
)

View file

@ -8,11 +8,11 @@ import app.revanced.patches.tumblr.featureflags.OverrideFeatureFlagsPatch
import app.revanced.patches.tumblr.timelinefilter.TimelineFilterPatch
@Patch(
name = "Disable Tumblr Live",
description = "Disable the Tumblr Live tab button and dashboard carousel.",
dependencies = [OverrideFeatureFlagsPatch::class, TimelineFilterPatch::class],
compatiblePackages = [CompatiblePackage("com.tumblr")],
)
@Deprecated("Tumblr Live was removed and is no longer served in the feed, making this patch useless.")
@Suppress("unused")
object DisableTumblrLivePatch : BytecodePatch(emptySet()) {
override fun execute(context: BytecodeContext) {

View file

@ -46,7 +46,6 @@ import app.revanced.patches.youtube.misc.settings.SettingsPatch
)
@Suppress("unused")
object HidePlayerFlyoutMenuPatch : ResourcePatch() {
private const val KEY = "revanced_hide_player_flyout"
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/integrations/youtube/patches/components/PlayerFlyoutMenuItemsFilter;"
@ -55,21 +54,21 @@ object HidePlayerFlyoutMenuPatch : ResourcePatch() {
AddResourcesPatch(this::class)
SettingsPatch.PreferenceScreen.PLAYER.addPreferences(
PreferenceScreen(
key = KEY,
preferences = setOf(
SwitchPreference("${KEY}_captions"),
SwitchPreference("${KEY}_additional_settings"),
SwitchPreference("${KEY}_loop_video"),
SwitchPreference("${KEY}_ambient_mode"),
SwitchPreference("${KEY}_report"),
SwitchPreference("${KEY}_help"),
SwitchPreference("${KEY}_speed"),
SwitchPreference("${KEY}_more_info"),
SwitchPreference("${KEY}_audio_track"),
SwitchPreference("${KEY}_watch_in_vr"),
),
)
PreferenceScreen(
key = "revanced_hide_player_flyout",
preferences = setOf(
SwitchPreference("revanced_hide_player_flyout_captions"),
SwitchPreference("revanced_hide_player_flyout_additional_settings"),
SwitchPreference("revanced_hide_player_flyout_loop_video"),
SwitchPreference("revanced_hide_player_flyout_ambient_mode"),
SwitchPreference("revanced_hide_player_flyout_report"),
SwitchPreference("revanced_hide_player_flyout_help"),
SwitchPreference("revanced_hide_player_flyout_speed"),
SwitchPreference("revanced_hide_player_flyout_more_info"),
SwitchPreference("revanced_hide_player_flyout_audio_track"),
SwitchPreference("revanced_hide_player_flyout_watch_in_vr"),
)
)
)
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)

View file

@ -13,7 +13,9 @@
<string name="revanced_settings_import_failure_parse">Import failed: %s</string>
</patch>
<patch id="misc.gms.BaseGmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than what is here, otherwise the text can be clipped and not entirely shown. -->
<string name="gms_core_toast_not_installed_message">GmsCore is not installed. Install it.</string>
<!-- Translations of this should not be longer than what is here, otherwise the text can be clipped and not entirely shown. -->
<string name="gms_core_toast_not_whitelisted_message">Follow the \"Don\'t kill my app\" guide for GmsCore.</string>
<string name="gms_core_dialog_title">Action needed</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">GmsCore is not whitelisted from battery optimization.\n\nFollow the \"Don\'t kill my app\" guide for GmsCore.</string>
@ -187,8 +189,8 @@
<string name="revanced_hide_keyword_content_phrases_summary">Keywords and phrases to hide, separated by new lines\n\nWords with uppercase letters in the middle must be entered with the casing (ie: iPhone, TikTok, LeBlanc)</string>
<string name="revanced_hide_keyword_content_about_title">About keyword filtering</string>
<string name="revanced_hide_keyword_content_about_summary">Home/Subscription/Search results are filtered to hide content that matches keyword phrases\n\nLimitations\n• Some Shorts may not be hidden\n• Some UI components may not be hidden\n• Searching for a keyword may show no results</string>
<string name="revanced_hide_keyword_toast_invalid_common" formatted="false">Invalid keyword. Cannot use: \'%s\' as a filter</string>
<string name="revanced_hide_keyword_toast_invalid_length" formatted="false">Invalid keyword. \'%s\' is less than %s characters</string>
<string name="revanced_hide_keyword_toast_invalid_common">Invalid keyword. Cannot use: \'%s\' as a filter</string>
<string name="revanced_hide_keyword_toast_invalid_length">Invalid keyword. \'%1$s\' is less than %2$s characters</string>
</patch>
<patch id="ad.general.HideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Hide general ads</string>
@ -466,9 +468,6 @@
<string name="revanced_hide_load_more_button_summary_on">Button is hidden</string>
<string name="revanced_hide_load_more_button_summary_off">Button is shown</string>
</patch>
<patch id="layout.hide.player.flyoutmenupanel.HidePlayerFlyoutMenuPatch">
<string name="video_title">Video</string>
</patch>
<patch id="layout.hide.rollingnumber.DisableRollingNumberAnimationPatch">
<string name="revanced_disable_rolling_number_animations_title">Disable rolling number animations</string>
<string name="revanced_disable_rolling_number_animations_summary_on">Rolling numbers are not animated</string>
@ -564,16 +563,16 @@
<string name="revanced_ryd_settings_title">Return YouTube Dislike</string>
<string name="revanced_ryd_video_likes_hidden_by_video_owner">Hidden</string>
<string name="revanced_ryd_failure_connection_timeout">Dislikes temporarily not available (API timed out)</string>
<string name="revanced_ryd_failure_connection_status_code" formatted="false">Dislikes not available (status %d)</string>
<string name="revanced_ryd_failure_connection_status_code">Dislikes not available (status %d)</string>
<string name="revanced_ryd_failure_client_rate_limit_requested">Dislikes not available (client API limit reached)</string>
<string name="revanced_ryd_failure_generic">Dislikes not available (%s)</string>
<!-- corner case situation, where user enables RYD while video is playing and then tries
to vote for the video -->
<!-- corner case situation, where user enables RYD while video is playing and then tries to vote for the video -->
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">Reload video to vote using ReturnYouTubeDislike</string>
<string name="revanced_ryd_enable_title">Return YouTube Dislike</string>
<string name="revanced_ryd_enable_summary_on">Dislikes are shown</string>
<string name="revanced_ryd_enable_summary_off">Dislikes are not shown</string>
<string name="revanced_ryd_shorts_title">Show dislikes on Shorts</string>
<!-- The %s string is either 'revanced_ryd_shorts_summary_disclaimer', or it is an empty string. This text should read normally in both situations. -->
<string name="revanced_ryd_shorts_summary_on">Dislikes shown on Shorts %s</string>
<string name="revanced_ryd_shorts_summary_off">Dislikes hidden on Shorts</string>
<string name="revanced_ryd_shorts_summary_disclaimer">Limitation: Dislikes may not appear in incognito mode</string>
@ -589,6 +588,7 @@
<string name="revanced_ryd_about">About</string>
<string name="revanced_ryd_attribution_title">ReturnYouTubeDislike.com</string>
<string name="revanced_ryd_attribution_summary">Data is provided by the Return YouTube Dislike API. Tap here to learn more</string>
<!-- Statistic strings are shown in the settings only when ReVanced debug mode is enabled. Typical users will never see these statistics. -->
<string name="revanced_ryd_statistics_category_title">ReturnYouTubeDislike API statistics of this device</string>
<string name="revanced_ryd_statistics_getFetchCallResponseTimeAverage_title">API response time, average</string>
<string name="revanced_ryd_statistics_getFetchCallResponseTimeMin_title">API response time, minimum</string>
@ -599,8 +599,7 @@
<string name="revanced_ryd_statistics_getFetchCallCount_zero_summary">No network calls made</string>
<string name="revanced_ryd_statistics_getFetchCallCount_non_zero_summary">%d network calls made</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_title">API fetch votes, number of timeouts</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_zero_summary">No
network calls timed out</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_zero_summary">No network calls timed out</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_non_zero_summary">%d network calls timed out</string>
<string name="revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_title">API client rate limits</string>
<string name="revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_zero_summary">No client rate limits encountered</string>
@ -731,19 +730,19 @@
<string name="revanced_sb_skip_showbutton">Show a skip button</string>
<string name="revanced_sb_skip_seekbaronly">Show in seek bar</string>
<string name="revanced_sb_skip_ignore">Disable</string>
<string name="revanced_sb_submit_failed_invalid" formatted="false">Unable to submit segment: %s</string>
<string name="revanced_sb_submit_failed_invalid">Unable to submit segment: %s</string>
<string name="revanced_sb_submit_failed_timeout">SponsorBlock is temporarily down</string>
<string name="revanced_sb_submit_failed_unknown_error" formatted="false">Unable to submit segment (status: %d %s)</string>
<string name="revanced_sb_submit_failed_unknown_error">Unable to submit segment (status: %1$d %2$s)</string>
<string name="revanced_sb_submit_failed_rate_limit">Unable to submit segment.\nRate Limited (too many from the same user or IP)</string>
<string name="revanced_sb_submit_failed_forbidden" formatted="false">Can\'t submit the segment: %s</string>
<string name="revanced_sb_submit_failed_forbidden">Can\'t submit the segment: %s</string>
<string name="revanced_sb_submit_failed_duplicate">Can\'t submit the segment.\nAlready exists</string>
<string name="revanced_sb_submit_succeeded">Segment submitted successfully</string>
<string name="revanced_sb_sponsorblock_connection_failure_generic">SponsorBlock temporarily not available</string>
<string name="revanced_sb_sponsorblock_connection_failure_status" formatted="false">SponsorBlock temporarily not available (status %d)</string>
<string name="revanced_sb_sponsorblock_connection_failure_status">SponsorBlock temporarily not available (status %d)</string>
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock temporarily not available (API timed out)</string>
<string name="revanced_sb_vote_failed_timeout">Unable to vote for segment (API timed out)</string>
<string name="revanced_sb_vote_failed_unknown_error" formatted="false">Unable to vote for segment (status: %d %s)</string>
<string name="revanced_sb_vote_failed_forbidden" formatted="false">Unable to vote for segment: %s</string>
<string name="revanced_sb_vote_failed_unknown_error">Unable to vote for segment (status: %1$d %2$s)</string>
<string name="revanced_sb_vote_failed_forbidden">Unable to vote for segment: %s</string>
<string name="revanced_sb_vote_upvote">Upvote</string>
<string name="revanced_sb_vote_downvote">Downvote</string>
<string name="revanced_sb_vote_category">Change category</string>
@ -751,14 +750,14 @@
<string name="revanced_sb_new_segment_choose_category">Choose the segment category</string>
<string name="revanced_sb_new_segment_disabled_category">Category is disabled in settings. Enable category to submit.</string>
<string name="revanced_sb_new_segment_title">New SponsorBlock segment</string>
<string name="revanced_sb_new_segment_mark_time_as_question" formatted="false">Set %02d:%02d:%03d as the start or end of a new segment?</string>
<string name="revanced_sb_new_segment_mark_time_as_question">Set %1$02d:%2$02d:%3$03d as the start or end of a new segment?</string>
<string name="revanced_sb_new_segment_mark_start">start</string>
<string name="revanced_sb_new_segment_mark_end">end</string>
<string name="revanced_sb_new_segment_now">now</string>
<string name="revanced_sb_new_segment_time_start">Time the segment begins at</string>
<string name="revanced_sb_new_segment_time_end">Time the segment ends at</string>
<string name="revanced_sb_new_segment_confirm_title">Are the times correct?</string>
<string name="revanced_sb_new_segment_confirm_content" formatted="false">The segment lasts from %02d:%02d to %02d:%02d (%d minutes %02d seconds)\nIs it ready to submit?</string>
<string name="revanced_sb_new_segment_confirm_content">The segment lasts from %1$02d:%2$02d to %3$02d:%4$02d (%5$d minutes %6$02d seconds)\nIs it ready to submit?</string>
<string name="revanced_sb_new_segment_start_is_before_end">Start must be before the end</string>
<string name="revanced_sb_new_segment_mark_locations_first">Mark two locations on the time bar first</string>
<string name="revanced_sb_new_segment_preview_segment_first">Preview the segment, and ensure it skips smoothly</string>
@ -769,22 +768,22 @@
<string name="revanced_sb_stats_connection_failure">Stats temporarily not available (API is down)</string>
<string name="revanced_sb_stats_loading">Loading...</string>
<string name="revanced_sb_stats_sb_disabled">SponsorBlock is disabled</string>
<string name="revanced_sb_stats_username" formatted="false">Your username: &lt;b>%s&lt;/b></string>
<string name="revanced_sb_stats_username">Your username: &lt;b>%s&lt;/b></string>
<string name="revanced_sb_stats_username_change">Tap here to change your username</string>
<string name="revanced_sb_stats_username_change_unknown_error" formatted="false">Unable to change username: Status: %d %s</string>
<string name="revanced_sb_stats_username_change_unknown_error">Unable to change username: Status: %1$d %2$s</string>
<string name="revanced_sb_stats_username_changed">Username successfully changed</string>
<string name="revanced_sb_stats_reputation" formatted="false">Your reputation is &lt;b>%.2f&lt;/b></string>
<string name="revanced_sb_stats_submissions" formatted="false">You\'ve created &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_reputation">Your reputation is &lt;b>%.2f&lt;/b></string>
<string name="revanced_sb_stats_submissions">You\'ve created &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_saved_zero">SponsorBlock leaderboard</string>
<string name="revanced_sb_stats_saved" formatted="false">You\'ve saved people from &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_saved">You\'ve saved people from &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_saved_sum_zero">Tap here to see the global stats and top contributors</string>
<string name="revanced_sb_stats_saved_sum" formatted="false">That\'s &lt;b>%s&lt;/b> of their lives.&lt;br>Tap here to see the leaderboard</string>
<string name="revanced_sb_stats_self_saved" formatted="false">You\'ve skipped &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_self_saved_sum" formatted="false">That\'s &lt;b>%s&lt;/b></string>
<string name="revanced_sb_stats_saved_sum">That\'s &lt;b>%s&lt;/b> of their lives.&lt;br>Tap here to see the leaderboard</string>
<string name="revanced_sb_stats_self_saved">You\'ve skipped &lt;b>%s&lt;/b> segments</string>
<string name="revanced_sb_stats_self_saved_sum">That\'s &lt;b>%s&lt;/b></string>
<string name="revanced_sb_stats_self_saved_reset_title">Reset skipped segments counter?</string>
<string name="revanced_sb_stats_saved_hour_format" formatted="false">%s hours %s minutes</string>
<string name="revanced_sb_stats_saved_minute_format" formatted="false">%s minutes %s seconds</string>
<string name="revanced_sb_stats_saved_second_format" formatted="false">%s seconds</string>
<string name="revanced_sb_stats_saved_hour_format">%1$s hours %2$s minutes</string>
<string name="revanced_sb_stats_saved_minute_format">%1$s minutes %2$s seconds</string>
<string name="revanced_sb_stats_saved_second_format">%s seconds</string>
<string name="revanced_sb_color_dot_label">Color:</string>
<string name="revanced_sb_color_changed">Color changed</string>
<string name="revanced_sb_color_reset">Color reset</string>
@ -800,7 +799,10 @@
<string name="revanced_spoof_app_version_summary_on">Version spoofed</string>
<string name="revanced_spoof_app_version_summary_off">Version not spoofed</string>
<string name="revanced_spoof_app_version_user_dialog_message">App version will be spoofed to an older version of YouTube.\n\nThis will change the appearance and features of the app, but unknown side effects may occur.\n\nIf later turned off, it is recommended to clear the app data to prevent UI bugs.</string>
<!-- It is ideal, but not required, if the text here appears alphabetically after the text used for 'revanced_spoof_app_version_title'.
This is because the 'General layout' menu uses alphabetic sorting, and it functionally works better if the spoof target selector appears below the 'Spoof app version' UI switch -->
<string name="revanced_spoof_app_version_target_title">Spoof app version target</string>
<!-- 'RYD' is referring to 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_entry_1">18.33.40 - Restore RYD on Shorts incognito mode</string>
<string name="revanced_spoof_app_version_target_entry_2">18.20.39 - Restore wide video speed &amp; quality menu</string>
<string name="revanced_spoof_app_version_target_entry_3">18.09.39 - Restore library tab</string>
@ -897,6 +899,7 @@
<string name="revanced_spoof_device_dimensions_summary_on">Device dimensions spoofed</string>
<string name="revanced_spoof_device_dimensions_summary_off">Device dimensions not spoofed\n\nSpoofing the device dimensions can unlock higher video qualities but unknown side effects may occur</string>
</patch>
<!-- This patch is no longer used, these strings are not in use, and these strings likely will be deleted in the future. -->
<patch id="misc.fix.playback.SpoofSignaturePatch">
<string name="revanced_spoof_signature_verification_screen_title">Spoof app signature</string>
<string name="revanced_spoof_signature_verification_screen_summary">Spoof the app signature to prevent playback issues</string>
@ -941,6 +944,7 @@
<string name="revanced_disable_zoom_haptics_summary_on">Haptics are disabled</string>
<string name="revanced_disable_zoom_haptics_summary_off">Haptics are enabled</string>
</patch>
<!-- This patch is no longer used and these strings will soon be deleted. -->
<patch id="video.hdrbrightness.HDRBrightnessPatch">
<string name="revanced_hdr_auto_brightness_title">Enable auto HDR brightness</string>
<string name="revanced_hdr_auto_brightness_summary_on">Auto HDR brightness is enabled</string>
@ -963,12 +967,12 @@
<string name="revanced_video_quality_default_mobile_title">Default video quality on mobile network</string>
<string name="revanced_remember_video_quality_mobile">mobile</string>
<string name="revanced_remember_video_quality_wifi">wifi</string>
<string name="revanced_remember_video_quality_toast" formatted="false">Changed default %s quality to: %s</string>
<string name="revanced_remember_video_quality_toast">Changed default %1$s quality to: %2$s</string>
</patch>
<patch id="video.speed.custom.CustomPlaybackSpeedPatch">
<string name="revanced_custom_playback_speeds_title">Custom playback speeds</string>
<string name="revanced_custom_playback_speeds_summary">Add or change the available playback speeds</string>
<string name="revanced_custom_playback_speeds_invalid" formatted="false">Custom speeds must be less than %s Using default values.</string>
<string name="revanced_custom_playback_speeds_invalid">Custom speeds must be less than %s Using default values.</string>
<string name="revanced_custom_playback_speeds_parse_exception">Invalid custom playback speeds. Using default values.</string>
</patch>
<patch id="video.speed.remember.RememberPlaybackSpeedPatch">
@ -976,7 +980,7 @@
<string name="revanced_remember_playback_speed_last_selected_summary_on">Playback speed changes apply to all videos</string>
<string name="revanced_remember_playback_speed_last_selected_summary_off">Playback speed changes only apply to the current video</string>
<string name="revanced_playback_speed_default_title">Default playback speed</string>
<string name="revanced_remember_playback_speed_toast" formatted="false">Changed default speed to: %s</string>
<string name="revanced_remember_playback_speed_toast">Changed default speed to: %s</string>
</patch>
<patch id="video.videoqualitymenu.RestoreOldVideoQualityMenuResourcePatch">
<string name="revanced_restore_old_video_quality_menu_title">Restore old video quality menu</string>