Compare commits

...

92 Commits

Author SHA1 Message Date
semantic-release-bot
d01b9a67c5 chore: Release v5.10.0-dev.4 [skip ci]
# [5.10.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.3...v5.10.0-dev.4) (2025-01-27)

### Features

* **YouTube - Hide video description components:** Add `Hide How this content was made section` ([#4355](https://github.com/ReVanced/revanced-patches/issues/4355)) ([a72404e](a72404eeab))
2025-01-27 08:39:16 +00:00
ILoveOpenSourceApplications
a72404eeab feat(YouTube - Hide video description components): Add Hide How this content was made section (#4355) 2025-01-27 10:36:13 +02:00
semantic-release-bot
3ff104528e chore: Release v5.10.0-dev.3 [skip ci]
# [5.10.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.2...v5.10.0-dev.3) (2025-01-27)

### Features

* **YouTube - Hide ads:** Add `Hide end screen store banner` ([#4351](https://github.com/ReVanced/revanced-patches/issues/4351)) ([76bbd7e](76bbd7ed2f))
2025-01-27 08:35:53 +00:00
ILoveOpenSourceApplications
76bbd7ed2f feat(YouTube - Hide ads): Add Hide end screen store banner (#4351) 2025-01-27 10:32:15 +02:00
semantic-release-bot
2fdf0f85c1 chore: Release v5.10.0-dev.2 [skip ci]
# [5.10.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.1...v5.10.0-dev.2) (2025-01-25)

### Features

* **YouTube:** Add patch `Disable HDR video` ([#4347](https://github.com/ReVanced/revanced-patches/issues/4347)) ([1d12c41](1d12c4156d))
2025-01-25 08:29:47 +00:00
LisoUseInAIKyrios
1d12c4156d feat(YouTube): Add patch Disable HDR video (#4347) 2025-01-25 10:26:46 +02:00
semantic-release-bot
c43050dce8 chore: Release v5.10.0-dev.1 [skip ci]
# [5.10.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.4...v5.10.0-dev.1) (2025-01-23)

### Features

* **YouTube - Theme:** Add option to use custom seekbar accent color ([#4337](https://github.com/ReVanced/revanced-patches/issues/4337)) ([8104bbd](8104bbd7d7))
2025-01-23 20:18:40 +00:00
LisoUseInAIKyrios
8104bbd7d7 feat(YouTube - Theme): Add option to use custom seekbar accent color (#4337) 2025-01-23 22:15:23 +02:00
semantic-release-bot
8487888e6b chore: Release v5.9.1-dev.4 [skip ci]
## [5.9.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.3...v5.9.1-dev.4) (2025-01-22)

### Bug Fixes

* **YouTube - Hide layout components:** Hide new kind of community post ([#4341](https://github.com/ReVanced/revanced-patches/issues/4341)) ([6721a28](6721a284cd))
2025-01-22 21:03:27 +00:00
Bceez
6721a284cd fix(YouTube - Hide layout components): Hide new kind of community post (#4341) 2025-01-22 22:00:33 +01:00
semantic-release-bot
6cde702854 chore: Release v5.9.1-dev.3 [skip ci]
## [5.9.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.2...v5.9.1-dev.3) (2025-01-22)

### Bug Fixes

* **YouTube - Hide seekbar:** Do not hide player seekbar if hide feed seekbar is enabled ([#4333](https://github.com/ReVanced/revanced-patches/issues/4333)) ([7c8efca](7c8efcaf41))
2025-01-22 12:01:57 +00:00
LisoUseInAIKyrios
7c8efcaf41 fix(YouTube - Hide seekbar): Do not hide player seekbar if hide feed seekbar is enabled (#4333) 2025-01-22 12:57:53 +01:00
semantic-release-bot
350ee02e3b chore: Release v5.9.1-dev.2 [skip ci]
## [5.9.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.1...v5.9.1-dev.2) (2025-01-22)

### Bug Fixes

* **YouTube - Theme:** Fix 19.25 - 19.45 patch error ([df2d070](df2d070a43))
2025-01-22 08:26:21 +00:00
LisoUseInAIKyrios
df2d070a43 fix(YouTube - Theme): Fix 19.25 - 19.45 patch error 2025-01-22 09:23:31 +01:00
semantic-release-bot
8167aaccc8 chore: Release v5.9.1-dev.1 [skip ci]
## [5.9.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.9.0...v5.9.1-dev.1) (2025-01-21)

### Bug Fixes

* **YouTube - Theme:** Replace custom seekbar gradient colors instead of disabling ([#4329](https://github.com/ReVanced/revanced-patches/issues/4329)) ([f4989ed](f4989ed0a5))
2025-01-21 20:23:57 +00:00
LisoUseInAIKyrios
f4989ed0a5 fix(YouTube - Theme): Replace custom seekbar gradient colors instead of disabling (#4329) 2025-01-21 21:20:19 +01:00
semantic-release-bot
8f5a0531bc chore: Release v5.9.0 [skip ci]
# [5.9.0](https://github.com/ReVanced/revanced-patches/compare/v5.8.1...v5.9.0) (2025-01-20)

### Bug Fixes

* **YouTube - Spoof video streams:** Resolve playback issues after changing from cellular to wifi ([#4277](https://github.com/ReVanced/revanced-patches/issues/4277)) ([fcad0ab](fcad0ab5bb))
* **YouTube - Spoof video streams:** Update client user-agent ([#4304](https://github.com/ReVanced/revanced-patches/issues/4304)) ([d85bcc3](d85bcc3c16))

### Features

* **YouTube - Hide feed components:** Handle new type of surveys ([#4295](https://github.com/ReVanced/revanced-patches/issues/4295)) ([e5e897d](e5e897de77))
* **YouTube - Playback speed:** Add option to change 2x tap and hold speed ([#4307](https://github.com/ReVanced/revanced-patches/issues/4307)) ([0615990](0615990138))
* **YouTube - Settings:** Add option to use new Cairo settings menus ([#4305](https://github.com/ReVanced/revanced-patches/issues/4305)) ([064b859](064b859d39))
2025-01-20 11:18:05 +00:00
LisoUseInAIKyrios
622554de14 chore: Merge branch dev to main (#4280) 2025-01-20 13:14:47 +02:00
github-actions[bot]
66e330ffe6 chore: Sync translations (#4319) 2025-01-20 12:14:23 +01:00
LisoUseInAIKyrios
2afcd3d63d chore: Change localized string log to warning 2025-01-20 11:52:31 +01:00
semantic-release-bot
80d7c78cf6 chore: Release v5.9.0-dev.4 [skip ci]
# [5.9.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.3...v5.9.0-dev.4) (2025-01-20)

### Bug Fixes

* **YouTube - Spoof video streams:** Update client user-agent ([#4304](https://github.com/ReVanced/revanced-patches/issues/4304)) ([d85bcc3](d85bcc3c16))
2025-01-20 10:51:48 +00:00
LisoUseInAIKyrios
d85bcc3c16 fix(YouTube - Spoof video streams): Update client user-agent (#4304) 2025-01-20 11:49:00 +01:00
github-actions[bot]
21368ea696 chore: Sync translations (#4318) 2025-01-20 11:48:40 +01:00
semantic-release-bot
e687d3ed37 chore: Release v5.9.0-dev.3 [skip ci]
# [5.9.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.2...v5.9.0-dev.3) (2025-01-19)

### Features

* **YouTube - Settings:** Add option to use new Cairo settings menus ([#4305](https://github.com/ReVanced/revanced-patches/issues/4305)) ([064b859](064b859d39))
2025-01-19 23:25:39 +00:00
LisoUseInAIKyrios
064b859d39 feat(YouTube - Settings): Add option to use new Cairo settings menus (#4305)
Co-authored-by: MarcaDian <152095496+marcadian@users.noreply.github.com>
2025-01-20 01:22:15 +02:00
github-actions[bot]
89882ddaf8 chore: Sync translations (#4316) 2025-01-20 01:21:48 +02:00
semantic-release-bot
41881ba161 chore: Release v5.9.0-dev.2 [skip ci]
# [5.9.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.1...v5.9.0-dev.2) (2025-01-18)

### Features

* **YouTube - Playback speed:** Add option to change 2x tap and hold speed ([#4307](https://github.com/ReVanced/revanced-patches/issues/4307)) ([0615990](0615990138))
2025-01-18 09:40:42 +00:00
LisoUseInAIKyrios
0615990138 feat(YouTube - Playback speed): Add option to change 2x tap and hold speed (#4307) 2025-01-18 10:37:34 +01:00
semantic-release-bot
70532313db chore: Release v5.9.0-dev.1 [skip ci]
# [5.9.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.2-dev.1...v5.9.0-dev.1) (2025-01-17)

### Features

* **YouTube - Hide feed components:** Handle new type of surveys ([#4295](https://github.com/ReVanced/revanced-patches/issues/4295)) ([e5e897d](e5e897de77))
2025-01-17 00:28:17 +00:00
ILoveOpenSourceApplications
e5e897de77 feat(YouTube - Hide feed components): Handle new type of surveys (#4295) 2025-01-17 01:25:43 +01:00
semantic-release-bot
1e57ce9658 chore: Release v5.8.2-dev.1 [skip ci]
## [5.8.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.1...v5.8.2-dev.1) (2025-01-09)

### Bug Fixes

* **YouTube - Spoof video streams:** Resolve playback issues after changing from cellular to wifi ([#4277](https://github.com/ReVanced/revanced-patches/issues/4277)) ([fcad0ab](fcad0ab5bb))
2025-01-09 17:13:13 +00:00
LisoUseInAIKyrios
fcad0ab5bb fix(YouTube - Spoof video streams): Resolve playback issues after changing from cellular to wifi (#4277) 2025-01-09 18:09:44 +01:00
semantic-release-bot
91471eccf9 chore: Release v5.8.1 [skip ci]
## [5.8.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.0...v5.8.1) (2025-01-07)

### Bug Fixes

* **YouTube - Spoof video streams:** Add 'Android Creator' ([#4262](https://github.com/ReVanced/revanced-patches/issues/4262)) ([3ee99b7](3ee99b7bf1))
2025-01-07 16:01:12 +00:00
oSumAtrIX
d559f016c6 chore: Merge branch dev to main (#4271) 2025-01-07 16:57:59 +01:00
github-actions[bot]
5a82d26f03 chore: Sync translations (#4275) 2025-01-07 12:17:17 +01:00
LisoUseInAIKyrios
e2eae499d9 ci: Fix crowdin cron pull strings? 2025-01-07 12:12:53 +01:00
LisoUseInAIKyrios
64919d6443 chore: Fix typo 2025-01-06 14:57:11 +01:00
semantic-release-bot
c6ffaf86ae chore: Release v5.8.1-dev.1 [skip ci]
## [5.8.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.0...v5.8.1-dev.1) (2025-01-06)

### Bug Fixes

* **YouTube - Spoof video streams:** Add 'Android Creator' ([#4262](https://github.com/ReVanced/revanced-patches/issues/4262)) ([3ee99b7](3ee99b7bf1))
2025-01-06 11:01:28 +00:00
LisoUseInAIKyrios
3ee99b7bf1 fix(YouTube - Spoof video streams): Add 'Android Creator' (#4262) 2025-01-06 11:58:27 +01:00
semantic-release-bot
6f9bf4873f chore: Release v5.8.0 [skip ci]
# [5.8.0](https://github.com/ReVanced/revanced-patches/compare/v5.7.2...v5.8.0) (2024-12-30)

### Bug Fixes

* **GmsCore support:** Do not show battery optimization error on Android Automotive devices (Google built-in) ([#4218](https://github.com/ReVanced/revanced-patches/issues/4218)) ([fa4aa54](fa4aa54f0c))
* **YouTube - Exit fullscreen mode:** Exit fullscreen mode of first video opened after cold start ([9496438](9496438da1))
* **YouTube - Force original audio:** If stream spoofing to Android then show a summary text why force audio is not available ([#4220](https://github.com/ReVanced/revanced-patches/issues/4220)) ([4de768f](4de768febf))
* **YouTube - Spoof video streams:** Ignore harmless error toast if hide ads is disabled ([e7c6943](e7c6943ca7))

### Features

* **Swipe controls:** Add option to enable/disable fullscreen swipe to next video ([#4222](https://github.com/ReVanced/revanced-patches/issues/4222)) ([29dbc9f](29dbc9ffbf))
* **YouTube - Hide Shorts components:** Add option to hide Shorts in watch history ([#4214](https://github.com/ReVanced/revanced-patches/issues/4214)) ([094a6aa](094a6aa6de))
* **YouTube - Spoof app version:** Add 'Restore old navigation and toolbar icons' ([9fac161](9fac1614e7))
* **YouTube:** Add `Change form factor` patch ([#4217](https://github.com/ReVanced/revanced-patches/issues/4217)) ([189e1c9](189e1c90c4))
* **YouTube:** Add `Exit fullscreen mode` patch ([#4223](https://github.com/ReVanced/revanced-patches/issues/4223)) ([f3c4d6f](f3c4d6fd64))
* **YouTube:** Add in app option to select a preferred language for ReVanced specific text ([#4231](https://github.com/ReVanced/revanced-patches/issues/4231)) ([7b90baa](7b90baadb5))
2024-12-30 01:50:09 +00:00
LisoUseInAIKyrios
29a73089a3 chore: Merge branch dev to main (#4213) 2024-12-30 05:46:51 +04:00
github-actions[bot]
74ef1841eb chore: Sync translations (#4240) 2024-12-30 05:41:11 +04:00
github-actions[bot]
0c544d28e3 chore: Sync translations (#4239) 2024-12-30 04:55:33 +04:00
semantic-release-bot
b1e5b99b44 chore: Release v5.8.0-dev.8 [skip ci]
# [5.8.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.7...v5.8.0-dev.8) (2024-12-28)

### Features

* **YouTube:** Add in app option to select a preferred language for ReVanced specific text ([#4231](https://github.com/ReVanced/revanced-patches/issues/4231)) ([7b90baa](7b90baadb5))
2024-12-28 08:33:56 +00:00
LisoUseInAIKyrios
7b90baadb5 feat(YouTube): Add in app option to select a preferred language for ReVanced specific text (#4231) 2024-12-28 12:30:57 +04:00
semantic-release-bot
4a6f3c8555 chore: Release v5.8.0-dev.7 [skip ci]
# [5.8.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.6...v5.8.0-dev.7) (2024-12-27)

### Bug Fixes

* **YouTube - Spoof video streams:** Ignore harmless error toast if hide ads is disabled ([e7c6943](e7c6943ca7))
2024-12-27 21:13:07 +00:00
LisoUseInAIKyrios
e7c6943ca7 fix(YouTube - Spoof video streams): Ignore harmless error toast if hide ads is disabled 2024-12-28 01:10:01 +04:00
semantic-release-bot
ae1b987c0d chore: Release v5.8.0-dev.6 [skip ci]
# [5.8.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.5...v5.8.0-dev.6) (2024-12-27)

### Bug Fixes

* **YouTube - Exit fullscreen mode:** Exit fullscreen mode of first video opened after cold start ([9496438](9496438da1))
2024-12-27 15:34:52 +00:00
LisoUseInAIKyrios
9496438da1 fix(YouTube - Exit fullscreen mode): Exit fullscreen mode of first video opened after cold start 2024-12-27 19:31:39 +04:00
github-actions[bot]
fa51631ea6 chore: Sync translations (#4232) 2024-12-27 19:30:03 +04:00
LisoUseInAIKyrios
8bf7108001 ci: Not fixing Crowdin cron task 2024-12-27 19:27:52 +04:00
LisoUseInAIKyrios
030eece04a refactor(YouTube - Exit fullscreen mode): Improve logging 2024-12-27 18:19:43 +04:00
LisoUseInAIKyrios
30009b723d refactor: Change context field to volatile
Field is set from main thread, but can be immediately accessed by non main threads.
2024-12-27 11:15:35 +04:00
semantic-release-bot
53b25ea7e9 chore: Release v5.8.0-dev.5 [skip ci]
# [5.8.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.4...v5.8.0-dev.5) (2024-12-27)

### Features

* **YouTube:** Add `Change form factor` patch ([#4217](https://github.com/ReVanced/revanced-patches/issues/4217)) ([189e1c9](189e1c90c4))
2024-12-27 06:51:57 +00:00
LisoUseInAIKyrios
189e1c90c4 feat(YouTube): Add Change form factor patch (#4217) 2024-12-27 10:48:14 +04:00
github-actions[bot]
f01603b3f3 chore: Sync translations (#4229) 2024-12-27 10:46:32 +04:00
semantic-release-bot
3db5651e5c chore: Release v5.8.0-dev.4 [skip ci]
# [5.8.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.3...v5.8.0-dev.4) (2024-12-27)

### Bug Fixes

* **GmsCore support:** Do not show battery optimization error on Android Automotive devices (Google built-in) ([#4218](https://github.com/ReVanced/revanced-patches/issues/4218)) ([fa4aa54](fa4aa54f0c))

### Features

* **Swipe controls:** Add option to enable/disable fullscreen swipe to next video ([#4222](https://github.com/ReVanced/revanced-patches/issues/4222)) ([29dbc9f](29dbc9ffbf))
* **YouTube:** Add `Exit fullscreen mode` patch ([#4223](https://github.com/ReVanced/revanced-patches/issues/4223)) ([f3c4d6f](f3c4d6fd64))
2024-12-27 06:28:58 +00:00
LisoUseInAIKyrios
f3c4d6fd64 feat(YouTube): Add Exit fullscreen mode patch (#4223) 2024-12-27 10:25:17 +04:00
LisoUseInAIKyrios
29dbc9ffbf feat(Swipe controls): Add option to enable/disable fullscreen swipe to next video (#4222) 2024-12-27 10:23:30 +04:00
LisoUseInAIKyrios
fa4aa54f0c fix(GmsCore support): Do not show battery optimization error on Android Automotive devices (Google built-in) (#4218) 2024-12-27 10:22:50 +04:00
github-actions[bot]
1d89ada07f chore: Sync translations (#4228) 2024-12-27 10:22:25 +04:00
LisoUseInAIKyrios
8c529abad5 ci: Fix Crowdin cron task? 2024-12-27 10:17:28 +04:00
LisoUseInAIKyrios
4ade7c7329 ci: Fix Crowdin cron task? 2024-12-26 14:08:46 +04:00
semantic-release-bot
f35247a872 chore: Release v5.8.0-dev.3 [skip ci]
# [5.8.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.2...v5.8.0-dev.3) (2024-12-26)

### Bug Fixes

* **YouTube - Force original audio:** If stream spoofing to Android then show a summary text why force audio is not available ([#4220](https://github.com/ReVanced/revanced-patches/issues/4220)) ([4de768f](4de768febf))
2024-12-26 10:02:26 +00:00
LisoUseInAIKyrios
4de768febf fix(YouTube - Force original audio): If stream spoofing to Android then show a summary text why force audio is not available (#4220) 2024-12-26 13:58:29 +04:00
github-actions[bot]
1a5c86db93 chore: Sync translations (#4216) 2024-12-26 13:58:13 +04:00
LisoUseInAIKyrios
dbba795468 chore(YouTube): Fix inconsistent strings 2024-12-25 04:59:12 +04:00
semantic-release-bot
0a9320551d chore: Release v5.8.0-dev.2 [skip ci]
# [5.8.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.1...v5.8.0-dev.2) (2024-12-24)

### Features

* **YouTube - Spoof app version:** Add 'Restore old navigation and toolbar icons' ([9fac161](9fac1614e7))
2024-12-24 22:11:57 +00:00
LisoUseInAIKyrios
9fac1614e7 feat(YouTube - Spoof app version): Add 'Restore old navigation and toolbar icons' 2024-12-25 02:09:10 +04:00
semantic-release-bot
2de3523c59 chore: Release v5.8.0-dev.1 [skip ci]
# [5.8.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.2...v5.8.0-dev.1) (2024-12-24)

### Features

* **YouTube - Hide Shorts components:** Add option to hide Shorts in watch history ([#4214](https://github.com/ReVanced/revanced-patches/issues/4214)) ([094a6aa](094a6aa6de))
2024-12-24 21:37:46 +00:00
github-actions[bot]
ad1e40b130 chore: Sync translations (#4215) 2024-12-25 01:34:11 +04:00
LisoUseInAIKyrios
094a6aa6de feat(YouTube - Hide Shorts components): Add option to hide Shorts in watch history (#4214) 2024-12-25 01:32:42 +04:00
LisoUseInAIKyrios
a14e03e4bb chore(YouTube - Spoof video streams): Update iOS side effects text 2024-12-24 18:40:55 +04:00
semantic-release-bot
6f40b6d30f chore: Release v5.7.2 [skip ci]
## [5.7.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.1...v5.7.2) (2024-12-24)

### Bug Fixes

* **YouTube - Hide layout components:** Don't hide Shorts channel bar when toggling for video player ([87e1c7f](87e1c7f4c8))
* **YouTube - Spoof video streams:** Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds ([#4202](https://github.com/ReVanced/revanced-patches/issues/4202)) ([ca21a69](ca21a69550))
2024-12-24 06:55:57 +00:00
LisoUseInAIKyrios
1711e1c39d chore: Merge branch dev to main (#4205) 2024-12-24 10:52:54 +04:00
github-actions[bot]
25372828d1 chore: Sync translations (#4210) 2024-12-24 10:52:18 +04:00
semantic-release-bot
f58245c6cd chore: Release v5.7.2-dev.2 [skip ci]
## [5.7.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.2-dev.1...v5.7.2-dev.2) (2024-12-23)

### Bug Fixes

* **YouTube - Hide layout components:** Don't hide Shorts channel bar when toggling for video player ([87e1c7f](87e1c7f4c8))
2024-12-23 23:00:32 +00:00
oSumAtrIX
87e1c7f4c8 fix(YouTube - Hide layout components): Don't hide Shorts channel bar when toggling for video player 2024-12-23 23:57:53 +01:00
semantic-release-bot
55d01c92d1 chore: Release v5.7.2-dev.1 [skip ci]
## [5.7.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.1...v5.7.2-dev.1) (2024-12-23)

### Bug Fixes

* **YouTube - Spoof video streams:** Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds ([#4202](https://github.com/ReVanced/revanced-patches/issues/4202)) ([ca21a69](ca21a69550))
2024-12-23 18:42:40 +00:00
LisoUseInAIKyrios
ca21a69550 fix(YouTube - Spoof video streams): Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds (#4202) 2024-12-23 22:39:27 +04:00
semantic-release-bot
634d0b4058 chore: Release v5.7.1 [skip ci]
## [5.7.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.0...v5.7.1) (2024-12-23)

### Bug Fixes

* **YouTube - SponsorBlock:** Show a toast and not a dialog if segment submitted successfully ([838edb4](838edb48e7))
* **YouTube - Spoof video streams:** Use 2 letter device language code ([e174113](e1741130af))
* **YouTube - Spoof video streams:** Use Android VR authentication if using default audio language ([#4191](https://github.com/ReVanced/revanced-patches/issues/4191)) ([99334d1](99334d1e53))
* **YouTube - Theme:** Use dark theme color for status and navigation bar ([4b81f70](4b81f7009b))
* **YouTube:** Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) ([#4195](https://github.com/ReVanced/revanced-patches/issues/4195)) ([0ae756b](0ae756b0fc))
2024-12-23 01:16:40 +00:00
LisoUseInAIKyrios
47ea8d5ec8 chore: Merge branch dev to main (#4192) 2024-12-23 05:13:45 +04:00
github-actions[bot]
9509ed53f3 chore: Sync translations (#4198) 2024-12-23 04:53:27 +04:00
semantic-release-bot
39542ddf55 chore: Release v5.7.1-dev.5 [skip ci]
## [5.7.1-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.4...v5.7.1-dev.5) (2024-12-22)

### Bug Fixes

* **YouTube - Spoof video streams:** Use 2 letter device language code ([e174113](e1741130af))
2024-12-22 23:40:44 +00:00
LisoUseInAIKyrios
e1741130af fix(YouTube - Spoof video streams): Use 2 letter device language code 2024-12-23 03:37:21 +04:00
semantic-release-bot
e54eb3ce87 chore: Release v5.7.1-dev.4 [skip ci]
## [5.7.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.3...v5.7.1-dev.4) (2024-12-22)

### Bug Fixes

* **YouTube:** Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) ([#4195](https://github.com/ReVanced/revanced-patches/issues/4195)) ([0ae756b](0ae756b0fc))
2024-12-22 17:45:50 +00:00
LisoUseInAIKyrios
0ae756b0fc fix(YouTube): Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) (#4195) 2024-12-22 21:42:41 +04:00
github-actions[bot]
77a0ac5c9c chore: Sync translations (#4196) 2024-12-22 21:42:29 +04:00
semantic-release-bot
899121b9de chore: Release v5.7.1-dev.3 [skip ci]
## [5.7.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.2...v5.7.1-dev.3) (2024-12-22)

### Bug Fixes

* **YouTube - SponsorBlock:** Show a toast and not a dialog if segment submitted successfully ([838edb4](838edb48e7))
2024-12-22 17:34:19 +00:00
LisoUseInAIKyrios
838edb48e7 fix(YouTube - SponsorBlock): Show a toast and not a dialog if segment submitted successfully 2024-12-22 21:31:41 +04:00
semantic-release-bot
b2665c916a chore: Release v5.7.1-dev.2 [skip ci]
## [5.7.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.1...v5.7.1-dev.2) (2024-12-22)

### Bug Fixes

* **YouTube - Theme:** Use dark theme color for status and navigation bar ([4b81f70](4b81f7009b))
2024-12-22 11:29:57 +00:00
LisoUseInAIKyrios
4b81f7009b fix(YouTube - Theme): Use dark theme color for status and navigation bar 2024-12-22 15:27:02 +04:00
174 changed files with 9547 additions and 5368 deletions

View File

@@ -16,8 +16,9 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: dev
fetch-depth: 0
clean: true
- name: Pull strings
uses: crowdin/github-action@v2

View File

@@ -1,3 +1,266 @@
# [5.10.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.3...v5.10.0-dev.4) (2025-01-27)
### Features
* **YouTube - Hide video description components:** Add `Hide How this content was made section` ([#4355](https://github.com/ReVanced/revanced-patches/issues/4355)) ([68ec54e](https://github.com/ReVanced/revanced-patches/commit/68ec54ef850ae8d6461dd0ef2846e6efbb59e482))
# [5.10.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.2...v5.10.0-dev.3) (2025-01-27)
### Features
* **YouTube - Hide ads:** Add `Hide end screen store banner` ([#4351](https://github.com/ReVanced/revanced-patches/issues/4351)) ([5505087](https://github.com/ReVanced/revanced-patches/commit/55050878028fed82b0f583a9f7ba06b8f267f8ec))
# [5.10.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.10.0-dev.1...v5.10.0-dev.2) (2025-01-25)
### Features
* **YouTube:** Add patch `Disable HDR video` ([#4347](https://github.com/ReVanced/revanced-patches/issues/4347)) ([0528f7c](https://github.com/ReVanced/revanced-patches/commit/0528f7cad856a2b1347e41944167b0583fc4a3d9))
# [5.10.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.4...v5.10.0-dev.1) (2025-01-23)
### Features
* **YouTube - Theme:** Add option to use custom seekbar accent color ([#4337](https://github.com/ReVanced/revanced-patches/issues/4337)) ([952b4fc](https://github.com/ReVanced/revanced-patches/commit/952b4fc4c9291e1a3e71437b503857763c973dd4))
## [5.9.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.3...v5.9.1-dev.4) (2025-01-22)
### Bug Fixes
* **YouTube - Hide layout components:** Hide new kind of community post ([#4341](https://github.com/ReVanced/revanced-patches/issues/4341)) ([02685c4](https://github.com/ReVanced/revanced-patches/commit/02685c4567aca55f22d45dc238a7d1f0ea264143))
## [5.9.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.2...v5.9.1-dev.3) (2025-01-22)
### Bug Fixes
* **YouTube - Hide seekbar:** Do not hide player seekbar if hide feed seekbar is enabled ([#4333](https://github.com/ReVanced/revanced-patches/issues/4333)) ([f5cf6f2](https://github.com/ReVanced/revanced-patches/commit/f5cf6f2a445492d33815a9772f49deac2d70eba9))
## [5.9.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.9.1-dev.1...v5.9.1-dev.2) (2025-01-22)
### Bug Fixes
* **YouTube - Theme:** Fix 19.25 - 19.45 patch error ([5b47a5f](https://github.com/ReVanced/revanced-patches/commit/5b47a5f0f6299daaae209341064fd85f16ca18a6))
## [5.9.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.9.0...v5.9.1-dev.1) (2025-01-21)
### Bug Fixes
* **YouTube - Theme:** Replace custom seekbar gradient colors instead of disabling ([#4329](https://github.com/ReVanced/revanced-patches/issues/4329)) ([f03da98](https://github.com/ReVanced/revanced-patches/commit/f03da983051021e0c372557a5354d5d967409564))
# [5.9.0](https://github.com/ReVanced/revanced-patches/compare/v5.8.1...v5.9.0) (2025-01-20)
### Bug Fixes
* **YouTube - Spoof video streams:** Resolve playback issues after changing from cellular to wifi ([#4277](https://github.com/ReVanced/revanced-patches/issues/4277)) ([e93e1c8](https://github.com/ReVanced/revanced-patches/commit/e93e1c8ec3367e941034e9c4e3725ec1db429a60))
* **YouTube - Spoof video streams:** Update client user-agent ([#4304](https://github.com/ReVanced/revanced-patches/issues/4304)) ([7917871](https://github.com/ReVanced/revanced-patches/commit/7917871f510b6b805370ef98a0cf8a4e2df0e900))
### Features
* **YouTube - Hide feed components:** Handle new type of surveys ([#4295](https://github.com/ReVanced/revanced-patches/issues/4295)) ([c770e03](https://github.com/ReVanced/revanced-patches/commit/c770e03f3801367cb531af860fbdfa43dca89af0))
* **YouTube - Playback speed:** Add option to change 2x tap and hold speed ([#4307](https://github.com/ReVanced/revanced-patches/issues/4307)) ([02fb26e](https://github.com/ReVanced/revanced-patches/commit/02fb26e9458fb8635d497e6e78f964055244d738))
* **YouTube - Settings:** Add option to use new Cairo settings menus ([#4305](https://github.com/ReVanced/revanced-patches/issues/4305)) ([7b8a2a2](https://github.com/ReVanced/revanced-patches/commit/7b8a2a2721ab5351f8c0251401aceddf0c5327df))
# [5.9.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.3...v5.9.0-dev.4) (2025-01-20)
### Bug Fixes
* **YouTube - Spoof video streams:** Update client user-agent ([#4304](https://github.com/ReVanced/revanced-patches/issues/4304)) ([7917871](https://github.com/ReVanced/revanced-patches/commit/7917871f510b6b805370ef98a0cf8a4e2df0e900))
# [5.9.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.2...v5.9.0-dev.3) (2025-01-19)
### Features
* **YouTube - Settings:** Add option to use new Cairo settings menus ([#4305](https://github.com/ReVanced/revanced-patches/issues/4305)) ([7b8a2a2](https://github.com/ReVanced/revanced-patches/commit/7b8a2a2721ab5351f8c0251401aceddf0c5327df))
# [5.9.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.9.0-dev.1...v5.9.0-dev.2) (2025-01-18)
### Features
* **YouTube - Playback speed:** Add option to change 2x tap and hold speed ([#4307](https://github.com/ReVanced/revanced-patches/issues/4307)) ([02fb26e](https://github.com/ReVanced/revanced-patches/commit/02fb26e9458fb8635d497e6e78f964055244d738))
# [5.9.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.2-dev.1...v5.9.0-dev.1) (2025-01-17)
### Features
* **YouTube - Hide feed components:** Handle new type of surveys ([#4295](https://github.com/ReVanced/revanced-patches/issues/4295)) ([c770e03](https://github.com/ReVanced/revanced-patches/commit/c770e03f3801367cb531af860fbdfa43dca89af0))
## [5.8.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.1...v5.8.2-dev.1) (2025-01-09)
### Bug Fixes
* **YouTube - Spoof video streams:** Resolve playback issues after changing from cellular to wifi ([#4277](https://github.com/ReVanced/revanced-patches/issues/4277)) ([e93e1c8](https://github.com/ReVanced/revanced-patches/commit/e93e1c8ec3367e941034e9c4e3725ec1db429a60))
## [5.8.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.0...v5.8.1) (2025-01-07)
### Bug Fixes
* **YouTube - Spoof video streams:** Add 'Android Creator' ([#4262](https://github.com/ReVanced/revanced-patches/issues/4262)) ([0479dd2](https://github.com/ReVanced/revanced-patches/commit/0479dd265e09b0accdf6ff6b00c8e938dc5b96c7))
## [5.8.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.8.0...v5.8.1-dev.1) (2025-01-06)
### Bug Fixes
* **YouTube - Spoof video streams:** Add 'Android Creator' ([#4262](https://github.com/ReVanced/revanced-patches/issues/4262)) ([0479dd2](https://github.com/ReVanced/revanced-patches/commit/0479dd265e09b0accdf6ff6b00c8e938dc5b96c7))
# [5.8.0](https://github.com/ReVanced/revanced-patches/compare/v5.7.2...v5.8.0) (2024-12-30)
### Bug Fixes
* **GmsCore support:** Do not show battery optimization error on Android Automotive devices (Google built-in) ([#4218](https://github.com/ReVanced/revanced-patches/issues/4218)) ([d6e389c](https://github.com/ReVanced/revanced-patches/commit/d6e389cc43bc40724f032b230f70048276349a19))
* **YouTube - Exit fullscreen mode:** Exit fullscreen mode of first video opened after cold start ([be5cf2e](https://github.com/ReVanced/revanced-patches/commit/be5cf2e834d87d51b5d3061d46bd7154d6306787))
* **YouTube - Force original audio:** If stream spoofing to Android then show a summary text why force audio is not available ([#4220](https://github.com/ReVanced/revanced-patches/issues/4220)) ([029aee8](https://github.com/ReVanced/revanced-patches/commit/029aee8023f096413fc80a2c583b4fe55ecb10ac))
* **YouTube - Spoof video streams:** Ignore harmless error toast if hide ads is disabled ([c3423bb](https://github.com/ReVanced/revanced-patches/commit/c3423bb9e531cfa52f6d28e0b98bbe8ab8684c30))
### Features
* **Swipe controls:** Add option to enable/disable fullscreen swipe to next video ([#4222](https://github.com/ReVanced/revanced-patches/issues/4222)) ([119092f](https://github.com/ReVanced/revanced-patches/commit/119092fafa4129849246df15fe8076ed3b491b85))
* **YouTube - Hide Shorts components:** Add option to hide Shorts in watch history ([#4214](https://github.com/ReVanced/revanced-patches/issues/4214)) ([19c2742](https://github.com/ReVanced/revanced-patches/commit/19c2742aa367367c77bb50ddad6f8a20fef8ea0a))
* **YouTube - Spoof app version:** Add 'Restore old navigation and toolbar icons' ([f84e459](https://github.com/ReVanced/revanced-patches/commit/f84e459d3d54b3001586796ab4e114ebadf09043))
* **YouTube:** Add `Change form factor` patch ([#4217](https://github.com/ReVanced/revanced-patches/issues/4217)) ([644ac5b](https://github.com/ReVanced/revanced-patches/commit/644ac5baa68b209a32300149a2efa009b776f9a7))
* **YouTube:** Add `Exit fullscreen mode` patch ([#4223](https://github.com/ReVanced/revanced-patches/issues/4223)) ([bb5d03b](https://github.com/ReVanced/revanced-patches/commit/bb5d03bd89a3f932c77e4e9de90174c374933688))
* **YouTube:** Add in app option to select a preferred language for ReVanced specific text ([#4231](https://github.com/ReVanced/revanced-patches/issues/4231)) ([3932af3](https://github.com/ReVanced/revanced-patches/commit/3932af397ae89a0b30191cd870bd6cddb7a078db))
# [5.8.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.7...v5.8.0-dev.8) (2024-12-28)
### Features
* **YouTube:** Add in app option to select a preferred language for ReVanced specific text ([#4231](https://github.com/ReVanced/revanced-patches/issues/4231)) ([3932af3](https://github.com/ReVanced/revanced-patches/commit/3932af397ae89a0b30191cd870bd6cddb7a078db))
# [5.8.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.6...v5.8.0-dev.7) (2024-12-27)
### Bug Fixes
* **YouTube - Spoof video streams:** Ignore harmless error toast if hide ads is disabled ([c3423bb](https://github.com/ReVanced/revanced-patches/commit/c3423bb9e531cfa52f6d28e0b98bbe8ab8684c30))
# [5.8.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.5...v5.8.0-dev.6) (2024-12-27)
### Bug Fixes
* **YouTube - Exit fullscreen mode:** Exit fullscreen mode of first video opened after cold start ([be5cf2e](https://github.com/ReVanced/revanced-patches/commit/be5cf2e834d87d51b5d3061d46bd7154d6306787))
# [5.8.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.4...v5.8.0-dev.5) (2024-12-27)
### Features
* **YouTube:** Add `Change form factor` patch ([#4217](https://github.com/ReVanced/revanced-patches/issues/4217)) ([644ac5b](https://github.com/ReVanced/revanced-patches/commit/644ac5baa68b209a32300149a2efa009b776f9a7))
# [5.8.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.3...v5.8.0-dev.4) (2024-12-27)
### Bug Fixes
* **GmsCore support:** Do not show battery optimization error on Android Automotive devices (Google built-in) ([#4218](https://github.com/ReVanced/revanced-patches/issues/4218)) ([d6e389c](https://github.com/ReVanced/revanced-patches/commit/d6e389cc43bc40724f032b230f70048276349a19))
### Features
* **Swipe controls:** Add option to enable/disable fullscreen swipe to next video ([#4222](https://github.com/ReVanced/revanced-patches/issues/4222)) ([119092f](https://github.com/ReVanced/revanced-patches/commit/119092fafa4129849246df15fe8076ed3b491b85))
* **YouTube:** Add `Exit fullscreen mode` patch ([#4223](https://github.com/ReVanced/revanced-patches/issues/4223)) ([bb5d03b](https://github.com/ReVanced/revanced-patches/commit/bb5d03bd89a3f932c77e4e9de90174c374933688))
# [5.8.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.2...v5.8.0-dev.3) (2024-12-26)
### Bug Fixes
* **YouTube - Force original audio:** If stream spoofing to Android then show a summary text why force audio is not available ([#4220](https://github.com/ReVanced/revanced-patches/issues/4220)) ([029aee8](https://github.com/ReVanced/revanced-patches/commit/029aee8023f096413fc80a2c583b4fe55ecb10ac))
# [5.8.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.8.0-dev.1...v5.8.0-dev.2) (2024-12-24)
### Features
* **YouTube - Spoof app version:** Add 'Restore old navigation and toolbar icons' ([f84e459](https://github.com/ReVanced/revanced-patches/commit/f84e459d3d54b3001586796ab4e114ebadf09043))
# [5.8.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.2...v5.8.0-dev.1) (2024-12-24)
### Features
* **YouTube - Hide Shorts components:** Add option to hide Shorts in watch history ([#4214](https://github.com/ReVanced/revanced-patches/issues/4214)) ([19c2742](https://github.com/ReVanced/revanced-patches/commit/19c2742aa367367c77bb50ddad6f8a20fef8ea0a))
## [5.7.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.1...v5.7.2) (2024-12-24)
### Bug Fixes
* **YouTube - Hide layout components:** Don't hide Shorts channel bar when toggling for video player ([9af6412](https://github.com/ReVanced/revanced-patches/commit/9af6412d92ec31e612eaabba6578453da0fc61d6))
* **YouTube - Spoof video streams:** Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds ([#4202](https://github.com/ReVanced/revanced-patches/issues/4202)) ([ab29f80](https://github.com/ReVanced/revanced-patches/commit/ab29f808a9f55b5ab0055533c1a6de549b0631a6))
## [5.7.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.2-dev.1...v5.7.2-dev.2) (2024-12-23)
### Bug Fixes
* **YouTube - Hide layout components:** Don't hide Shorts channel bar when toggling for video player ([9af6412](https://github.com/ReVanced/revanced-patches/commit/9af6412d92ec31e612eaabba6578453da0fc61d6))
## [5.7.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.1...v5.7.2-dev.1) (2024-12-23)
### Bug Fixes
* **YouTube - Spoof video streams:** Add iOS TV client, restore iOS 'force AVC', show client type in stats for nerds ([#4202](https://github.com/ReVanced/revanced-patches/issues/4202)) ([ab29f80](https://github.com/ReVanced/revanced-patches/commit/ab29f808a9f55b5ab0055533c1a6de549b0631a6))
## [5.7.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.0...v5.7.1) (2024-12-23)
### Bug Fixes
* **YouTube - SponsorBlock:** Show a toast and not a dialog if segment submitted successfully ([134b189](https://github.com/ReVanced/revanced-patches/commit/134b189791113dcf1a1cb7c87b8a0954f432730c))
* **YouTube - Spoof video streams:** Use 2 letter device language code ([33ff997](https://github.com/ReVanced/revanced-patches/commit/33ff9972000581aca92262f984efb114eeeb9537))
* **YouTube - Spoof video streams:** Use Android VR authentication if using default audio language ([#4191](https://github.com/ReVanced/revanced-patches/issues/4191)) ([98773cc](https://github.com/ReVanced/revanced-patches/commit/98773cc7d46e5c9c7715b82c8006f1ccbcc5443c))
* **YouTube - Theme:** Use dark theme color for status and navigation bar ([0240efe](https://github.com/ReVanced/revanced-patches/commit/0240efe33e5444625ca2b760c861c9046d3dc836))
* **YouTube:** Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) ([#4195](https://github.com/ReVanced/revanced-patches/issues/4195)) ([dda788c](https://github.com/ReVanced/revanced-patches/commit/dda788c58c789d4f91646ea8e8a8077f590ab6b3))
## [5.7.1-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.4...v5.7.1-dev.5) (2024-12-22)
### Bug Fixes
* **YouTube - Spoof video streams:** Use 2 letter device language code ([33ff997](https://github.com/ReVanced/revanced-patches/commit/33ff9972000581aca92262f984efb114eeeb9537))
## [5.7.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.3...v5.7.1-dev.4) (2024-12-22)
### Bug Fixes
* **YouTube:** Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) ([#4195](https://github.com/ReVanced/revanced-patches/issues/4195)) ([dda788c](https://github.com/ReVanced/revanced-patches/commit/dda788c58c789d4f91646ea8e8a8077f590ab6b3))
## [5.7.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.2...v5.7.1-dev.3) (2024-12-22)
### Bug Fixes
* **YouTube - SponsorBlock:** Show a toast and not a dialog if segment submitted successfully ([134b189](https://github.com/ReVanced/revanced-patches/commit/134b189791113dcf1a1cb7c87b8a0954f432730c))
## [5.7.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.1...v5.7.1-dev.2) (2024-12-22)
### Bug Fixes
* **YouTube - Theme:** Use dark theme color for status and navigation bar ([0240efe](https://github.com/ReVanced/revanced-patches/commit/0240efe33e5444625ca2b760c861c9046d3dc836))
## [5.7.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.0...v5.7.1-dev.1) (2024-12-22)

View File

@@ -106,7 +106,11 @@ public class GmsCoreSupport {
}
// Check if GmsCore is whitelisted from battery optimizations.
if (batteryOptimizationsEnabled(context)) {
if (isAndroidAutomotive(context)) {
// Ignore Android Automotive devices (Google built-in),
// as there is no way to disable battery optimizations.
Logger.printDebug(() -> "Device is Android Automotive");
} else if (batteryOptimizationsEnabled(context)) {
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
showBatteryOptimizationDialog(context,
@@ -147,6 +151,10 @@ public class GmsCoreSupport {
return !powerManager.isIgnoringBatteryOptimizations(GMS_CORE_PACKAGE_NAME);
}
private static boolean isAndroidAutomotive(Context context) {
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
private static String getGmsCoreDownload() {
final var vendorGroupId = getGmsCoreVendorGroupId();
//noinspection SwitchStatementWithTooFewBranches

View File

@@ -40,13 +40,15 @@ import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
public class Utils {
@SuppressLint("StaticFieldLeak")
private static Context context;
private static volatile Context context;
private static String versionName;
private static String applicationLabel;
@@ -360,7 +362,17 @@ public class Utils {
}
public static void setContext(Context appContext) {
// Must initially set context as the language settings needs it.
context = appContext;
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
if (language != AppLanguage.DEFAULT) {
// Create a new context with the desired language.
Configuration config = appContext.getResources().getConfiguration();
config.setLocale(language.getLocale());
context = appContext.createConfigurationContext(config);
}
// In some apps like TikTok, the Setting classes can load in weird orders due to cyclic class dependencies.
// Calling the regular printDebug method here can cause a Settings context null pointer exception,
// even though the context is already set before the call.
@@ -523,6 +535,11 @@ public class Utils {
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
}
public static boolean isLandscapeOrientation() {
final int orientation = context.getResources().getConfiguration().orientation;
return orientation == Configuration.ORIENTATION_LANDSCAPE;
}
/**
* Automatically logs any exceptions the runnable throws.
*
@@ -595,7 +612,7 @@ public class Utils {
|| networkType == NetworkType.OTHER;
}
@SuppressLint("MissingPermission") // permission already included in YouTube
@SuppressLint({"MissingPermission", "deprecation"}) // Permission already included in YouTube.
public static NetworkType getNetworkType() {
Context networkContext = getContext();
if (networkContext == null) {
@@ -705,8 +722,8 @@ public class Utils {
Preference preference = group.getPreference(i);
final Sort preferenceSort;
if (preference instanceof PreferenceGroup) {
sortPreferenceGroups((PreferenceGroup) preference);
if (preference instanceof PreferenceGroup subGroup) {
sortPreferenceGroups(subGroup);
preferenceSort = groupSort; // Sort value for groups is for it's content, not itself.
} else {
// Allow individual preferences to set a key sorting.
@@ -760,8 +777,8 @@ public class Utils {
return;
}
String deviceLanguage = Utils.getContext().getResources().getConfiguration().locale.getLanguage();
if (deviceLanguage.equals("en")) {
String revancedLocale = Utils.getContext().getResources().getConfiguration().locale.getLanguage();
if (revancedLocale.equals(Locale.ENGLISH.getLanguage())) {
return;
}
@@ -769,8 +786,8 @@ public class Utils {
Preference pref = group.getPreference(i);
pref.setSingleLineTitle(false);
if (pref instanceof PreferenceGroup) {
setPreferenceTitlesToMultiLineIfNeeded((PreferenceGroup) pref);
if (pref instanceof PreferenceGroup subGroup) {
setPreferenceTitlesToMultiLineIfNeeded(subGroup);
}
}
}

View File

@@ -0,0 +1,114 @@
package app.revanced.extension.shared.settings;
import java.util.Locale;
public enum AppLanguage {
/**
* The current app language.
*/
DEFAULT,
// Language codes found in locale_config.xml
// All region specific variants have been removed.
AF,
AM,
AR,
AS,
AZ,
BE,
BG,
BN,
BS,
CA,
CS,
DA,
DE,
EL,
EN,
ES,
ET,
EU,
FA,
FI,
FR,
GL,
GU,
HI,
HE, // App uses obsolete 'IW' and not the modern 'HE' ISO code.
HR,
HU,
HY,
ID,
IS,
IT,
JA,
KA,
KK,
KM,
KN,
KO,
KY,
LO,
LT,
LV,
MK,
ML,
MN,
MR,
MS,
MY,
NE,
NL,
NB,
OR,
PA,
PL,
PT,
RO,
RU,
SI,
SK,
SL,
SQ,
SR,
SV,
SW,
TA,
TE,
TH,
TL,
TR,
UK,
UR,
UZ,
VI,
ZH,
ZU;
private final String language;
AppLanguage() {
language = name().toLowerCase(Locale.US);
}
/**
* @return The 2 letter ISO 639_1 language code.
*/
public String getLanguage() {
// Changing the app language does not force the app to completely restart,
// so the default needs to be the current language and not a static field.
if (this == DEFAULT) {
return Locale.getDefault().getLanguage();
}
return language;
}
public Locale getLocale() {
if (this == DEFAULT) {
return Locale.getDefault();
}
return Locale.forLanguageTag(language);
}
}

View File

@@ -3,8 +3,10 @@ package app.revanced.extension.shared.settings;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
import app.revanced.extension.shared.spoof.ClientType;
/**
* Settings shared across multiple apps.
@@ -19,6 +21,14 @@ public class BaseSettings {
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
public static final EnumSetting<AppLanguage> REVANCED_LANGUAGE = new EnumSetting<>("revanced_language", AppLanguage.DEFAULT, true, "revanced_language_user_dialog_message");
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, parent(SPOOF_VIDEO_STREAMS));
public static final EnumSetting<AppLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability());
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
// Client type must be last spoof setting due to cyclic references.
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
}

View File

@@ -153,7 +153,6 @@ public abstract class Setting<T> {
/**
* Confirmation message to display, if the user tries to change the setting from the default value.
* Currently this works only for Boolean setting types.
*/
@Nullable
public final StringRef userDialogMessage;
@@ -244,6 +243,7 @@ public abstract class Setting<T> {
*
* This method will be deleted in the future.
*/
@SuppressWarnings("rawtypes")
public static void migrateFromOldPreferences(@NonNull SharedPrefCategory oldPrefs, @NonNull Setting setting, String settingKey) {
if (!oldPrefs.preferences.contains(settingKey)) {
return; // Nothing to do.
@@ -419,6 +419,7 @@ public abstract class Setting<T> {
boolean rebootSettingChanged = false;
int numberOfSettingsImported = 0;
//noinspection rawtypes
for (Setting setting : SETTINGS) {
String key = setting.getImportExportKey();
if (json.has(key)) {

View File

@@ -42,7 +42,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
try {
Setting<?> setting = Setting.getSettingFromPath(str);
Setting<?> setting = Setting.getSettingFromPath(Objects.requireNonNull(str));
if (setting == null) {
return;
}
@@ -52,23 +52,21 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
}
Logger.printDebug(() -> "Preference changed: " + setting.key);
// Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'.
updatePreference(pref, setting, true, settingImportInProgress);
// Update any other preference availability that may now be different.
updateUIAvailability();
if (settingImportInProgress) {
return;
}
if (!showingUserDialogMessage) {
if (setting.userDialogMessage != null && ((SwitchPreference) pref).isChecked() != (Boolean) setting.defaultValue) {
showSettingUserDialogConfirmation((SwitchPreference) pref, (BooleanSetting) setting);
if (!settingImportInProgress && !showingUserDialogMessage) {
if (setting.userDialogMessage != null && !prefIsSetToDefault(pref, setting)) {
// Do not change the setting yet, to allow preserving whatever
// list/text value was previously set if it needs to be reverted.
showSettingUserDialogConfirmation(pref, setting);
return;
} else if (setting.rebootApp) {
showRestartDialog(getContext());
}
}
// Apply 'Setting <- Preference', unless during importing when it needs to be 'Setting -> Preference'.
updatePreference(pref, setting, true, settingImportInProgress);
// Update any other preference availability that may now be different.
updateUIAvailability();
} catch (Exception ex) {
Logger.printException(() -> "OnSharedPreferenceChangeListener failure", ex);
}
@@ -92,7 +90,7 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
Utils.setPreferenceTitlesToMultiLineIfNeeded(screen);
}
private void showSettingUserDialogConfirmation(SwitchPreference switchPref, BooleanSetting setting) {
private void showSettingUserDialogConfirmation(Preference pref, Setting<?> setting) {
Utils.verifyOnMainThread();
final var context = getContext();
@@ -104,12 +102,19 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
.setTitle(confirmDialogTitle)
.setMessage(Objects.requireNonNull(setting.userDialogMessage).toString())
.setPositiveButton(android.R.string.ok, (dialog, id) -> {
// User confirmed, save to the Setting.
updatePreference(pref, setting, true, false);
// Update availability of other preferences that may be changed.
updateUIAvailability();
if (setting.rebootApp) {
showRestartDialog(context);
}
})
.setNegativeButton(android.R.string.cancel, (dialog, id) -> {
switchPref.setChecked(setting.defaultValue); // Recursive call that resets the Setting value.
// Restore whatever the setting was before the change.
updatePreference(pref, setting, true, true);
})
.setOnDismissListener(dialog -> {
showingUserDialogMessage = false;
@@ -132,6 +137,24 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
updatePreferenceScreen(getPreferenceScreen(), false, false);
}
/**
* @return If the preference is currently set to the default value of the Setting.
*/
protected boolean prefIsSetToDefault(Preference pref, Setting<?> setting) {
if (pref instanceof SwitchPreference switchPref) {
return switchPref.isChecked() == (Boolean) setting.defaultValue;
}
if (pref instanceof EditTextPreference editPreference) {
return editPreference.getText().equals(setting.defaultValue.toString());
}
if (pref instanceof ListPreference listPref) {
return listPref.getValue().equals(setting.defaultValue.toString());
}
throw new IllegalStateException("Must override method to handle "
+ "preference type: " + pref.getClass());
}
/**
* Syncs all UI Preferences to any {@link Setting} they represent.
*/
@@ -170,23 +193,20 @@ public abstract class AbstractPreferenceFragment extends PreferenceFragment {
protected void syncSettingWithPreference(@NonNull Preference pref,
@NonNull Setting<?> setting,
boolean applySettingToPreference) {
if (pref instanceof SwitchPreference) {
SwitchPreference switchPref = (SwitchPreference) pref;
if (pref instanceof SwitchPreference switchPref) {
BooleanSetting boolSetting = (BooleanSetting) setting;
if (applySettingToPreference) {
switchPref.setChecked(boolSetting.get());
} else {
BooleanSetting.privateSetValue(boolSetting, switchPref.isChecked());
}
} else if (pref instanceof EditTextPreference) {
EditTextPreference editPreference = (EditTextPreference) pref;
} else if (pref instanceof EditTextPreference editPreference) {
if (applySettingToPreference) {
editPreference.setText(setting.get().toString());
} else {
Setting.privateSetValueFromString(setting, editPreference.getText());
}
} else if (pref instanceof ListPreference) {
ListPreference listPref = (ListPreference) pref;
} else if (pref instanceof ListPreference listPref) {
if (applySettingToPreference) {
listPref.setValue(setting.get().toString());
} else {

View File

@@ -1,113 +0,0 @@
package app.revanced.extension.shared.spoof;
import java.util.Locale;
public enum AudioStreamLanguage {
/**
* YouTube default.
* Can be the original language or can be app language,
* depending on what YouTube decides to pick as the default.
*/
DEFAULT,
// Language codes found in locale_config.xml
// Region specific variants of Chinese/English/Spanish/French have been removed.
AF,
AM,
AR,
AS,
AZ,
BE,
BG,
BN,
BS,
CA,
CS,
DA,
DE,
EL,
EN,
ES,
ET,
EU,
FA,
FI,
FR,
GL,
GU,
HI,
HE, // App uses obsolete 'IW' and 'HE' is modern ISO code.
HR,
HU,
HY,
ID,
IS,
IT,
JA,
KA,
KK,
KM,
KN,
KO,
KY,
LO,
LT,
LV,
MK,
ML,
MN,
MR,
MS,
MY,
NE,
NL,
NB,
OR,
PA,
PL,
PT_BR,
PT_PT,
RO,
RU,
SI,
SK,
SL,
SQ,
SR,
SV,
SW,
TA,
TE,
TH,
TL,
TR,
UK,
UR,
UZ,
VI,
ZH,
ZU;
private final String iso639_1;
AudioStreamLanguage() {
String name = name();
final int regionSeparatorIndex = name.indexOf('_');
if (regionSeparatorIndex >= 0) {
iso639_1 = name.substring(0, regionSeparatorIndex).toLowerCase(Locale.US)
+ name.substring(regionSeparatorIndex);
} else {
iso639_1 = name().toLowerCase(Locale.US);
}
}
public String getIso639_1() {
// Changing the app language does not force the app to completely restart,
// so the default needs to be the current language and not a static field.
if (this == DEFAULT) {
return Locale.getDefault().toLanguageTag();
}
return iso639_1;
}
}

View File

@@ -4,36 +4,119 @@ import android.os.Build;
import androidx.annotation.Nullable;
import java.util.Locale;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.BaseSettings;
public enum ClientType {
// https://dumps.tadiphone.dev/dumps/oculus/eureka
ANDROID_VR_NO_AUTH( // Must be first so a default audio language can be set.
ANDROID_VR_NO_AUTH(
28,
"ANDROID_VR",
"com.google.android.apps.youtube.vr.oculus",
"Oculus",
"Quest 3",
"Android",
"12",
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
"32", // Android 12.1
"1.56.21",
false),
// Fall over to authenticated ('hl' is ignored and audio is same as language set in users Google account).
ANDROID_VR(
ANDROID_VR_NO_AUTH.id,
ANDROID_VR_NO_AUTH.clientName,
ANDROID_VR_NO_AUTH.deviceModel,
ANDROID_VR_NO_AUTH.osVersion,
ANDROID_VR_NO_AUTH.userAgent,
ANDROID_VR_NO_AUTH.androidSdkVersion,
ANDROID_VR_NO_AUTH.clientVersion,
true),
// Android 12.1
"32",
"SQ3A.220605.009.A1",
"132.0.6808.3",
"1.61.48",
false,
false,
"Android VR No auth"
),
// Chromecast with Google TV 4K.
// https://dumps.tadiphone.dev/dumps/google/kirkwood
ANDROID_UNPLUGGED(
29,
"ANDROID_UNPLUGGED",
"com.google.android.apps.youtube.unplugged",
"Google",
"Google TV Streamer",
"Android",
"14",
"com.google.android.apps.youtube.unplugged/8.49.0 (Linux; U; Android 14; GB) gzip",
"34",
"UTT3.240625.001.K5",
"132.0.6808.3",
"8.49.0",
true); // Requires login.
true,
true,
"Android TV"
),
// Cannot play livestreams and lacks HDR, but can play videos with music and labeled "for children".
// Google Pixel 9 Pro Fold
// https://dumps.tadiphone.dev/dumps/google/barbet
ANDROID_CREATOR(
14,
"ANDROID_CREATOR",
"com.google.android.apps.youtube.creator",
"Google",
"Pixel 9 Pro Fold",
"Android",
"15",
"35",
"AP3A.241005.015.A2",
"132.0.6779.0",
"23.47.101",
true,
true,
"Android Creator"
),
ANDROID_VR(
ANDROID_VR_NO_AUTH.id,
ANDROID_VR_NO_AUTH.clientName,
ANDROID_VR_NO_AUTH.packageName,
ANDROID_VR_NO_AUTH.deviceMake,
ANDROID_VR_NO_AUTH.deviceModel,
ANDROID_VR_NO_AUTH.osName,
ANDROID_VR_NO_AUTH.osVersion,
ANDROID_VR_NO_AUTH.androidSdkVersion,
ANDROID_VR_NO_AUTH.buildId,
ANDROID_VR_NO_AUTH.cronetVersion,
ANDROID_VR_NO_AUTH.clientVersion,
ANDROID_VR_NO_AUTH.requiresAuth,
true,
"Android VR"
),
IOS_UNPLUGGED(
33,
"IOS_UNPLUGGED",
"com.google.ios.youtubeunplugged",
"Apple",
forceAVC()
// 11 Pro Max (last device with iOS 13)
? "iPhone12,5"
// 15 Pro Max
: "iPhone16,2",
"iOS",
forceAVC()
// iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1.
? "13.7.17H35"
: "18.2.22C152",
null,
null,
null,
// Version number should be a valid iOS release.
// https://www.ipa4fun.com/history/152043/
forceAVC()
// Some newer versions can also force AVC,
// but 6.45 is the last version that supports iOS 13.
? "6.45"
: "8.49",
true,
true,
forceAVC()
? "iOS TV Force AVC"
: "iOS TV"
);
private static boolean forceAVC() {
return BaseSettings.SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC.get();
}
/**
* YouTube
@@ -44,20 +127,35 @@ public enum ClientType {
public final String clientName;
/**
* Device model, equivalent to {@link Build#MODEL} (System property: ro.product.model)
* App package name.
*/
public final String deviceModel;
/**
* Device OS version.
*/
public final String osVersion;
private final String packageName;
/**
* Player user-agent.
*/
public final String userAgent;
/**
* Device model, equivalent to {@link Build#MANUFACTURER} (System property: ro.product.vendor.manufacturer)
*/
public final String deviceMake;
/**
* Device model, equivalent to {@link Build#MODEL} (System property: ro.product.vendor.model)
*/
public final String deviceModel;
/**
* Device OS name.
*/
public final String osName;
/**
* Device OS version.
*/
public final String osVersion;
/**
* Android SDK version, equivalent to {@link Build.VERSION#SDK} (System property: ro.build.version.sdk)
* Field is null if not applicable.
@@ -65,31 +163,97 @@ public enum ClientType {
@Nullable
public final String androidSdkVersion;
/**
* Android build id, equivalent to {@link Build#ID}.
* Field is null if not applicable.
*/
@Nullable
private final String buildId;
/**
* Cronet release version, as found in decompiled client apk.
* Field is null if not applicable.
*/
@Nullable
private final String cronetVersion;
/**
* App version.
*/
public final String clientVersion;
/**
* If the client can access the API logged in.
* If this client requires authentication and does not work
* if logged out or in incognito mode.
*/
public final boolean canLogin;
public final boolean requiresAuth;
/**
* If the client should use authentication if available.
*/
public final boolean useAuth;
/**
* Friendly name displayed in stats for nerds.
*/
public final String friendlyName;
@SuppressWarnings("ConstantLocale")
ClientType(int id,
String clientName,
String packageName,
String deviceMake,
String deviceModel,
String osName,
String osVersion,
String userAgent,
@Nullable String androidSdkVersion,
@Nullable String buildId,
@Nullable String cronetVersion,
String clientVersion,
boolean canLogin) {
boolean requiresAuth,
boolean useAuth,
String friendlyName) {
this.id = id;
this.clientName = clientName;
this.packageName = packageName;
this.deviceMake = deviceMake;
this.deviceModel = deviceModel;
this.osName = osName;
this.osVersion = osVersion;
this.userAgent = userAgent;
this.androidSdkVersion = androidSdkVersion;
this.buildId = buildId;
this.cronetVersion = cronetVersion;
this.clientVersion = clientVersion;
this.canLogin = canLogin;
this.requiresAuth = requiresAuth;
this.useAuth = useAuth;
this.friendlyName = friendlyName;
Locale defaultLocale = Locale.getDefault();
if (androidSdkVersion == null) {
// Convert version from '18.2.22C152' into '18_2_22'
String userAgentOsVersion = osVersion
.replaceAll("(\\d+\\.\\d+\\.\\d+).*", "$1")
.replace(".", "_");
// https://github.com/mitmproxy/mitmproxy/issues/4836
this.userAgent = String.format("%s/%s (%s; U; CPU iOS %s like Mac OS X; %s)",
packageName,
clientVersion,
deviceModel,
userAgentOsVersion,
defaultLocale
);
} else {
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s; Cronet/%s)",
packageName,
clientVersion,
osVersion,
defaultLocale,
deviceModel,
Objects.requireNonNull(buildId),
Objects.requireNonNull(cronetVersion)
);
}
Logger.printDebug(() -> "userAgent: " + this.userAgent);
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.extension.shared.spoof;
import android.net.Uri;
import android.text.TextUtils;
import androidx.annotation.Nullable;
@@ -17,6 +18,9 @@ import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
public class SpoofVideoStreamsPatch {
private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_VIDEO_STREAMS.get();
private static final boolean FIX_HLS_CURRENT_TIME = SPOOF_STREAMING_DATA
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
/**
* Any unreachable ip address. Used to intentionally fail requests.
*/
@@ -30,15 +34,10 @@ public class SpoofVideoStreamsPatch {
return false; // Modified during patching.
}
public static final class NotSpoofingAndroidAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
if (SpoofVideoStreamsPatch.isPatchIncluded()) {
return !BaseSettings.SPOOF_VIDEO_STREAMS.get();
}
return true;
}
public static boolean notSpoofingToAndroid() {
return !isPatchIncluded()
|| !BaseSettings.SPOOF_VIDEO_STREAMS.get()
|| BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
}
/**
@@ -78,9 +77,9 @@ public class SpoofVideoStreamsPatch {
String path = originalUri.getPath();
if (path != null && path.contains("initplayback")) {
Logger.printDebug(() -> "Blocking 'initplayback' by returning unreachable url");
Logger.printDebug(() -> "Blocking 'initplayback' by clearing query");
return UNREACHABLE_HOST_URI_STRING;
return originalUri.buildUpon().clearQuery().build().toString();
}
} catch (Exception ex) {
Logger.printException(() -> "blockInitPlaybackRequest failure", ex);
@@ -97,6 +96,17 @@ public class SpoofVideoStreamsPatch {
return SPOOF_STREAMING_DATA;
}
/**
* Injection point.
* Only invoked when playing a livestream on an iOS client.
*/
public static boolean fixHLSCurrentTime(boolean original) {
if (!SPOOF_STREAMING_DATA) {
return original;
}
return false;
}
/**
* Injection point.
*/
@@ -105,20 +115,27 @@ public class SpoofVideoStreamsPatch {
try {
Uri uri = Uri.parse(url);
String path = uri.getPath();
if (path == null || !path.contains("player")) {
return;
}
// 'get_drm_license' has no video id and appears to happen when waiting for a paid video to start.
// 'heartbeat' has no video id and appears to be only after playback has started.
// 'refresh' has no video id and appears to happen when waiting for a livestream to start.
if (path != null && path.contains("player") && !path.contains("heartbeat")
&& !path.contains("refresh")) {
String id = uri.getQueryParameter("id");
if (id == null) {
Logger.printException(() -> "Ignoring request that has no video id." +
" Url: " + url + " headers: " + requestHeaders);
return;
}
StreamingDataRequest.fetchRequest(id, requestHeaders);
// 'ad_break' has no video id.
if (path.contains("get_drm_license") || path.contains("heartbeat")
|| path.contains("refresh") || path.contains("ad_break")) {
Logger.printDebug(() -> "Ignoring path: " + path);
return;
}
String id = uri.getQueryParameter("id");
if (id == null) {
Logger.printException(() -> "Ignoring request with no id: " + url);
return;
}
StreamingDataRequest.fetchRequest(id, requestHeaders);
} catch (Exception ex) {
Logger.printException(() -> "buildRequest failure", ex);
}
@@ -183,4 +200,38 @@ public class SpoofVideoStreamsPatch {
return postData;
}
/**
* Injection point.
*/
public static String appendSpoofedClient(String videoFormat) {
try {
if (SPOOF_STREAMING_DATA && BaseSettings.SPOOF_STREAMING_DATA_STATS_FOR_NERDS.get()
&& !TextUtils.isEmpty(videoFormat)) {
// Force LTR layout, to match the same LTR video time/length layout YouTube uses for all languages.
return "\u202D" + videoFormat + "\u2009(" // u202D = left to right override
+ StreamingDataRequest.getLastSpoofedClientName() + ")";
}
} catch (Exception ex) {
Logger.printException(() -> "appendSpoofedClient failure", ex);
}
return videoFormat;
}
public static final class AudioStreamLanguageOverrideAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.ANDROID_VR_NO_AUTH;
}
}
public static final class SpoofiOSAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
}
}
}

View File

@@ -5,6 +5,7 @@ import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Locale;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.requests.Requester;
@@ -30,27 +31,39 @@ final class PlayerRoutes {
private PlayerRoutes() {
}
static String createInnertubeBody(ClientType clientType) {
static String createInnertubeBody(ClientType clientType, String videoId) {
JSONObject innerTubeBody = new JSONObject();
try {
JSONObject context = new JSONObject();
// Can override default language only if no login is used.
// Could use preferred audio for all clients that do not login,
// but if this is a fall over client it will set the language even though
// the audio language is not selectable in the UI.
ClientType userSelectedClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
Locale streamLocale = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH
? BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getLocale()
: Locale.getDefault();
JSONObject client = new JSONObject();
client.put("hl", BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getIso639_1());
client.put("deviceMake", clientType.deviceMake);
client.put("deviceModel", clientType.deviceModel);
client.put("clientName", clientType.clientName);
client.put("clientVersion", clientType.clientVersion);
client.put("deviceModel", clientType.deviceModel);
client.put("osName", clientType.osName);
client.put("osVersion", clientType.osVersion);
if (clientType.androidSdkVersion != null) {
client.put("androidSdkVersion", clientType.androidSdkVersion);
}
client.put("hl", streamLocale.getLanguage());
client.put("gl", streamLocale.getCountry());
context.put("client", client);
innerTubeBody.put("context", context);
innerTubeBody.put("contentCheckOk", true);
innerTubeBody.put("racyCheckOk", true);
innerTubeBody.put("videoId", "%s");
innerTubeBody.put("videoId", videoId);
} catch (JSONException e) {
Logger.printException(() -> "Failed to create innerTubeBody", e);
}
@@ -66,6 +79,9 @@ final class PlayerRoutes {
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", clientType.userAgent);
// Not a typo. "Client-Name" uses the client type id.
connection.setRequestProperty("X-YouTube-Client-Name", String.valueOf(clientType.id));
connection.setRequestProperty("X-YouTube-Client-Version", clientType.clientVersion);
connection.setUseCaches(false);
connection.setDoOutput(true);

View File

@@ -22,7 +22,6 @@ import java.util.concurrent.TimeoutException;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
import app.revanced.extension.shared.spoof.ClientType;
/**
@@ -36,7 +35,22 @@ import app.revanced.extension.shared.spoof.ClientType;
*/
public class StreamingDataRequest {
private static final ClientType[] CLIENT_ORDER_TO_USE = ClientType.values();
private static final ClientType[] CLIENT_ORDER_TO_USE;
static {
ClientType[] allClientTypes = ClientType.values();
ClientType preferredClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
CLIENT_ORDER_TO_USE[0] = preferredClient;
int i = 1;
for (ClientType c : allClientTypes) {
if (c != preferredClient) {
CLIENT_ORDER_TO_USE[i++] = c;
}
}
}
private static final String AUTHORIZATION_HEADER = "Authorization";
@@ -73,6 +87,13 @@ public class StreamingDataRequest {
}
});
private static volatile ClientType lastSpoofedClientType;
public static String getLastSpoofedClientName() {
ClientType client = lastSpoofedClientType;
return client == null ? "Unknown" : client.friendlyName;
}
private final String videoId;
private final Future<ByteBuffer> future;
@@ -99,7 +120,8 @@ public class StreamingDataRequest {
}
@Nullable
private static HttpURLConnection send(ClientType clientType, String videoId,
private static HttpURLConnection send(ClientType clientType,
String videoId,
Map<String, String> playerHeaders,
boolean showErrorToasts) {
Objects.requireNonNull(clientType);
@@ -107,21 +129,24 @@ public class StreamingDataRequest {
Objects.requireNonNull(playerHeaders);
final long startTime = System.currentTimeMillis();
Logger.printDebug(() -> "Fetching video streams for: " + videoId + " using client: " + clientType);
try {
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType);
connection.setConnectTimeout(HTTP_TIMEOUT_MILLISECONDS);
connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS);
boolean authHeadersIncludes = false;
for (String key : REQUEST_HEADER_KEYS) {
String value = playerHeaders.get(key);
if (value != null) {
if (key.equals(AUTHORIZATION_HEADER)) {
if (!clientType.canLogin) {
if (!clientType.useAuth) {
Logger.printDebug(() -> "Not including request header: " + key);
continue;
}
authHeadersIncludes = true;
}
Logger.printDebug(() -> "Including request header: " + key);
@@ -129,7 +154,15 @@ public class StreamingDataRequest {
}
}
String innerTubeBody = String.format(PlayerRoutes.createInnertubeBody(clientType), videoId);
if (!authHeadersIncludes && clientType.requiresAuth) {
Logger.printDebug(() -> "Skipping client since user is not logged in: " + clientType
+ " videoId: " + videoId);
return null;
}
Logger.printDebug(() -> "Fetching video streams for: " + videoId + " using client: " + clientType);
String innerTubeBody = PlayerRoutes.createInnertubeBody(clientType, videoId);
byte[] requestBody = innerTubeBody.getBytes(StandardCharsets.UTF_8);
connection.setFixedLengthStreamingMode(requestBody.length);
connection.getOutputStream().write(requestBody);
@@ -161,23 +194,17 @@ public class StreamingDataRequest {
// Retry with different client if empty response body is received.
int i = 0;
for (ClientType clientType : CLIENT_ORDER_TO_USE) {
// Show an error if the last client type fails, or if the debug is enabled then show for all attempts.
// Show an error if the last client type fails, or if debug is enabled then show for all attempts.
final boolean showErrorToast = (++i == CLIENT_ORDER_TO_USE.length) || debugEnabled;
if (clientType == ClientType.ANDROID_VR_NO_AUTH
&& BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get() == AudioStreamLanguage.DEFAULT) {
// Only use no auth Android VR if a non default audio language is selected.
continue;
}
HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast);
if (connection != null) {
try {
// gzip encoding doesn't response with content length (-1),
// but empty response body does.
if (connection.getContentLength() == 0) {
if (BaseSettings.DEBUG.get()) {
Logger.printException(() -> "Ignoring empty client response: " + clientType);
if (BaseSettings.DEBUG.get() && BaseSettings.DEBUG_TOAST_ON_ERROR.get()) {
Utils.showToastShort("Ignoring empty spoof stream client: " + clientType);
}
} else {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
@@ -188,6 +215,7 @@ public class StreamingDataRequest {
while ((bytesRead = inputStream.read(buffer)) >= 0) {
baos.write(buffer, 0, bytesRead);
}
lastSpoofedClientType = clientType;
return ByteBuffer.wrap(baos.toByteArray());
}
@@ -198,7 +226,8 @@ public class StreamingDataRequest {
}
}
handleConnectionError("Could not fetch any client streams", null, debugEnabled);
lastSpoofedClientType = null;
handleConnectionError("Could not fetch any client streams", null, true);
return null;
}

View File

@@ -90,4 +90,12 @@ public class ThemeHelper {
public static int getForegroundColor() {
return isDarkTheme() ? getLightThemeColor() : getDarkThemeColor();
}
public static int getToolbarBackgroundColor() {
final String colorName = isDarkTheme()
? "yt_black3"
: "yt_white1";
return getColorInt(colorName);
}
}

View File

@@ -176,14 +176,13 @@ public final class AlternativeThumbnailsPatch {
// Unknown tab, treat as the home tab;
return homeOption;
}
if (selectedNavButton == NavigationButton.HOME) {
return homeOption;
}
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS || selectedNavButton == NavigationButton.NOTIFICATIONS) {
return subscriptionsOption;
}
// A library tab variant is active.
return libraryOption;
return switch (selectedNavButton) {
case SUBSCRIPTIONS, NOTIFICATIONS -> subscriptionsOption;
case LIBRARY -> libraryOption;
// Home or explore tab.
default -> homeOption;
};
}
/**

View File

@@ -0,0 +1,54 @@
package app.revanced.extension.youtube.patches;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class ChangeFormFactorPatch {
public enum FormFactor {
/**
* Unmodified, and same as un-patched.
*/
DEFAULT(null),
/**
* <pre>
* Some changes include:
* - Explore tab is present.
* - watch history is missing.
* - feed thumbnails fade in.
*/
UNKNOWN(0),
SMALL(1),
LARGE(2),
/**
* Cars with 'Google built-in'.
* Layout seems identical to {@link #UNKNOWN}
* even when using an Android Automotive device.
*/
AUTOMOTIVE(3),
WEARABLE(4);
@Nullable
final Integer formFactorType;
FormFactor(@Nullable Integer formFactorType) {
this.formFactorType = formFactorType;
}
}
@Nullable
private static final Integer FORM_FACTOR_TYPE = Settings.CHANGE_FORM_FACTOR.get().formFactorType;
/**
* Injection point.
*/
public static int getFormFactor(int original) {
return FORM_FACTOR_TYPE == null
? original
: FORM_FACTOR_TYPE;
}
}

View File

@@ -0,0 +1,15 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class DisableHdrPatch {
/**
* Injection point.
*/
public static boolean disableHDRVideo() {
return !Settings.DISABLE_HDR_VIDEO.get();
}
}

View File

@@ -9,14 +9,23 @@ import app.revanced.extension.shared.settings.BaseSettings;
@SuppressWarnings("unused")
public final class EnableDebuggingPatch {
private static final ConcurrentMap<Long, Boolean> featureFlags
= new ConcurrentHashMap<>(300, 0.75f, 1);
/**
* Only log if debugging is enabled on startup.
* This prevents enabling debugging
* while the app is running then failing to restart
* resulting in an incomplete log.
*/
private static final boolean LOG_FEATURE_FLAGS = BaseSettings.DEBUG.get();
private static final ConcurrentMap<Long, Boolean> featureFlags = LOG_FEATURE_FLAGS
? new ConcurrentHashMap<>(800, 0.5f, 1)
: null;
/**
* Injection point.
*/
public static boolean isBooleanFeatureFlagEnabled(boolean value, long flag) {
if (value && BaseSettings.DEBUG.get()) {
if (LOG_FEATURE_FLAGS && value) {
if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> "boolean feature is enabled: " + flag);
}
@@ -29,7 +38,7 @@ public final class EnableDebuggingPatch {
* Injection point.
*/
public static double isDoubleFeatureFlagEnabled(double value, long flag, double defaultValue) {
if (defaultValue != value && BaseSettings.DEBUG.get()) {
if (LOG_FEATURE_FLAGS && defaultValue != value) {
if (featureFlags.putIfAbsent(flag, true) == null) {
// Align the log outputs to make post processing easier.
Logger.printDebug(() -> " double feature is enabled: " + flag
@@ -44,7 +53,7 @@ public final class EnableDebuggingPatch {
* Injection point.
*/
public static long isLongFeatureFlagEnabled(long value, long flag, long defaultValue) {
if (defaultValue != value && BaseSettings.DEBUG.get()) {
if (LOG_FEATURE_FLAGS && defaultValue != value) {
if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> " long feature is enabled: " + flag
+ " value: " + value + (defaultValue == 0 ? "" : " default: " + defaultValue));
@@ -58,7 +67,7 @@ public final class EnableDebuggingPatch {
* Injection point.
*/
public static String isStringFeatureFlagEnabled(String value, long flag, String defaultValue) {
if (BaseSettings.DEBUG.get() && !defaultValue.equals(value)) {
if (LOG_FEATURE_FLAGS && !defaultValue.equals(value)) {
if (featureFlags.putIfAbsent(flag, true) == null) {
Logger.printDebug(() -> " string feature is enabled: " + flag
+ " value: " + value + (defaultValue.isEmpty() ? "" : " default: " + defaultValue));

View File

@@ -0,0 +1,63 @@
package app.revanced.extension.youtube.patches;
import android.widget.ImageView;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public class ExitFullscreenPatch {
public enum FullscreenMode {
DISABLED,
PORTRAIT,
LANDSCAPE,
PORTRAIT_LANDSCAPE,
}
/**
* Injection point.
*/
public static void endOfVideoReached() {
try {
FullscreenMode mode = Settings.EXIT_FULLSCREEN.get();
if (mode == FullscreenMode.DISABLED) {
return;
}
if (PlayerType.getCurrent() == PlayerType.WATCH_WHILE_FULLSCREEN) {
if (mode != FullscreenMode.PORTRAIT_LANDSCAPE) {
if (Utils.isLandscapeOrientation()) {
if (mode == FullscreenMode.PORTRAIT) {
return;
}
} else if (mode == FullscreenMode.LANDSCAPE) {
return;
}
}
// If the user cold launches the app and plays a video but does not
// tap to show the overlay controls, the fullscreen button is not
// set because the overlay controls are not attached.
// To fix this, push the perform click to the back fo the main thread,
// and by then the overlay controls will be visible since the video is now finished.
Utils.runOnMainThread(() -> {
ImageView button = PlayerControlsPatch.fullscreenButtonRef.get();
if (button == null) {
Logger.printDebug(() -> "Fullscreen button is null, cannot click");
} else {
Logger.printDebug(() -> "Clicking fullscreen button");
final boolean soundEffectsEnabled = button.isSoundEffectsEnabled();
button.setSoundEffectsEnabled(false);
button.performClick();
button.setSoundEffectsEnabled(soundEffectsEnabled);
}
});
}
} catch (Exception ex) {
Logger.printException(() -> "endOfVideoReached failure", ex);
}
}
}

View File

@@ -0,0 +1,24 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.shared.PlayerType;
@SuppressWarnings("unused")
public class FixPlaybackSpeedWhilePlayingPatch {
private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f;
public static boolean playbackSpeedChanged(float playbackSpeed) {
if (playbackSpeed == DEFAULT_YOUTUBE_PLAYBACK_SPEED &&
PlayerType.getCurrent().isMaximizedOrFullscreen()) {
Logger.printDebug(() -> "Blocking call to change playback speed to 1.0x");
return true;
}
return false;
}
}

View File

@@ -1,6 +1,8 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -8,6 +10,20 @@ public class ForceOriginalAudioPatch {
private static final String DEFAULT_AUDIO_TRACKS_SUFFIX = ".4";
/**
* If the conditions to use this patch were present when the app launched.
*/
public static boolean PATCH_AVAILABLE = SpoofVideoStreamsPatch.notSpoofingToAndroid();
public static final class ForceOriginalAudioAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
// Check conditions of launch and now. Otherwise if spoofing is changed
// without a restart the setting will show as available when it's not.
return PATCH_AVAILABLE && SpoofVideoStreamsPatch.notSpoofingToAndroid();
}
}
/**
* Injection point.
*/

View File

@@ -4,15 +4,30 @@ import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import java.lang.ref.WeakReference;
import app.revanced.extension.shared.Logger;
@SuppressWarnings("unused")
public class PlayerControlsPatch {
public static WeakReference<ImageView> fullscreenButtonRef = new WeakReference<>(null);
private static boolean fullscreenButtonVisibilityCallbacksExist() {
return false; // Modified during patching if needed.
}
/**
* Injection point.
*/
public static void setFullscreenCloseButton(ImageView imageButton) {
fullscreenButtonRef = new WeakReference<>(imageButton);
Logger.printDebug(() -> "Fullscreen button set");
if (!fullscreenButtonVisibilityCallbacksExist()) {
return;
}
// Add a global listener, since the protected method
// View#onVisibilityChanged() does not have any call backs.
imageButton.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@@ -39,7 +54,7 @@ public class PlayerControlsPatch {
}
// noinspection EmptyMethod
public static void fullscreenButtonVisibilityChanged(boolean isVisible) {
private static void fullscreenButtonVisibilityChanged(boolean isVisible) {
// Code added during patching.
}

View File

@@ -1,16 +0,0 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class TabletLayoutPatch {
private static final boolean TABLET_LAYOUT_ENABLED = Settings.TABLET_LAYOUT.get();
/**
* Injection point.
*/
public static boolean getTabletLayoutEnabled() {
return TABLET_LAYOUT_ENABLED;
}
}

View File

@@ -27,6 +27,9 @@ public final class AdsFilter extends Filter {
private final StringFilterGroup playerShoppingShelf;
private final ByteArrayFilterGroup playerShoppingShelfBuffer;
private final StringFilterGroup fullscreenOverlay;
private final ByteArrayFilterGroup endScreenStoreBannerBuffer;
private final StringFilterGroup channelProfile;
private final ByteArrayFilterGroup visitStoreButton;
@@ -112,11 +115,6 @@ public final class AdsFilter extends Filter {
"expandable_list"
);
channelProfile = new StringFilterGroup(
null,
"channel_profile.eml"
);
playerShoppingShelf = new StringFilterGroup(
null,
"horizontal_shelf.eml"
@@ -127,6 +125,21 @@ public final class AdsFilter extends Filter {
"shopping_item_card_list.eml"
);
fullscreenOverlay = new StringFilterGroup(
Settings.HIDE_END_SCREEN_STORE_BANNER,
"fullscreen_overlay.eml"
);
endScreenStoreBannerBuffer = new ByteArrayFilterGroup(
null,
"gstatic.com/shopping"
);
channelProfile = new StringFilterGroup(
null,
"channel_profile.eml"
);
visitStoreButton = new ByteArrayFilterGroup(
Settings.HIDE_VISIT_STORE_BUTTON,
"header_store_button"
@@ -154,6 +167,7 @@ public final class AdsFilter extends Filter {
viewProducts,
selfSponsor,
fullscreenAd,
fullscreenOverlay,
channelProfile,
webLinkPanel,
shoppingLinks,
@@ -172,6 +186,13 @@ public final class AdsFilter extends Filter {
return false;
}
if (matchedGroup == fullscreenOverlay) {
if (contentIndex == 0 && endScreenStoreBannerBuffer.check(protobufBufferArray).isFiltered()) {
return super.isFiltered(identifier, path, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
return false;
}
if (exceptions.matches(path))
return false;

View File

@@ -45,6 +45,11 @@ final class DescriptionComponentsFilter extends Filter {
"transcript_section"
);
final StringFilterGroup howThisWasMadeSection = new StringFilterGroup(
Settings.HIDE_HOW_THIS_WAS_MADE_SECTION,
"how_this_was_made_section"
);
macroMarkersCarousel = new StringFilterGroup(
null,
"macro_markers_carousel.eml"
@@ -64,6 +69,7 @@ final class DescriptionComponentsFilter extends Filter {
addPathCallbacks(
attributesSection,
infoCardsSection,
howThisWasMadeSection,
podcastSection,
transcriptSection,
macroMarkersCarousel

View File

@@ -528,14 +528,13 @@ final class KeywordContentFilter extends Filter {
if (selectedNavButton == null) {
return hideHome; // Unknown tab, treat the same as home.
}
if (selectedNavButton == NavigationButton.HOME) {
return hideHome;
}
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS) {
return hideSubscriptions;
}
// User is in the Library or Notifications tab.
return false;
return switch (selectedNavButton) {
case HOME, EXPLORE -> hideHome;
case SUBSCRIPTIONS -> hideSubscriptions;
// User is in the Library or notifications.
default -> false;
};
}
private void updateStats(boolean videoWasHidden, @Nullable String keyword) {

View File

@@ -80,7 +80,8 @@ public final class LayoutComponentsFilter extends Filter {
"images_post_root_slim.eml",
"text_post_root_slim.eml",
"post_base_wrapper_slim.eml",
"poll_post_root.eml"
"poll_post_root.eml",
"videos_post_root.eml"
);
final var communityGuidelines = new StringFilterGroup(
@@ -106,7 +107,8 @@ public final class LayoutComponentsFilter extends Filter {
inFeedSurvey = new StringFilterGroup(
Settings.HIDE_FEED_SURVEY,
"in_feed_survey",
"slimline_survey"
"slimline_survey",
"feed_nudge"
);
final var medicalPanel = new StringFilterGroup(
@@ -161,9 +163,9 @@ public final class LayoutComponentsFilter extends Filter {
"inline_expander"
);
final var channelBar = new StringFilterGroup(
final var compactChannelBar = new StringFilterGroup(
Settings.HIDE_CHANNEL_BAR,
"channel_bar"
"compact_channel_bar"
);
final var relatedVideos = new StringFilterGroup(
@@ -252,7 +254,7 @@ public final class LayoutComponentsFilter extends Filter {
inFeedSurvey,
notifyMe,
likeSubscribeGlow,
channelBar,
compactChannelBar,
communityPosts,
paidPromotion,
searchResultVideo,

View File

@@ -297,7 +297,7 @@ public final class ShortsFilter extends Filter {
if (matchedGroup == suggestedAction) {
// Skip searching the buffer if all suggested actions are set to hidden.
// This has a secondary effect of hiding all new un-identified actions
// under the assumption that the user wants all actions hidden.
// under the assumption that the user wants all suggestions hidden.
if (isEverySuggestedActionFilterEnabled()) {
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);
}
@@ -324,19 +324,22 @@ public final class ShortsFilter extends Filter {
}
private static boolean shouldHideShortsFeedItems() {
// Known issue if hide home is on but at least one other hide is off:
//
// Shorts suggestions will load in the background if a video is opened and
// immediately minimized before any suggestions are loaded.
// In this state the player type will show minimized, which cannot
// distinguish between Shorts suggestions loading in the player and between
// scrolling thru search/home/subscription tabs while a player is minimized.
final boolean hideHome = Settings.HIDE_SHORTS_HOME.get();
final boolean hideSubscriptions = Settings.HIDE_SHORTS_SUBSCRIPTIONS.get();
final boolean hideSearch = Settings.HIDE_SHORTS_SEARCH.get();
final boolean hideHistory = Settings.HIDE_SHORTS_HISTORY.get();
if (hideHome && hideSubscriptions && hideSearch) {
// Shorts suggestions can load in the background if a video is opened and
// then immediately minimized before any suggestions are loaded.
// In this state the player type will show minimized, which makes it not possible to
// distinguish between Shorts suggestions loading in the player and between
// scrolling thru search/home/subscription tabs while a player is minimized.
//
// To avoid this situation for users that never want to show Shorts (all hide Shorts options are enabled)
// then hide all Shorts everywhere including the Library history and Library playlists.
if (!hideHome && !hideSubscriptions && !hideSearch && !hideHistory) {
return false;
}
if (hideHome && hideSubscriptions && hideSearch && hideHistory) {
return true;
}
@@ -352,24 +355,29 @@ public final class ShortsFilter extends Filter {
}
// Avoid checking navigation button status if all other Shorts should show.
if (!hideHome && !hideSubscriptions) {
if (!hideHome && !hideSubscriptions && !hideHistory) {
return false;
}
// Check navigation absolutely last since the check may block this thread.
NavigationButton selectedNavButton = NavigationButton.getSelectedNavigationButton();
if (selectedNavButton == null) {
return hideHome; // Unknown tab, treat the same as home.
}
if (selectedNavButton == NavigationButton.HOME) {
return hideHome;
}
if (selectedNavButton == NavigationButton.SUBSCRIPTIONS) {
return hideSubscriptions;
}
// User must be in the library tab. Don't hide the history or any playlists here.
return false;
return switch (selectedNavButton) {
case HOME, EXPLORE -> hideHome;
case SUBSCRIPTIONS -> hideSubscriptions;
case LIBRARY -> hideHistory;
default -> false;
};
}
/**
* Injection point. Only used if patching older than 19.03.
* This hook may be obsolete even for old versions
* as they now use a litho layout like newer versions.
*/
public static void hideShortsShelf(final View shortsShelfView) {
if (shouldHideShortsFeedItems()) {
Utils.hideViewByLayoutParams(shortsShelfView);

View File

@@ -32,6 +32,11 @@ public class CustomPlaybackSpeedPatch {
*/
public static final float PLAYBACK_SPEED_MAXIMUM = 8;
/**
* Tap and hold speed.
*/
private static final float TAP_AND_HOLD_SPEED;
/**
* Custom playback speeds.
*/
@@ -48,12 +53,27 @@ public class CustomPlaybackSpeedPatch {
private static String[] preferenceListEntries, preferenceListEntryValues;
static {
final float holdSpeed = Settings.SPEED_TAP_AND_HOLD.get();
if (holdSpeed > 0 && holdSpeed <= PLAYBACK_SPEED_MAXIMUM) {
TAP_AND_HOLD_SPEED = holdSpeed;
} else {
showInvalidCustomSpeedToast();
Settings.SPEED_TAP_AND_HOLD.resetToDefault();
TAP_AND_HOLD_SPEED = Settings.SPEED_TAP_AND_HOLD.get();
}
loadCustomSpeeds();
}
private static void resetCustomSpeeds(@NonNull String toastMessage) {
Utils.showToastLong(toastMessage);
Settings.CUSTOM_PLAYBACK_SPEEDS.resetToDefault();
/**
* Injection point.
*/
public static float tapAndHoldSpeed() {
return TAP_AND_HOLD_SPEED;
}
private static void showInvalidCustomSpeedToast() {
Utils.showToastLong(str("revanced_custom_playback_speeds_invalid", PLAYBACK_SPEED_MAXIMUM));
}
private static void loadCustomSpeeds() {
@@ -74,17 +94,18 @@ public class CustomPlaybackSpeedPatch {
}
if (speedFloat >= PLAYBACK_SPEED_MAXIMUM) {
resetCustomSpeeds(str("revanced_custom_playback_speeds_invalid", PLAYBACK_SPEED_MAXIMUM));
showInvalidCustomSpeedToast();
Settings.CUSTOM_PLAYBACK_SPEEDS.resetToDefault();
loadCustomSpeeds();
return;
}
customPlaybackSpeeds[i] = speedFloat;
i++;
customPlaybackSpeeds[i++] = speedFloat;
}
} catch (Exception ex) {
Logger.printInfo(() -> "parse error", ex);
resetCustomSpeeds(str("revanced_custom_playback_speeds_parse_exception"));
Utils.showToastLong(str("revanced_custom_playback_speeds_parse_exception"));
Settings.CUSTOM_PLAYBACK_SPEEDS.resetToDefault();
loadCustomSpeeds();
}
}

View File

@@ -18,6 +18,8 @@ public final class SeekbarColorPatch {
private static final boolean SEEKBAR_CUSTOM_COLOR_ENABLED = Settings.SEEKBAR_CUSTOM_COLOR.get();
private static final boolean HIDE_SEEKBAR_THUMBNAIL_ENABLED = Settings.HIDE_SEEKBAR_THUMBNAIL.get();
/**
* Default color of the litho seekbar.
* Differs slightly from the default custom seekbar color setting.
@@ -25,14 +27,19 @@ public final class SeekbarColorPatch {
private static final int ORIGINAL_SEEKBAR_COLOR = 0xFFFF0000;
/**
* Default colors of the gradient seekbar.
* Feed default colors of the gradient seekbar.
*/
private static final int[] ORIGINAL_SEEKBAR_GRADIENT_COLORS = { 0xFFFF0033, 0xFFFF2791 };
private static final int[] FEED_ORIGINAL_SEEKBAR_GRADIENT_COLORS = { 0xFFFF0033, 0xFFFF2791 };
/**
* Default positions of the gradient seekbar.
* Feed default positions of the gradient seekbar.
*/
private static final float[] ORIGINAL_SEEKBAR_GRADIENT_POSITIONS = { 0.8f, 1.0f };
private static final float[] FEED_ORIGINAL_SEEKBAR_GRADIENT_POSITIONS = { 0.8f, 1.0f };
/**
* Empty seekbar gradient, if hide seekbar in feed is enabled.
*/
private static final int[] HIDDEN_SEEKBAR_GRADIENT_COLORS = { 0x0, 0x0 };
/**
* Default YouTube seekbar color brightness.
@@ -41,16 +48,21 @@ public final class SeekbarColorPatch {
/**
* If {@link Settings#SEEKBAR_CUSTOM_COLOR} is enabled,
* this is the color value of {@link Settings#SEEKBAR_CUSTOM_COLOR_VALUE}.
* this is the color value of {@link Settings#SEEKBAR_CUSTOM_COLOR_PRIMARY}.
* Otherwise this is {@link #ORIGINAL_SEEKBAR_COLOR}.
*/
private static int seekbarColor = ORIGINAL_SEEKBAR_COLOR;
private static int customSeekbarColor = ORIGINAL_SEEKBAR_COLOR;
/**
* Custom seekbar hue, saturation, and brightness values.
*/
private static final float[] customSeekbarColorHSV = new float[3];
/**
* Custom seekbar color, used for linear gradient replacements.
*/
private static final int[] customSeekbarColorGradient = new int[2];
static {
float[] hsv = new float[3];
Color.colorToHSV(ORIGINAL_SEEKBAR_COLOR, hsv);
@@ -63,26 +75,22 @@ public final class SeekbarColorPatch {
private static void loadCustomSeekbarColor() {
try {
seekbarColor = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_VALUE.get());
Color.colorToHSV(seekbarColor, customSeekbarColorHSV);
customSeekbarColor = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_PRIMARY.get());
Color.colorToHSV(customSeekbarColor, customSeekbarColorHSV);
customSeekbarColorGradient[0] = customSeekbarColor;
customSeekbarColorGradient[1] = Color.parseColor(Settings.SEEKBAR_CUSTOM_COLOR_ACCENT.get());
} catch (Exception ex) {
Utils.showToastShort(str("revanced_seekbar_custom_color_invalid"));
Settings.SEEKBAR_CUSTOM_COLOR_VALUE.resetToDefault();
Settings.SEEKBAR_CUSTOM_COLOR_PRIMARY.resetToDefault();
Settings.SEEKBAR_CUSTOM_COLOR_ACCENT.resetToDefault();
loadCustomSeekbarColor();
}
}
public static int getSeekbarColor() {
return seekbarColor;
}
/**
* Injection point
*/
public static boolean playerSeekbarGradientEnabled(boolean original) {
if (SEEKBAR_CUSTOM_COLOR_ENABLED) return false;
return original;
return customSeekbarColor;
}
/**
@@ -126,7 +134,7 @@ public final class SeekbarColorPatch {
// Even if the seekbar color xml value is changed to a completely different color (such as green),
// a color filter still cannot be selectively applied when the drawable has more than 1 color.
try {
String seekbarStyle = get9BitStyleIdentifier(seekbarColor);
String seekbarStyle = get9BitStyleIdentifier(customSeekbarColor);
Logger.printDebug(() -> "Using splash seekbar style: " + seekbarStyle);
final int styleIdentifierDefault = Utils.getResourceIdentifier(
@@ -146,6 +154,13 @@ public final class SeekbarColorPatch {
}
}
/**
* Injection point.
*/
public static boolean showWatchHistoryProgressDrawable(boolean original) {
return !HIDE_SEEKBAR_THUMBNAIL_ENABLED && original;
}
/**
* Injection point.
*
@@ -156,35 +171,61 @@ public final class SeekbarColorPatch {
*/
public static int getLithoColor(int colorValue) {
if (colorValue == ORIGINAL_SEEKBAR_COLOR) {
if (Settings.HIDE_SEEKBAR_THUMBNAIL.get()) {
return 0x00000000;
if (HIDE_SEEKBAR_THUMBNAIL_ENABLED) {
return 0x0;
}
return getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR);
return customSeekbarColor;
}
return colorValue;
}
private static String colorArrayToHex(int[] colors) {
final int length = colors.length;
StringBuilder builder = new StringBuilder(length * 12);
builder.append("[");
int i = 0;
for (int color : colors) {
builder.append(String.format("#%X", color));
if (++i < length) {
builder.append(", ");
}
}
builder.append("]");
return builder.toString();
}
/**
* Injection point.
*/
public static void setLinearGradient(int[] colors, float[] positions) {
final boolean hideSeekbar = Settings.HIDE_SEEKBAR_THUMBNAIL.get();
public static int[] getPlayerLinearGradient(int[] original) {
return SEEKBAR_CUSTOM_COLOR_ENABLED
? customSeekbarColorGradient
: original;
}
if (SEEKBAR_CUSTOM_COLOR_ENABLED || hideSeekbar) {
/**
* Injection point.
*/
public static int[] getLithoLinearGradient(int[] colors, float[] positions) {
if (SEEKBAR_CUSTOM_COLOR_ENABLED || HIDE_SEEKBAR_THUMBNAIL_ENABLED) {
// Most litho usage of linear gradients is hooked here,
// so must only change if the values are those for the seekbar.
if (Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_COLORS, colors)
&& Arrays.equals(ORIGINAL_SEEKBAR_GRADIENT_POSITIONS, positions)) {
Arrays.fill(colors, hideSeekbar
? 0x00000000
: seekbarColor);
return;
if ((Arrays.equals(FEED_ORIGINAL_SEEKBAR_GRADIENT_COLORS, colors)
&& Arrays.equals(FEED_ORIGINAL_SEEKBAR_GRADIENT_POSITIONS, positions))) {
return HIDE_SEEKBAR_THUMBNAIL_ENABLED
? HIDDEN_SEEKBAR_GRADIENT_COLORS
: customSeekbarColorGradient;
}
Logger.printDebug(() -> "Ignoring gradient colors: " + Arrays.toString(colors)
Logger.printDebug(() -> "Ignoring gradient colors: " + colorArrayToHex(colors)
+ " positions: " + Arrays.toString(positions));
}
return colors;
}
/**
@@ -198,7 +239,7 @@ public final class SeekbarColorPatch {
}
return colorValue == ORIGINAL_SEEKBAR_COLOR
? getSeekbarColorValue(ORIGINAL_SEEKBAR_COLOR)
? customSeekbarColor
: colorValue;
}
@@ -208,11 +249,9 @@ public final class SeekbarColorPatch {
* Overrides color used for the video player seekbar.
*/
public static int getVideoPlayerSeekbarColor(int originalColor) {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED) {
return originalColor;
}
return getSeekbarColorValue(originalColor);
return SEEKBAR_CUSTOM_COLOR_ENABLED
? getSeekbarColorValue(originalColor)
: originalColor;
}
/**
@@ -221,10 +260,6 @@ public final class SeekbarColorPatch {
*/
private static int getSeekbarColorValue(int originalColor) {
try {
if (!SEEKBAR_CUSTOM_COLOR_ENABLED || originalColor == seekbarColor) {
return originalColor; // nothing to do
}
final int alphaDifference = Color.alpha(originalColor) - Color.alpha(ORIGINAL_SEEKBAR_COLOR);
// The seekbar uses the same color but different brightness for different situations.
@@ -237,7 +272,7 @@ public final class SeekbarColorPatch {
hsv[1] = customSeekbarColorHSV[1];
hsv[2] = clamp(customSeekbarColorHSV[2] + brightnessDifference, 0, 1);
final int replacementAlpha = clamp(Color.alpha(seekbarColor) + alphaDifference, 0, 255);
final int replacementAlpha = clamp(Color.alpha(customSeekbarColor) + alphaDifference, 0, 255);
final int replacementColor = Color.HSVToColor(replacementAlpha, hsv);
Logger.printDebug(() -> String.format("Original color: #%08X replacement color: #%08X",
originalColor, replacementColor));

View File

@@ -1,21 +1,30 @@
package app.revanced.extension.youtube.settings;
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.preference.PreferenceFragment;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.TextView;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
import android.widget.Toolbar;
import androidx.annotation.RequiresApi;
import java.util.Objects;
import static app.revanced.extension.shared.Utils.getChildView;
import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.VersionCheckPatch;
import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment;
import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment;
import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment;
/**
* Hooks LicenseActivity.
@@ -25,17 +34,57 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
@SuppressWarnings("unused")
public class LicenseActivityHook {
private static ViewGroup.LayoutParams toolbarLayoutParams;
public static void setToolbarLayoutParams(Toolbar toolbar) {
if (toolbarLayoutParams != null) {
toolbar.setLayoutParams(toolbarLayoutParams);
}
}
/**
* Injection point.
* Overrides the ReVanced settings language.
*/
public static Context getAttachBaseContext(Context original) {
AppLanguage language = BaseSettings.REVANCED_LANGUAGE.get();
if (language == AppLanguage.DEFAULT) {
return original;
}
return Utils.getContext();
}
/**
* Injection point.
*/
public static boolean useCairoSettingsFragment(boolean original) {
// Early targets have layout issues and it's better to always force off.
if (!VersionCheckPatch.IS_19_34_OR_GREATER) {
return false;
}
if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) {
return false;
}
// On the first launch of a clean install, forcing the cairo menu can give a
// half broken appearance because all the preference icons may not be available yet.
// 19.34+ cairo settings are always on, so it doesn't need to be forced anyway.
// Cairo setting will show on the next launch of the app.
return original;
}
/**
* Injection point.
* <p>
* Hooks LicenseActivity#onCreate in order to inject our own fragment.
*/
@RequiresApi(api = Build.VERSION_CODES.N)
public static void initialize(Activity licenseActivity) {
try {
ThemeHelper.setActivityTheme(licenseActivity);
licenseActivity.setContentView(
getResourceIdentifier("revanced_settings_with_toolbar", "layout"));
setBackButton(licenseActivity);
licenseActivity.setContentView(getResourceIdentifier(
"revanced_settings_with_toolbar", "layout"));
PreferenceFragment fragment;
String toolbarTitleResourceName;
@@ -58,7 +107,7 @@ public class LicenseActivityHook {
return;
}
setToolbarTitle(licenseActivity, toolbarTitleResourceName);
createToolbar(licenseActivity, toolbarTitleResourceName);
//noinspection deprecation
licenseActivity.getFragmentManager()
@@ -70,28 +119,36 @@ public class LicenseActivityHook {
}
}
private static void setToolbarTitle(Activity activity, String toolbarTitleResourceName) {
ViewGroup toolbar = activity.findViewById(getToolbarResourceId());
TextView toolbarTextView = Objects.requireNonNull(getChildView(toolbar, false,
view -> view instanceof TextView));
toolbarTextView.setText(getResourceIdentifier(toolbarTitleResourceName, "string"));
}
@RequiresApi(api = Build.VERSION_CODES.N)
@SuppressLint("UseCompatLoadingForDrawables")
private static void setBackButton(Activity activity) {
ViewGroup toolbar = activity.findViewById(getToolbarResourceId());
ImageButton imageButton = Objects.requireNonNull(getChildView(toolbar, false,
view -> view instanceof ImageButton));
imageButton.setImageDrawable(ReVancedPreferenceFragment.getBackButtonDrawable());
imageButton.setOnClickListener(view -> activity.onBackPressed());
}
private static void createToolbar(Activity activity, String toolbarTitleResourceName) {
// Replace dummy placeholder toolbar.
// This is required to fix submenu title alignment issue with Android ASOP 15+
ViewGroup toolBarParent = activity.findViewById(
getResourceIdentifier("revanced_toolbar_parent", "id"));
ViewGroup dummyToolbar = toolBarParent.findViewById(getResourceIdentifier(
"revanced_toolbar", "id"));
toolbarLayoutParams = dummyToolbar.getLayoutParams();
toolBarParent.removeView(dummyToolbar);
private static int getToolbarResourceId() {
final int toolbarResourceId = getResourceIdentifier("revanced_toolbar", "id");
if (toolbarResourceId == 0) {
throw new IllegalStateException("Could not find back button resource");
Toolbar toolbar = new Toolbar(toolBarParent.getContext());
toolbar.setBackgroundColor(ThemeHelper.getToolbarBackgroundColor());
toolbar.setNavigationIcon(ReVancedPreferenceFragment.getBackButtonDrawable());
toolbar.setNavigationOnClickListener(view -> activity.onBackPressed());
toolbar.setTitle(getResourceIdentifier(toolbarTitleResourceName, "string"));
final int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16,
Utils.getContext().getResources().getDisplayMetrics());
toolbar.setTitleMarginStart(margin);
toolbar.setTitleMarginEnd(margin);
TextView toolbarTextView = Utils.getChildView(toolbar, false,
view -> view instanceof TextView);
if (toolbarTextView != null) {
toolbarTextView.setTextColor(ThemeHelper.getForegroundColor());
}
return toolbarResourceId;
setToolbarLayoutParams(toolbar);
toolBarParent.addView(toolbar, 0);
}
}

View File

@@ -7,8 +7,10 @@ import static app.revanced.extension.shared.settings.Setting.migrateFromOldPrefe
import static app.revanced.extension.shared.settings.Setting.migrateOldSettingToNew;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.settings.Setting.parentsAny;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.NotSpoofingAndroidAvailability;
import static app.revanced.extension.youtube.patches.ChangeFormFactorPatch.FormFactor;
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideExpandCloseAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
@@ -25,6 +27,8 @@ import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehavi
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.SKIP_AUTOMATICALLY_ONCE;
import android.graphics.Color;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.BooleanSetting;
@@ -43,21 +47,24 @@ import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
public class Settings extends BaseSettings {
// Video
public static final BooleanSetting DISABLE_HDR_VIDEO = new BooleanSetting("revanced_disable_hdr_video", FALSE);
public static final BooleanSetting RESTORE_OLD_VIDEO_QUALITY_MENU = new BooleanSetting("revanced_restore_old_video_quality_menu", TRUE);
public static final BooleanSetting REMEMBER_VIDEO_QUALITY_LAST_SELECTED = new BooleanSetting("revanced_remember_video_quality_last_selected", FALSE);
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_WIFI = new IntegerSetting("revanced_video_quality_default_wifi", -2);
public static final IntegerSetting VIDEO_QUALITY_DEFAULT_MOBILE = new IntegerSetting("revanced_video_quality_default_mobile", -2);
// Speed
public static final FloatSetting SPEED_TAP_AND_HOLD = new FloatSetting("revanced_speed_tap_and_hold", 2.0f, true);
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
public static final BooleanSetting CUSTOM_SPEED_MENU = new BooleanSetting("revanced_custom_speed_menu", TRUE);
public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", -2.0f);
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
// Audio
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE, new NotSpoofingAndroidAvailability());
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE, new ForceOriginalAudioAvailability());
// Ads
public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE);
public static final BooleanSetting HIDE_END_SCREEN_STORE_BANNER = new BooleanSetting("revanced_hide_end_screen_store_banner", FALSE);
public static final BooleanSetting HIDE_FULLSCREEN_ADS = new BooleanSetting("revanced_hide_fullscreen_ads", TRUE);
public static final BooleanSetting HIDE_GENERAL_ADS = new BooleanSetting("revanced_hide_general_ads", TRUE);
public static final BooleanSetting HIDE_GET_PREMIUM = new BooleanSetting("revanced_hide_get_premium", TRUE);
@@ -120,6 +127,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DISABLE_LIKE_SUBSCRIBE_GLOW = new BooleanSetting("revanced_disable_like_subscribe_glow", FALSE);
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("revanced_disable_rolling_number_animations", FALSE);
public static final BooleanSetting DISABLE_SUGGESTED_VIDEO_END_SCREEN = new BooleanSetting("revanced_disable_suggested_video_end_screen", FALSE, true);
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("revanced_exit_fullscreen", FullscreenMode.DISABLED);
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("revanced_hide_autoplay_button", TRUE, true);
public static final BooleanSetting HIDE_CAPTIONS_BUTTON = new BooleanSetting("revanced_hide_captions_button", FALSE);
public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_hide_cast_button", TRUE, true);
@@ -139,10 +147,10 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SUBSCRIBERS_COMMUNITY_GUIDELINES = new BooleanSetting("revanced_hide_subscribers_community_guidelines", TRUE);
public static final BooleanSetting HIDE_TIMED_REACTIONS = new BooleanSetting("revanced_hide_timed_reactions", TRUE);
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", TRUE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
public static final BooleanSetting OPEN_VIDEOS_FULLSCREEN_PORTRAIT = new BooleanSetting("revanced_open_videos_fullscreen_portrait", FALSE);
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
// Miniplayer
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.DEFAULT, true);
private static final Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4);
@@ -171,6 +179,7 @@ public class Settings extends BaseSettings {
// Description
public static final BooleanSetting HIDE_ATTRIBUTES_SECTION = new BooleanSetting("revanced_hide_attributes_section", FALSE);
public static final BooleanSetting HIDE_CHAPTERS_SECTION = new BooleanSetting("revanced_hide_chapters_section", TRUE);
public static final BooleanSetting HIDE_HOW_THIS_WAS_MADE_SECTION = new BooleanSetting("revanced_hide_how_this_was_made_section", FALSE);
public static final BooleanSetting HIDE_INFO_CARDS_SECTION = new BooleanSetting("revanced_hide_info_cards_section", TRUE);
public static final BooleanSetting HIDE_KEY_CONCEPTS_SECTION = new BooleanSetting("revanced_hide_key_concepts_section", FALSE);
public static final BooleanSetting HIDE_PODCAST_SECTION = new BooleanSetting("revanced_hide_podcast_section", TRUE);
@@ -200,15 +209,16 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_PLAYER_FLYOUT_WATCH_IN_VR = new BooleanSetting("revanced_hide_player_flyout_watch_in_vr", TRUE);
// General layout
public static final BooleanSetting RESTORE_OLD_SETTINGS_MENUS = new BooleanSetting("revanced_restore_old_settings_menus", FALSE, true);
public static final EnumSetting<FormFactor> CHANGE_FORM_FACTOR = new EnumSetting<>("revanced_change_form_factor", FormFactor.DEFAULT, true, "revanced_change_form_factor_user_dialog_message");
public static final BooleanSetting BYPASS_IMAGE_REGION_RESTRICTIONS = new BooleanSetting("revanced_bypass_image_region_restrictions", FALSE, true);
public static final BooleanSetting GRADIENT_LOADING_SCREEN = new BooleanSetting("revanced_gradient_loading_screen", FALSE, true);
public static final BooleanSetting REMOVE_VIEWER_DISCRETION_DIALOG = new BooleanSetting("revanced_remove_viewer_discretion_dialog", FALSE,
"revanced_remove_viewer_discretion_dialog_user_dialog_message");
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message");
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", IS_19_17_OR_GREATER ? "19.35.36" : "17.33.42", true, parent(SPOOF_APP_VERSION));
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", IS_19_17_OR_GREATER ? "19.26.42" : "17.33.42", true, parent(SPOOF_APP_VERSION));
// Custom filter
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
public static final StringSetting CUSTOM_FILTER_STRINGS = new StringSetting("revanced_custom_filter_strings", "", true, parent(CUSTOM_FILTER));
@@ -233,6 +243,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SHORTS_FULL_VIDEO_LINK_LABEL = new BooleanSetting("revanced_hide_shorts_full_video_link_label", FALSE);
public static final BooleanSetting HIDE_SHORTS_GREEN_SCREEN_BUTTON = new BooleanSetting("revanced_hide_shorts_green_screen_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_HASHTAG_BUTTON = new BooleanSetting("revanced_hide_shorts_hashtag_button", TRUE);
public static final BooleanSetting HIDE_SHORTS_HISTORY = new BooleanSetting("revanced_hide_shorts_history", FALSE);
public static final BooleanSetting HIDE_SHORTS_HOME = new BooleanSetting("revanced_hide_shorts_home", FALSE);
public static final BooleanSetting HIDE_SHORTS_INFO_PANEL = new BooleanSetting("revanced_hide_shorts_info_panel", TRUE);
public static final BooleanSetting HIDE_SHORTS_JOIN_BUTTON = new BooleanSetting("revanced_hide_shorts_join_button", TRUE);
@@ -261,18 +272,19 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SHORTS_AUTOPLAY_BACKGROUND = new BooleanSetting("revanced_shorts_autoplay_background", TRUE);
// Seekbar
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", TRUE);
public static final BooleanSetting DISABLE_PRECISE_SEEKING_GESTURE = new BooleanSetting("revanced_disable_precise_seeking_gesture", FALSE);
public static final BooleanSetting HIDE_SEEKBAR = new BooleanSetting("revanced_hide_seekbar", FALSE, true);
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE);
public static final BooleanSetting HIDE_SEEKBAR_THUMBNAIL = new BooleanSetting("revanced_hide_seekbar_thumbnail", FALSE, true);
public static final BooleanSetting HIDE_TIMESTAMP = new BooleanSetting("revanced_hide_timestamp", FALSE);
public static final BooleanSetting RESTORE_OLD_SEEKBAR_THUMBNAILS = new BooleanSetting("revanced_restore_old_seekbar_thumbnails", TRUE);
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", TRUE);
public static final BooleanSetting SEEKBAR_TAPPING = new BooleanSetting("revanced_seekbar_tapping", FALSE);
public static final BooleanSetting SEEKBAR_THUMBNAILS_HIGH_QUALITY = new BooleanSetting("revanced_seekbar_thumbnails_high_quality", FALSE, true,
"revanced_seekbar_thumbnails_high_quality_dialog_message", new SeekbarThumbnailsHighQualityAvailability());
public static final BooleanSetting SLIDE_TO_SEEK = new BooleanSetting("revanced_slide_to_seek", FALSE, true);
public static final StringSetting SEEKBAR_CUSTOM_COLOR_VALUE = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
public static final BooleanSetting SEEKBAR_CUSTOM_COLOR = new BooleanSetting("revanced_seekbar_custom_color", FALSE, true);
private static final StringSetting DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_value", "#FF0033");
public static final StringSetting SEEKBAR_CUSTOM_COLOR_PRIMARY = new StringSetting("revanced_seekbar_custom_color_primary", "#FF0033", true, parent(SEEKBAR_CUSTOM_COLOR));
public static final StringSetting SEEKBAR_CUSTOM_COLOR_ACCENT = new StringSetting("revanced_seekbar_custom_color_accent", "#FF2791", true, parent(SEEKBAR_CUSTOM_COLOR));
// Misc
public static final BooleanSetting ANNOUNCEMENTS = new BooleanSetting("revanced_announcements", TRUE);
@@ -293,8 +305,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DEBUG_PROTOBUFFER = new BooleanSetting("revanced_debug_protobuffer", FALSE, parent(BaseSettings.DEBUG));
// Swipe controls
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", TRUE);
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", TRUE);
public static final BooleanSetting SWIPE_CHANGE_VIDEO = new BooleanSetting("revanced_swipe_change_video", FALSE, true);
public static final BooleanSetting SWIPE_BRIGHTNESS = new BooleanSetting("revanced_swipe_brightness", FALSE);
public static final BooleanSetting SWIPE_VOLUME = new BooleanSetting("revanced_swipe_volume", FALSE);
public static final BooleanSetting SWIPE_PRESS_TO_ENGAGE = new BooleanSetting("revanced_swipe_press_to_engage", FALSE, true,
parentsAny(SWIPE_BRIGHTNESS, SWIPE_VOLUME));
public static final BooleanSetting SWIPE_HAPTIC_FEEDBACK = new BooleanSetting("revanced_swipe_haptic_feedback", TRUE, true,
@@ -399,6 +412,30 @@ public class Settings extends BaseSettings {
MINIPLAYER_TYPE.save(MINIMAL);
}
// Migrate old single color seekbar with a slightly brighter accent color based on the primary.
// Eventually delete this logic.
if (!DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY.isSetToDefault()) {
try {
String oldPrimaryColorString = DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY.get();
final int oldPrimaryColor = Color.parseColor(oldPrimaryColorString);
SEEKBAR_CUSTOM_COLOR_PRIMARY.save(oldPrimaryColorString);
final float brightnessScale = 1.3f;
final int accentColor = Color.argb(
0, // Save without alpha channel.
Math.min(255, (int) (brightnessScale * Color.red(oldPrimaryColor))),
Math.min(255, (int) (brightnessScale * Color.green(oldPrimaryColor))),
Math.min(255, (int) (brightnessScale * Color.blue(oldPrimaryColor)))
);
SEEKBAR_CUSTOM_COLOR_ACCENT.save(String.format("#%06X", accentColor));
} catch (Exception ex) {
Logger.printException(() -> "Could not parse old seekbar color", ex);
}
DEPRECATED_SEEKBAR_CUSTOM_COLOR_PRIMARY.resetToDefault();
}
// endregion
// region SB import/export callbacks

View File

@@ -0,0 +1,36 @@
package app.revanced.extension.youtube.settings.preference;
import static app.revanced.extension.shared.StringRef.str;
import android.content.Context;
import android.preference.SwitchPreference;
import android.util.AttributeSet;
import app.revanced.extension.youtube.patches.ForceOriginalAudioPatch;
@SuppressWarnings({"deprecation", "unused"})
public class ForceOriginalAudioSwitchPreference extends SwitchPreference {
{
if (!ForceOriginalAudioPatch.PATCH_AVAILABLE) {
// Show why force audio is not available.
String summary = str("revanced_force_original_audio_not_available");
setSummary(summary);
setSummaryOn(summary);
setSummaryOff(summary);
}
}
public ForceOriginalAudioSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public ForceOriginalAudioSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ForceOriginalAudioSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ForceOriginalAudioSwitchPreference(Context context) {
super(context);
}
}

View File

@@ -25,9 +25,12 @@ import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.EnumSetting;
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
import app.revanced.extension.youtube.ThemeHelper;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.LicenseActivityHook;
import app.revanced.extension.youtube.settings.Settings;
/**
@@ -109,15 +112,20 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
}
preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key);
if (preference instanceof ListPreference languagePreference) {
sortListPreferenceByValues(languagePreference, 1);
}
sortPreferenceListMenu(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE);
sortPreferenceListMenu(BaseSettings.REVANCED_LANGUAGE);
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);
}
}
private void sortPreferenceListMenu(EnumSetting<?> setting) {
Preference preference = findPreference(setting.key);
if (preference instanceof ListPreference languagePreference) {
sortListPreferenceByValues(languagePreference, 1);
}
}
private void setPreferenceScreenToolbar(PreferenceScreen parentScreen) {
for (int i = 0, preferenceCount = parentScreen.getPreferenceCount(); i < preferenceCount; i++) {
Preference childPreference = parentScreen.getPreference(i);
@@ -133,9 +141,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
.getParent();
// Fix required for Android 15 and YT 19.45+
// FIXME:
// On Android 15 the text layout is not aligned the same as the parent
// screen and it looks a little off. Otherwise this works.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
rootView.setOnApplyWindowInsetsListener((v, insets) -> {
Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars());
@@ -162,6 +167,8 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
toolbarTextView.setTextColor(ThemeHelper.getForegroundColor());
}
LicenseActivityHook.setToolbarLayoutParams(toolbar);
rootView.addView(toolbar, 0);
return false;
}

View File

@@ -0,0 +1,95 @@
package app.revanced.extension.youtube.settings.preference;
import static app.revanced.extension.shared.StringRef.str;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.spoof.ClientType;
@SuppressWarnings({"deprecation", "unused"})
public class SpoofStreamingDataSideEffectsPreference extends Preference {
@Nullable
private ClientType currentClientType;
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
// Because this listener may run before the ReVanced settings fragment updates Settings,
// this could show the prior config and not the current.
//
// Push this call to the end of the main run queue,
// so all other listeners are done and Settings is up to date.
Utils.runOnMainThread(this::updateUI);
};
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SpoofStreamingDataSideEffectsPreference(Context context) {
super(context);
}
private void addChangeListener() {
Setting.preferences.preferences.registerOnSharedPreferenceChangeListener(listener);
}
private void removeChangeListener() {
Setting.preferences.preferences.unregisterOnSharedPreferenceChangeListener(listener);
}
@Override
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
super.onAttachedToHierarchy(preferenceManager);
updateUI();
addChangeListener();
}
@Override
protected void onPrepareForRemoval() {
super.onPrepareForRemoval();
removeChangeListener();
}
private void updateUI() {
ClientType clientType = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
if (currentClientType == clientType) {
return;
}
Logger.printDebug(() -> "Updating spoof stream side effects preference");
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
String key = "revanced_spoof_video_streams_about_" +
(clientType == ClientType.IOS_UNPLUGGED
? "ios_tv"
: "android");
String title = str(key + "_title");
String summary = str(key + "_summary");
// Android VR supports AV1 but all other clients do not.
if (clientType != ClientType.ANDROID_VR && clientType != ClientType.ANDROID_VR_NO_AUTH) {
summary += '\n' + str("revanced_spoof_video_streams_about_no_av1");
}
setTitle(title);
setSummary(summary);
}
}

View File

@@ -3,12 +3,15 @@ package app.revanced.extension.youtube.shared;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton.CREATE;
import android.app.Activity;
import android.os.Build;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -54,20 +57,21 @@ public final class NavigationBar {
* How long to wait for the set nav button latch to be released. Maximum wait time must
* be as small as possible while still allowing enough time for the nav bar to update.
*
* YT calls it's back button handlers out of order,
* and litho starts filtering before the navigation bar is updated.
* YT calls it's back button handlers out of order, and litho starts filtering before the
* navigation bar is updated. Fixing this situation and not needlessly waiting requires
* somehow detecting if a back button key/gesture will not change the active tab.
*
* Fixing this situation and not needlessly waiting requires somehow
* detecting if a back button key-press will cause a tab change.
* On average the time between pressing the back button and the first litho event is
* about 10-20ms. Waiting up to 75-150ms should be enough time to handle normal use cases
* and not be noticeable, since YT typically takes 100-200ms (or more) to update the view.
*
* Typically after pressing the back button, the time between the first litho event and
* when the nav button is updated is about 10-20ms. Using 50-100ms here should be enough time
* and not noticeable, since YT typically takes 100-200ms (or more) to update the view anyways.
* This delay is only noticeable when the device back button/gesture will not
* change the current navigation tab, such as backing out of the watch history.
*
* This issue can also be avoided on a patch by patch basis, by avoiding calls to
* {@link NavigationButton#getSelectedNavigationButton()} unless absolutely necessary.
*/
private static final long LATCH_AWAIT_TIMEOUT_MILLISECONDS = 75;
private static final long LATCH_AWAIT_TIMEOUT_MILLISECONDS = 120;
/**
* Used as a workaround to fix the issue of YT calling back button handlers out of order.
@@ -113,7 +117,8 @@ public final class NavigationBar {
// The latch is released from the main thread, and waiting from the main thread will always timeout.
// This situation has only been observed when navigating out of a submenu and not changing tabs.
// and for that use case the nav bar does not change so it's safe to return here.
Logger.printDebug(() -> "Cannot block main thread waiting for nav button. Using last known navbar button status.");
Logger.printDebug(() -> "Cannot block main thread waiting for nav button. " +
"Using last known navbar button status.");
return;
}
@@ -131,7 +136,9 @@ public final class NavigationBar {
Logger.printDebug(() -> "Latch wait timed out");
} catch (InterruptedException ex) {
Logger.printException(() -> "Latch wait interrupted failure", ex); // Will never happen.
// Calling YouTube thread was interrupted.
Logger.printException(() -> "Latch wait interrupted", ex);
Thread.currentThread().interrupt(); // Restore interrupt status flag.
}
}
@@ -238,6 +245,30 @@ public final class NavigationBar {
// Code is added during patching.
}
/**
* Use the bundled non cairo filled icon instead of a custom icon.
* Use the old non cairo filled icon, which is almost identical to
* the what would be the filled cairo icon.
*/
private static final int fillBellCairoBlack = Utils.getResourceIdentifier(
"yt_fill_bell_black_24", "drawable");
/**
* Injection point.
* Fixes missing drawable.
*/
@RequiresApi(api = Build.VERSION_CODES.N)
@SuppressWarnings({"unchecked", "rawtypes"})
public static void setCairoNotificationFilledIcon(EnumMap enumMap, Enum tabActivityCairo) {
if (fillBellCairoBlack != 0) {
// Show a popup informing this fix is no longer needed to those who might care.
if (BaseSettings.DEBUG.get() && enumMap.containsKey(tabActivityCairo)) {
Logger.printException(() -> "YouTube fixed the cairo notification icons");
}
enumMap.putIfAbsent(tabActivityCairo, fillBellCairoBlack);
}
}
public enum NavigationButton {
HOME("PIVOT_HOME", "TAB_HOME_CAIRO"),
SHORTS("TAB_SHORTS", "TAB_SHORTS_CAIRO"),
@@ -246,6 +277,10 @@ public final class NavigationBar {
* This tab will never be in a selected state, even if the create video UI is on screen.
*/
CREATE("CREATION_TAB_LARGE", "CREATION_TAB_LARGE_CAIRO"),
/**
* Only shown to automotive layout.
*/
EXPLORE("TAB_EXPLORE"),
SUBSCRIPTIONS("PIVOT_SUBSCRIPTIONS", "TAB_SUBSCRIPTIONS_CAIRO"),
/**
* Notifications tab. Only present when
@@ -283,8 +318,8 @@ public final class NavigationBar {
*
* All code calling this method should handle a null return value.
*
* <b>Due to issues with how YT processes physical back button events,
* this patch uses workarounds that can cause this method to take up to 75ms
* <b>Due to issues with how YT processes physical back button/gesture events,
* this patch uses workarounds that can cause this method to take up to 120ms
* if the device back button was recently pressed.</b>
*
* @return The active navigation tab.

View File

@@ -73,7 +73,7 @@ enum class PlayerType {
onChange(currentPlayerType)
}
@Volatile // value is read/write from different threads
@Volatile // Read/write from different threads.
private var currentPlayerType = NONE
/**

View File

@@ -46,6 +46,7 @@ enum class VideoState {
currentVideoState = value
}
@Volatile // Read/write from different threads.
private var currentVideoState: VideoState? = null
}
}

View File

@@ -150,11 +150,16 @@ public class SBRequester {
String end = String.format(Locale.US, TIME_TEMPLATE, endTime / 1000f);
String duration = String.format(Locale.US, TIME_TEMPLATE, videoLength / 1000f);
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, privateUserId, videoId, category, start, end, duration);
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS,
privateUserId, videoId, category, start, end, duration);
final int responseCode = connection.getResponseCode();
String userMessage = switch (responseCode) {
case HTTP_STATUS_CODE_SUCCESS -> str("revanced_sb_submit_succeeded");
if (responseCode == HTTP_STATUS_CODE_SUCCESS) {
Utils.showToastLong(str("revanced_sb_submit_succeeded"));
return;
}
String userErrorMessage = switch (responseCode) {
case 409 -> str("revanced_sb_submit_failed_duplicate");
case 403 -> str("revanced_sb_submit_failed_forbidden",
Requester.parseErrorStringAndDisconnect(connection));
@@ -167,7 +172,7 @@ public class SBRequester {
// Message might be about the users account or an error too large to show in a toast.
// Use a dialog instead.
SponsorBlockUtils.showErrorDialog(userMessage);
SponsorBlockUtils.showErrorDialog(userErrorMessage);
} catch (SocketTimeoutException ex) {
Logger.printDebug(() -> "Timeout", ex);
Utils.showToastLong(str("revanced_sb_submit_failed_timeout"));

View File

@@ -8,6 +8,7 @@ import android.view.MotionEvent
import android.view.ViewGroup
import app.revanced.extension.shared.Logger.printDebug
import app.revanced.extension.shared.Logger.printException
import app.revanced.extension.youtube.settings.Settings
import app.revanced.extension.youtube.shared.PlayerType
import app.revanced.extension.youtube.swipecontrols.controller.AudioVolumeController
import app.revanced.extension.youtube.swipecontrols.controller.ScreenBrightnessController
@@ -232,5 +233,12 @@ class SwipeControlsHostActivity : Activity() {
@JvmStatic
var currentHost: WeakReference<SwipeControlsHostActivity> = WeakReference(null)
private set
/**
* Injection point.
*/
@Suppress("unused")
@JvmStatic
fun allowSwipeChangeVideo(original: Boolean): Boolean = Settings.SWIPE_CHANGE_VIDEO.get()
}
}

View File

@@ -1,5 +0,0 @@
package com.google.protos.youtube.api.innertube;
public class InnertubeContext$ClientInfo {
public int r;
}

View File

@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
org.gradle.parallel = true
android.useAndroidX = true
kotlin.code.style = official
version = 5.7.1-dev.1
version = 5.10.0-dev.4

View File

@@ -461,10 +461,6 @@ public final class app/revanced/patches/reddit/customclients/joeyforreddit/detec
public static final fun getDisablePiracyDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/redditisfun/api/FingerprintsKt {
public static final fun baseClientIdFingerprint (Ljava/lang/String;)Lapp/revanced/patcher/Fingerprint;
}
public final class app/revanced/patches/reddit/customclients/redditisfun/api/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -548,7 +544,6 @@ public final class app/revanced/patches/shared/misc/checks/BaseCheckEnvironmentP
}
public final class app/revanced/patches/shared/misc/extension/ExtensionHook {
public final fun getFingerprint ()Lapp/revanced/patcher/Fingerprint;
public final fun invoke (Lapp/revanced/patcher/patch/BytecodePatchContext;Ljava/lang/String;)V
}
@@ -608,16 +603,19 @@ public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatch
}
public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
public static final fun settingsPatch (Lkotlin/Pair;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
public static synthetic fun settingsPatch$default (Lkotlin/Pair;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
public static synthetic fun settingsPatch$default (Ljava/util/List;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch;
}
public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreference {
public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getIcon ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String;
public final fun getLayout ()Ljava/lang/String;
public final fun getSummaryKey ()Ljava/lang/String;
public final fun getTag ()Ljava/lang/String;
public final fun getTitleKey ()Ljava/lang/String;
@@ -640,17 +638,19 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP
public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getIcon ()Ljava/lang/String;
public final fun getKey ()Ljava/lang/String;
public final fun getLayout ()Ljava/lang/String;
public final fun getPreferences ()Ljava/util/Set;
public final fun getTitleKey ()Ljava/lang/String;
public abstract fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
}
public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V
public final fun getCategories ()Ljava/util/Set;
public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
@@ -658,8 +658,8 @@ public class app/revanced/patches/shared/misc/settings/preference/BasePreference
}
public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen$Category : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection {
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V
public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;
public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceCategory;
@@ -667,6 +667,7 @@ public class app/revanced/patches/shared/misc/settings/preference/BasePreference
public final class app/revanced/patches/shared/misc/settings/preference/InputType : java/lang/Enum {
public static final field NUMBER Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field NUMBER_DECIMAL Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field TEXT Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field TEXT_CAP_CHARACTERS Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public static final field TEXT_MULTI_LINE Lapp/revanced/patches/shared/misc/settings/preference/InputType;
@@ -677,8 +678,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/InputTyp
}
public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getIntent ()Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;
public fun hashCode ()I
@@ -698,8 +699,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getEntries ()Lapp/revanced/util/resource/ArrayResource;
public final fun getEntriesKey ()Ljava/lang/String;
public final fun getEntryValues ()Lapp/revanced/util/resource/ArrayResource;
@@ -708,22 +709,22 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref
}
public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getSelectable ()Z
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
public class app/revanced/patches/shared/misc/settings/preference/PreferenceCategory : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getPreferences ()Ljava/util/Set;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
public class app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getPreferences ()Ljava/util/Set;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
@@ -732,6 +733,7 @@ public final class app/revanced/patches/shared/misc/settings/preference/Preferen
public static final field BY_KEY Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
public static final field BY_TITLE Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
public static final field UNSORTED Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
public final fun appendSortType (Ljava/lang/String;)Ljava/lang/String;
public static fun getEntries ()Lkotlin/enums/EnumEntries;
public final fun getKeySuffix ()Ljava/lang/String;
public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;
@@ -750,8 +752,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SummaryT
public final class app/revanced/patches/shared/misc/settings/preference/SwitchPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getSummaryOffKey ()Ljava/lang/String;
public final fun getSummaryOnKey ()Ljava/lang/String;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
@@ -759,8 +761,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SwitchPr
public final class app/revanced/patches/shared/misc/settings/preference/TextPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getInputType ()Lapp/revanced/patches/shared/misc/settings/preference/InputType;
public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element;
}
@@ -1106,6 +1108,10 @@ public final class app/revanced/patches/youtube/layout/buttons/overlay/HidePlaye
public static final fun getHidePlayerOverlayButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/formfactor/ChangeFormFactorPatchKt {
public static final fun getChangeFormFactorPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/hide/endscreencards/HideEndscreenCardsPatchKt {
public static final fun getHideEndscreenCardsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1235,7 +1241,6 @@ public final class app/revanced/patches/youtube/layout/startupshortsreset/Disabl
}
public final class app/revanced/patches/youtube/layout/tablet/EnableTabletLayoutPatchKt {
public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String;
public static final fun getEnableTabletLayoutPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1268,10 +1273,6 @@ public final class app/revanced/patches/youtube/misc/backgroundplayback/Backgrou
public static final fun getBackgroundPlaybackPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/check/CheckEnvironmentPatchKt {
public static final fun getCheckEnvironmentPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/debugging/EnableDebuggingPatchKt {
public static final fun getEnableDebuggingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1296,6 +1297,10 @@ public final class app/revanced/patches/youtube/misc/fix/playback/UserAgentClien
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatchKt {
public static final fun getFixPlaybackSpeedWhilePlayingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatchKt {
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1360,6 +1365,7 @@ public final class app/revanced/patches/youtube/misc/playservice/VersionCheckPat
public static final fun is_19_43_or_greater ()Z
public static final fun is_19_46_or_greater ()Z
public static final fun is_19_47_or_greater ()Z
public static final fun is_19_49_or_greater ()Z
}
public final class app/revanced/patches/youtube/misc/privacy/RemoveTrackingQueryParameterPatchKt {
@@ -1404,14 +1410,14 @@ public final class app/revanced/patches/youtube/misc/zoomhaptics/ZoomHapticsPatc
public static final fun getZoomHapticsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/shared/FingerprintsKt {
public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint;
}
public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt {
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/hdr/DisableHdrPatchKt {
public static final fun getDisableHdrPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt {
public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V
@@ -1453,10 +1459,6 @@ public final class app/revanced/patches/youtube/video/speed/button/PlaybackSpeed
public static final fun getPlaybackSpeedButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/speed/custom/CustomPlaybackSpeedPatchKt {
public static final fun getSpeedUnavailableId ()J
}
public final class app/revanced/patches/youtube/video/videoid/VideoIdPatchKt {
public static final fun getVideoIdPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun hookBackgroundPlayVideoId (Ljava/lang/String;)V

View File

@@ -4,7 +4,7 @@ import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
fun baseClientIdFingerprint(string: String) = fingerprint {
internal fun baseClientIdFingerprint(string: String) = fingerprint {
strings("yyOCBp.RHJhDKd", string)
}

View File

@@ -92,7 +92,7 @@ fun sharedExtensionPatch(
}
class ExtensionHook internal constructor(
val fingerprint: Fingerprint,
private val fingerprint: Fingerprint,
private val insertIndexResolver: ((Method) -> Int),
private val contextRegisterResolver: (Method) -> String,
) {

View File

@@ -1,5 +1,6 @@
package app.revanced.patches.shared.misc.settings
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResource
import app.revanced.patches.all.misc.resources.addResources
@@ -12,15 +13,22 @@ import app.revanced.util.getNode
import app.revanced.util.insertFirst
import org.w3c.dom.Node
// TODO: Delete this on next major version bump.
@Deprecated("Use non deprecated settings patch function")
fun settingsPatch (
rootPreference: Pair<IntentPreference, String>,
preferences: Set<BasePreference>,
) = settingsPatch(listOf(rootPreference), preferences)
/**
* A resource patch that adds settings to a settings fragment.
*
* @param rootPreference A pair of an intent preference and the name of the fragment file to add it to.
* If null, no preference will be added.
* @param rootPreferences List of intent preferences and the name of the fragment file to add it to.
* File names that do not exist are ignored and not processed.
* @param preferences A set of preferences to add to the ReVanced fragment.
*/
fun settingsPatch(
rootPreference: Pair<IntentPreference, String>? = null,
fun settingsPatch (
rootPreferences: List<Pair<BasePreference, String>>? = null,
preferences: Set<BasePreference>,
) = resourcePatch {
dependsOn(addResourcesPatch)
@@ -46,10 +54,20 @@ fun settingsPatch(
}
// Add the root preference to an existing fragment if needed.
rootPreference?.let { (intentPreference, fragment) ->
document("res/xml/$fragment.xml").use { document ->
document.getNode("PreferenceScreen").addPreference(intentPreference, true)
rootPreferences?.let {
var modified = false
it.forEach { (intent, fileName) ->
val preferenceFileName = "res/xml/$fileName.xml"
if (get(preferenceFileName).exists()) {
document(preferenceFileName).use { document ->
document.getNode("PreferenceScreen").addPreference(intent, true)
}
modified = true
}
}
if (!modified) throw PatchException("No declared preference files exists: $rootPreferences")
}
// Add all preferences to the ReVanced fragment.

View File

@@ -9,6 +9,8 @@ import org.w3c.dom.Element
*
* @param key The key of the preference. If null, other parameters must be specified.
* @param titleKey The key of the preference title.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param summaryKey The key of the preference summary.
* @param tag The tag or full class name of the preference.
*/
@@ -17,6 +19,8 @@ abstract class BasePreference(
val key: String? = null,
val titleKey: String = "${key}_title",
val summaryKey: String? = "${key}_summary",
val icon: String? = null,
val layout: String? = null,
val tag: String
) {
/**
@@ -33,6 +37,11 @@ abstract class BasePreference(
key?.let { setAttribute("android:key", it) }
setAttribute("android:title", "@string/${titleKey}")
summaryKey?.let { addSummary(it) }
icon?.let {
setAttribute("android:icon", it)
setAttribute("app:iconSpaceReserved", "true")
}
layout?.let { setAttribute("android:layout", layout) }
}
override fun hashCode(): Int {

View File

@@ -24,16 +24,20 @@ abstract class BasePreferenceScreen(
key: String? = null,
titleKey: String = "${key}_title",
private val summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
preferences: MutableSet<BasePreference> = mutableSetOf(),
val categories: MutableSet<Category> = mutableSetOf(),
private val sorting: Sorting = Sorting.BY_TITLE,
) : BasePreferenceCollection(key, titleKey, preferences) {
) : BasePreferenceCollection(key, titleKey, icon, layout, preferences) {
override fun transform(): PreferenceScreenPreference {
return PreferenceScreenPreference(
key,
titleKey,
summaryKey,
icon,
layout,
sorting,
// Screens and preferences are sorted at runtime by extension code,
// so title sorting uses the localized language in use.
@@ -56,12 +60,17 @@ abstract class BasePreferenceScreen(
open inner class Category(
key: String? = null,
titleKey: String = "${key}_title",
icon: String? = null,
layout: String? = null,
preferences: MutableSet<BasePreference> = mutableSetOf(),
) : BasePreferenceCollection(key, titleKey, preferences) {
) : BasePreferenceCollection(key, titleKey, icon, layout, preferences) {
override fun transform(): PreferenceCategory {
return PreferenceCategory(
key,
titleKey,
icon,
layout,
sorting,
preferences = preferences,
)
}
@@ -82,6 +91,8 @@ abstract class BasePreferenceScreen(
abstract class BasePreferenceCollection(
val key: String? = null,
val titleKey: String = "${key}_title",
val icon: String? = null,
val layout: String? = null,
val preferences: MutableSet<BasePreference> = mutableSetOf(),
) {
abstract fun transform(): BasePreference

View File

@@ -5,4 +5,5 @@ enum class InputType(val type: String) {
TEXT_CAP_CHARACTERS("textCapCharacters"),
TEXT_MULTI_LINE("textMultiLine"),
NUMBER("number"),
NUMBER_DECIMAL("numberDecimal"),
}

View File

@@ -9,6 +9,8 @@ import org.w3c.dom.Document
* @param key Optional preference key.
* @param titleKey The preference title key.
* @param summaryKey The preference summary key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The preference tag.
* @param intent The intent to open.
*/
@@ -16,9 +18,11 @@ class IntentPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
tag: String = "Preference",
val intent: Intent,
) : BasePreference(key, titleKey, summaryKey, tag) {
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {

View File

@@ -10,6 +10,8 @@ import org.w3c.dom.Document
* @param key The preference key. If null, other parameters must be specified.
* @param titleKey The preference title key.
* @param summaryKey The preference summary key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The preference tag.
* @param entriesKey The entries array key.
* @param entryValuesKey The entry values array key.
@@ -19,10 +21,12 @@ class ListPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
tag: String = "ListPreference",
val entriesKey: String? = "${key}_entries",
val entryValuesKey: String? = "${key}_entry_values"
) : BasePreference(key, titleKey, summaryKey, tag) {
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
var entries: ArrayResource? = null
private set
var entryValues: ArrayResource? = null

View File

@@ -10,6 +10,8 @@ import org.w3c.dom.Document
*
* @param key The preference key.
* @param summaryKey The preference summary key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The tag or full class name of the preference.
* @param selectable If the preference is selectable and responds to tap events.
*/
@@ -18,9 +20,11 @@ class NonInteractivePreference(
key: String,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
tag: String = "Preference",
val selectable: Boolean = false,
) : BasePreference(key, titleKey, summaryKey, tag) {
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {
setAttribute("android:selectable", selectable.toString())

View File

@@ -1,5 +1,6 @@
package app.revanced.patches.shared.misc.settings.preference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.util.resource.BaseResource
import org.w3c.dom.Document
@@ -8,6 +9,8 @@ import org.w3c.dom.Document
*
* @param key The key of the preference. If null, other parameters must be specified.
* @param titleKey The key of the preference title.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The tag or full class name of the preference.
* @param preferences The preferences in this category.
*/
@@ -15,9 +18,12 @@ import org.w3c.dom.Document
open class PreferenceCategory(
key: String? = null,
titleKey: String = "${key}_title",
icon: String? = null,
layout: String? = null,
sorting: Sorting = Sorting.BY_TITLE,
tag: String = "PreferenceCategory",
val preferences: Set<BasePreference>
) : BasePreference(key, titleKey, null, tag) {
) : BasePreference(sorting.appendSortType(key), titleKey, null, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {

View File

@@ -9,6 +9,8 @@ import org.w3c.dom.Document
* @param key The key of the preference. If null, other parameters must be specified.
* @param titleKey The key of the preference title.
* @param summaryKey The key of the preference summary.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param sorting Sorting to use. If the sorting is not [Sorting.UNSORTED],
* then the key parameter will be modified to include the sort type.
* @param tag The tag or full class name of the preference.
@@ -19,6 +21,8 @@ open class PreferenceScreenPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
sorting: Sorting = Sorting.BY_TITLE,
tag: String = "PreferenceScreen",
val preferences: Set<BasePreference>,
@@ -28,7 +32,7 @@ open class PreferenceScreenPreference(
// or adding new attributes to the attrs.xml file.
// Since the key value is not currently used by the extensions,
// for now it's much simpler to modify the key to include the sort parameter.
) : BasePreference(if (sorting == Sorting.UNSORTED) key else (key + sorting.keySuffix), titleKey, summaryKey, tag) {
) : BasePreference(sorting.appendSortType(key), titleKey, summaryKey, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {
preferences.forEach {
@@ -53,6 +57,16 @@ open class PreferenceScreenPreference(
/**
* Unspecified sorting.
*/
UNSORTED("_sort_by_unsorted"),
UNSORTED("_sort_by_unsorted");
/**
* @return The key with this sort type appended to to the end,
* or if key is null then null is returned.
*/
fun appendSortType(key: String?): String? {
if (key == null) return null
if (this == UNSORTED) return key
return key + keySuffix
}
}
}

View File

@@ -8,6 +8,8 @@ import org.w3c.dom.Document
*
* @param key The preference key. If null, other parameters must be specified.
* @param titleKey The preference title key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The preference tag.
* @param summaryOnKey The preference summary-on key.
* @param summaryOffKey The preference summary-off key.
@@ -17,9 +19,11 @@ class SwitchPreference(
key: String? = null,
titleKey: String = "${key}_title",
tag: String = "SwitchPreference",
icon: String? = null,
layout: String? = null,
val summaryOnKey: String = "${key}_summary_on",
val summaryOffKey: String = "${key}_summary_off"
) : BasePreference(key, titleKey, null, tag) {
) : BasePreference(key, titleKey, null, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {
addSummary(summaryOnKey, SummaryType.ON)

View File

@@ -9,6 +9,8 @@ import org.w3c.dom.Document
* @param key The preference key. If null, other parameters must be specified.
* @param titleKey The preference title key.
* @param summaryKey The preference summary key.
* @param icon The preference icon resource name.
* @param layout Layout declaration.
* @param tag The preference tag.
* @param inputType The preference input type.
*/
@@ -17,9 +19,11 @@ class TextPreference(
key: String? = null,
titleKey: String = "${key}_title",
summaryKey: String? = "${key}_summary",
icon: String? = null,
layout: String? = null,
tag: String = "app.revanced.extension.shared.settings.preference.ResettableEditTextPreference",
val inputType: InputType = InputType.TEXT
) : BasePreference(key, titleKey, summaryKey, tag) {
) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -111,6 +112,23 @@ internal val buildMediaDataSourceFingerprint = fingerprint {
)
}
internal const val HLS_CURRENT_TIME_FEATURE_FLAG = 45355374L
internal val hlsCurrentTimeFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("Z", "L")
literal {
HLS_CURRENT_TIME_FEATURE_FLAG
}
}
internal val nerdsStatsVideoFormatBuilderFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Ljava/lang/String;")
parameters("L")
strings("codecs=\"")
}
internal val patchIncludedExtensionMethodFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("Z")

View File

@@ -10,8 +10,10 @@ import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertFeatureFlagBooleanOverride
import app.revanced.util.returnEarly
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -206,6 +208,34 @@ fun spoofVideoStreamsPatch(
""",
)
}
// endregion
// region Append spoof info.
nerdsStatsVideoFormatBuilderFingerprint.method.apply {
findInstructionIndicesReversedOrThrow(Opcode.RETURN_OBJECT).forEach { index ->
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->appendSpoofedClient(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$register
"""
)
}
}
// endregion
// region Fix iOS livestream current time.
hlsCurrentTimeFingerprint.method.insertFeatureFlagBooleanOverride(
HLS_CURRENT_TIME_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
)
// endregion
executeBlock()

View File

@@ -37,6 +37,7 @@ private val hideAdsResourcePatch = resourcePatch {
PreferenceScreen.ADS.addPreferences(
SwitchPreference("revanced_hide_general_ads"),
SwitchPreference("revanced_hide_end_screen_store_banner"),
SwitchPreference("revanced_hide_fullscreen_ads"),
SwitchPreference("revanced_hide_buttoned_ads"),
SwitchPreference("revanced_hide_paid_promotion_label"),

View File

@@ -1,12 +1,23 @@
package app.revanced.patches.youtube.interaction.swipecontrols
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
internal val swipeControlsHostActivityFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters()
custom { method, _ ->
method.definingClass == "Lapp/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity;"
method.definingClass == EXTENSION_CLASS_DESCRIPTOR
}
}
internal const val SWIPE_CHANGE_VIDEO_FEATURE_FLAG = 45631116L
internal val swipeChangeVideoFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters("L")
literal {
SWIPE_CHANGE_VIDEO_FEATURE_FLAG
}
}

View File

@@ -10,6 +10,7 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_43_or_greater
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityFingerprint
@@ -17,6 +18,8 @@ import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/swipecontrols/SwipeControlsHostActivity;"
private val swipeControlsResourcePatch = resourcePatch {
dependsOn(
settingsPatch,
@@ -26,6 +29,12 @@ private val swipeControlsResourcePatch = resourcePatch {
execute {
addResources("youtube", "interaction.swipecontrols.swipeControlsResourcePatch")
if (is_19_43_or_greater) {
PreferenceScreen.SWIPE_CONTROLS.addPreferences(
SwitchPreference("revanced_swipe_change_video")
)
}
PreferenceScreen.SWIPE_CONTROLS.addPreferences(
SwitchPreference("revanced_swipe_brightness"),
SwitchPreference("revanced_swipe_volume"),
@@ -101,5 +110,16 @@ val swipeControlsPatch = bytecodePatch(
).toMutable()
}
}
// region patch to enable/disable swipe to change video.
if (is_19_43_or_greater) {
swipeChangeVideoFingerprint.method.insertFeatureFlagBooleanOverride(
SWIPE_CHANGE_VIDEO_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->allowSwipeChangeVideo(Z)Z"
)
}
// endregion
}
}

View File

@@ -65,9 +65,12 @@ val navigationButtonsPatch = bytecodePatch(
)
if (is_19_25_or_greater) {
preferences += SwitchPreference("revanced_disable_translucent_status_bar")
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_light")
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_dark")
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_disable_translucent_status_bar")
)
}
PreferenceScreen.GENERAL_LAYOUT.addPreferences(

View File

@@ -0,0 +1,75 @@
package app.revanced.patches.youtube.layout.formfactor
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/ChangeFormFactorPatch;"
@Suppress("unused")
val changeFormFactorPatch = bytecodePatch(
name = "Change form factor",
description = "Adds an option to change the UI appearance to a phone, tablet, or automotive device.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
addResources("youtube", "layout.formfactor.changeFormFactorPatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
ListPreference(
"revanced_change_form_factor",
summaryKey = null,
)
)
createPlayerRequestBodyWithModelFingerprint.method.apply {
val formFactorEnumClass = formFactorEnumConstructorFingerprint.originalClassDef.type
val index = indexOfFirstInstructionOrThrow {
val reference = getReference<FieldReference>()
opcode == Opcode.IGET &&
reference?.definingClass == formFactorEnumClass &&
reference.type == "I"
}
val register = getInstruction<TwoRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getFormFactor(I)I
move-result v$register
"""
)
}
}
}

View File

@@ -0,0 +1,49 @@
package app.revanced.patches.youtube.layout.formfactor
import app.revanced.patcher.fingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
internal val formFactorEnumConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
strings(
"UNKNOWN_FORM_FACTOR",
"SMALL_FORM_FACTOR",
"LARGE_FORM_FACTOR",
"AUTOMOTIVE_FORM_FACTOR"
)
}
internal val createPlayerRequestBodyWithModelFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
parameters()
opcodes(Opcode.OR_INT_LIT16)
custom { method, _ ->
method.indexOfModelInstruction() >= 0 &&
method.indexOfReleaseInstruction() >= 0
}
}
private fun Method.indexOfModelInstruction() =
indexOfFirstInstruction {
val reference = getReference<FieldReference>()
reference?.definingClass == "Landroid/os/Build;" &&
reference.name == "MODEL" &&
reference.type == "Ljava/lang/String;"
}
internal fun Method.indexOfReleaseInstruction(): Int =
indexOfFirstInstruction {
val reference = getReference<FieldReference>()
reference?.definingClass == "Landroid/os/Build${'$'}VERSION;" &&
reference.name == "RELEASE" &&
reference.type == "Ljava/lang/String;"
}

View File

@@ -147,6 +147,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
SwitchPreference("revanced_hide_attributes_section"),
SwitchPreference("revanced_hide_chapters_section"),
SwitchPreference("revanced_hide_info_cards_section"),
SwitchPreference("revanced_hide_how_this_was_made_section"),
SwitchPreference("revanced_hide_key_concepts_section"),
SwitchPreference("revanced_hide_podcast_section"),
SwitchPreference("revanced_hide_transcript_section"),

View File

@@ -71,6 +71,7 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
SwitchPreference("revanced_hide_shorts_home"),
SwitchPreference("revanced_hide_shorts_subscriptions"),
SwitchPreference("revanced_hide_shorts_search"),
SwitchPreference("revanced_hide_shorts_history"),
PreferenceScreenPreference(
key = "revanced_shorts_player_screen",

View File

@@ -0,0 +1,67 @@
package app.revanced.patches.youtube.layout.player.fullscreen
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playercontrols.playerControlsPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.autoRepeatFingerprint
import app.revanced.patches.youtube.shared.autoRepeatParentFingerprint
import app.revanced.util.addInstructionsAtControlFlowLabel
@Suppress("unused")
internal val exitFullscreenPatch = bytecodePatch(
name = "Exit fullscreen mode",
description = "Adds options to automatically exit fullscreen mode when a video reaches the end."
) {
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
)
)
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
playerTypeHookPatch,
playerControlsPatch
)
// Cannot declare as top level since this patch is in the same package as
// other patches that declare same constant name with internal visibility.
@Suppress("LocalVariableName")
val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/ExitFullscreenPatch;"
execute {
addResources("youtube", "layout.player.fullscreen.exitFullscreenPatch")
PreferenceScreen.PLAYER.addPreferences(
ListPreference(
"revanced_exit_fullscreen",
summaryKey = null,
)
)
autoRepeatFingerprint.match(autoRepeatParentFingerprint.originalClassDef).method.apply {
addInstructionsAtControlFlowLabel(
implementation!!.instructions.lastIndex,
"invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->endOfVideoReached()V",
)
}
}
}

View File

@@ -34,13 +34,17 @@ internal val shortsSeekbarColorFingerprint = fingerprint {
literal { reelTimeBarPlayedColorId }
}
internal const val PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG = 45617850L
internal val playerSeekbarHandleColorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters("Landroid/content/Context;")
literal { ytStaticBrandRedId }
}
internal val playerSeekbarGradientConfigFingerprint = fingerprint {
internal val watchHistoryMenuUseProgressDrawableFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
parameters()
literal { PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG }
returns("V")
parameters("L")
literal { -1712394514 }
}
internal val lithoLinearGradientFingerprint = fingerprint {
@@ -49,6 +53,49 @@ internal val lithoLinearGradientFingerprint = fingerprint {
parameters("F", "F", "F", "F", "[I", "[F")
}
/**
* 19.49+
*/
internal val playerLinearGradientFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
parameters("I", "I", "I", "I", "Landroid/content/Context;", "I")
returns("Landroid/graphics/LinearGradient;")
opcodes(
Opcode.FILLED_NEW_ARRAY,
Opcode.MOVE_RESULT_OBJECT
)
literal { ytYoutubeMagentaColorId }
}
/**
* 19.46 - 19.47
*/
internal val playerLinearGradientLegacy1946Fingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("I", "I", "I", "I")
returns("V")
opcodes(
Opcode.FILLED_NEW_ARRAY,
Opcode.MOVE_RESULT_OBJECT
)
custom { method, _ ->
method.name == "setBounds" && method.containsLiteralInstruction(ytYoutubeMagentaColorId)
}
}
/**
* 19.25 - 19.45
*/
internal val playerLinearGradientLegacy1925Fingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters("Landroid/content/Context;")
opcodes(
Opcode.FILLED_NEW_ARRAY,
Opcode.MOVE_RESULT_OBJECT
)
literal { ytYoutubeMagentaColorId }
}
internal const val launchScreenLayoutTypeLotteFeatureFlag = 268507948L
internal val launchScreenLayoutTypeFingerprint = fingerprint {

View File

@@ -14,7 +14,9 @@ import app.revanced.patches.youtube.layout.theme.lithoColorHookPatch
import app.revanced.patches.youtube.layout.theme.lithoColorOverrideHook
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_49_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
@@ -39,6 +41,10 @@ internal var inlineTimeBarColorizedBarPlayedColorDarkId = -1L
private set
internal var inlineTimeBarPlayedNotHighlightedColorId = -1L
private set
internal var ytYoutubeMagentaColorId = -1L
private set
internal var ytStaticBrandRedId = -1L
private set
internal const val splashSeekbarColorAttributeName = "splash_custom_seekbar_color"
@@ -83,6 +89,15 @@ private val seekbarColorResourcePatch = resourcePatch {
return@execute
}
ytYoutubeMagentaColorId = resourceMappings[
"color",
"yt_youtube_magenta",
]
ytStaticBrandRedId = resourceMappings[
"attr",
"ytStaticBrandRed",
]
// Add attribute and styles for splash screen custom color.
// Using a style is the only way to selectively change just the seekbar fill color.
//
@@ -182,28 +197,31 @@ val seekbarColorPatch = bytecodePatch(
sharedExtensionPatch,
lithoColorHookPatch,
seekbarColorResourcePatch,
versionCheckPatch
)
execute {
fun MutableMethod.addColorChangeInstructions(resourceId: Long) {
val registerIndex = indexOfFirstLiteralInstructionOrThrow(resourceId) + 2
val colorRegister = getInstruction<OneRegisterInstruction>(registerIndex).registerA
fun MutableMethod.addColorChangeInstructions(resourceId: Long, methodName: String) {
val index = indexOfFirstLiteralInstructionOrThrow(resourceId)
val insertIndex = indexOfFirstInstructionOrThrow(index, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(insertIndex).registerA
addInstructions(
registerIndex + 1,
insertIndex + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$methodName(I)I
move-result v$register
"""
invoke-static { v$colorRegister }, $EXTENSION_CLASS_DESCRIPTOR->getVideoPlayerSeekbarColor(I)I
move-result v$colorRegister
""",
)
}
playerSeekbarColorFingerprint.method.apply {
addColorChangeInstructions(inlineTimeBarColorizedBarPlayedColorDarkId)
addColorChangeInstructions(inlineTimeBarPlayedNotHighlightedColorId)
addColorChangeInstructions(inlineTimeBarColorizedBarPlayedColorDarkId, "getVideoPlayerSeekbarColor")
addColorChangeInstructions(inlineTimeBarPlayedNotHighlightedColorId, "getVideoPlayerSeekbarColor")
}
shortsSeekbarColorFingerprint.method.apply {
addColorChangeInstructions(reelTimeBarPlayedColorId)
addColorChangeInstructions(reelTimeBarPlayedColorId, "getVideoPlayerSeekbarColor")
}
setSeekbarClickedColorFingerprint.originalMethod.let {
@@ -229,16 +247,63 @@ val seekbarColorPatch = bytecodePatch(
// 19.25+ changes
playerSeekbarGradientConfigFingerprint.method.insertFeatureFlagBooleanOverride(
PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z"
)
playerSeekbarHandleColorFingerprint.method.apply {
addColorChangeInstructions(ytStaticBrandRedId, "getVideoPlayerSeekbarColor")
}
lithoLinearGradientFingerprint.method.addInstruction(
// If hiding feed seekbar thumbnails, then turn off the cairo gradient
// of the watch history menu items as they use the same gradient as the
// player and there is no easy way to distinguish which to use a transparent color.
if (is_19_34_or_greater) {
watchHistoryMenuUseProgressDrawableFingerprint.method.apply {
val progressIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Landroid/widget/ProgressBar;" && reference.name == "setMax"
}
val index = indexOfFirstInstructionOrThrow(progressIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->showWatchHistoryProgressDrawable(Z)Z
move-result v$register
"""
)
}
}
lithoLinearGradientFingerprint.method.addInstructions(
0,
"invoke-static/range { p4 .. p5 }, $EXTENSION_CLASS_DESCRIPTOR->setLinearGradient([I[F)V"
"""
invoke-static/range { p4 .. p5 }, $EXTENSION_CLASS_DESCRIPTOR->getLithoLinearGradient([I[F)[I
move-result-object p4
"""
)
val playerFingerprint =
if (is_19_49_or_greater) {
playerLinearGradientFingerprint
} else if (is_19_46_or_greater) {
playerLinearGradientLegacy1946Fingerprint
} else {
playerLinearGradientLegacy1925Fingerprint
}
playerFingerprint.let {
it.method.apply {
val index = it.patternMatch!!.endIndex
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->getPlayerLinearGradient([I)[I
move-result-object v$register
"""
)
}
}
// region apply seekbar custom color to splash screen animation.

View File

@@ -1,66 +1,9 @@
package app.revanced.patches.youtube.layout.tablet
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.layout.formfactor.changeFormFactorPatch
const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/TabletLayoutPatch;"
val enableTabletLayoutPatch = bytecodePatch(
name = "Enable tablet layout",
description = "Adds an option to enable tablet layout.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
addResources("youtube", "layout.tablet.enableTabletLayoutPatch")
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_tablet_layout"),
)
getFormFactorFingerprint.method.apply {
val returnIsLargeFormFactorIndex = instructions.lastIndex - 4
val returnIsLargeFormFactorLabel = getInstruction(returnIsLargeFormFactorIndex)
addInstructionsWithLabels(
0,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getTabletLayoutEnabled()Z
move-result v0
if-nez v0, :is_large_form_factor
""",
ExternalLabel(
"is_large_form_factor",
returnIsLargeFormFactorLabel,
),
)
}
}
}
@Deprecated("Use 'Change form factor' instead.")
val enableTabletLayoutPatch = bytecodePatch {
dependsOn(changeFormFactorPatch)
}

View File

@@ -1,25 +0,0 @@
package app.revanced.patches.youtube.layout.tablet
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
internal val getFormFactorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("L")
parameters("Landroid/content/Context;", "Ljava/util/List;")
opcodes(
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.SGET_OBJECT,
Opcode.RETURN_OBJECT,
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.RETURN_OBJECT,
)
strings("")
}

View File

@@ -13,6 +13,8 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.layout.seekbar.seekbarColorPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.forEachChildElement
@@ -71,6 +73,7 @@ val themePatch = bytecodePatch(
dependsOn(
lithoColorHookPatch,
seekbarColorPatch,
versionCheckPatch,
resourcePatch {
dependsOn(
settingsPatch,
@@ -83,9 +86,15 @@ val themePatch = bytecodePatch(
PreferenceScreen.SEEKBAR.addPreferences(
SwitchPreference("revanced_seekbar_custom_color"),
TextPreference("revanced_seekbar_custom_color_value", inputType = InputType.TEXT_CAP_CHARACTERS),
TextPreference("revanced_seekbar_custom_color_primary", inputType = InputType.TEXT_CAP_CHARACTERS),
)
if (is_19_25_or_greater) {
PreferenceScreen.SEEKBAR.addPreferences(
TextPreference("revanced_seekbar_custom_color_accent", inputType = InputType.TEXT_CAP_CHARACTERS),
)
}
// Edit theme colors via resources.
document("res/values/colors.xml").use { document ->
@@ -163,22 +172,31 @@ val themePatch = bytecodePatch(
}
// Fix the splash screen dark mode background color.
// In earlier versions of the app this is white and makes no sense for dark mode.
// This is only required for 19.32 and greater, but is applied to all targets.
// Only dark mode needs this fix as light mode correctly uses the custom color.
// In 19.32+ the dark mode splash screen is white and fades to black.
// Maybe it's a bug in YT, or maybe it intentionally. Who knows.
document("res/values-night/styles.xml").use { document ->
// Create a night mode specific override for the splash screen background.
val style = document.createElement("style")
style.setAttribute("name", "Theme.YouTube.Home")
style.setAttribute("parent", "@style/Base.V23.Theme.YouTube.Home")
val windowItem = document.createElement("item")
windowItem.setAttribute("name", "android:windowBackground")
windowItem.textContent = "@color/$splashBackgroundColor"
style.appendChild(windowItem)
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
resourcesNode.appendChild(style)
val childNodes = resourcesNode.childNodes
for (i in 0 until childNodes.length) {
val node = childNodes.item(i) as? Element ?: continue
val nodeAttributeName = node.getAttribute("name")
if (nodeAttributeName.startsWith("Theme.YouTube.Launcher")) {
val nodeAttributeParent = node.getAttribute("parent")
val style = document.createElement("style")
style.setAttribute("name", "Theme.YouTube.Home")
style.setAttribute("parent", nodeAttributeParent)
val windowItem = document.createElement("item")
windowItem.setAttribute("name", "android:windowBackground")
windowItem.textContent = "@color/$splashBackgroundColor"
style.appendChild(windowItem)
resourcesNode.removeChild(node)
resourcesNode.appendChild(style)
}
}
}
}
}

View File

@@ -4,7 +4,7 @@ import app.revanced.patches.shared.misc.checks.checkEnvironmentPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
val checkEnvironmentPatch = checkEnvironmentPatch(
internal val checkEnvironmentPatch = checkEnvironmentPatch(
mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint,
extensionPatch = sharedExtensionPatch,
"com.google.android.youtube",

View File

@@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
private const val EXTENSION_CLASS_DESCRIPTOR =
@@ -13,7 +14,10 @@ val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch(
name = "Check watch history domain name resolution",
description = "Checks if the device DNS server is preventing user watch history from being saved.",
) {
dependsOn(addResourcesPatch)
dependsOn(
sharedExtensionPatch,
addResourcesPatch
)
compatibleWith(
"com.google.android.youtube"(

View File

@@ -1,47 +0,0 @@
package app.revanced.patches.youtube.misc.fix.cairo
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.youtube.misc.backgroundplayback.backgroundPlaybackPatch
import app.revanced.patches.youtube.misc.playservice.is_19_04_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
internal val disableCairoSettingsPatch = bytecodePatch(
description = "Disables Cairo Fragment from being used.",
) {
dependsOn(versionCheckPatch)
execute {
if (!is_19_04_or_greater) {
return@execute
}
/**
* <pre>
* Cairo Fragment was added since YouTube v19.04.38.
*
* Disable this for the following reasons:
* 1. [backgroundPlaybackPatch] does not activate the Minimized playback setting of Cairo Fragment.
* 2. Some patches do not yet support Cairo Fragments (ie: custom Seekbar color).
* 3. Settings preferences added by ReVanced are missing.
*
* Screenshots of the Cairo Fragment:
* <a href="https://github.com/qnblackcat/uYouPlus/issues/1468">uYouPlus#1468</a>.
*/
cairoFragmentConfigFingerprint.method.apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(CAIRO_CONFIG_LITERAL_VALUE)
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
addInstruction(
resultIndex + 1,
"const/16 v$register, 0x0",
)
}
}
}

View File

@@ -1,19 +0,0 @@
package app.revanced.patches.youtube.misc.fix.cairo
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Added in YouTube v19.04.38.
*
* When this value is true, Cairo Fragment is used.
* In this case, some of the patches may be broken, so set this value to FALSE.
*/
internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
internal val cairoFragmentConfigFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
literal { CAIRO_CONFIG_LITERAL_VALUE }
}

View File

@@ -0,0 +1,61 @@
package app.revanced.patches.youtube.misc.fix.playbackspeed
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/FixPlaybackSpeedWhilePlayingPatch;"
/**
* Fixes a bug in YouTube 19.34+ where the playback speed
* can incorrectly reset to 1.0x under certain conditions.
*
* Reproduction steps using 19.34+
* 1. Open a video and start playback
* 2. Change the speed to any value that is not 1.0x.
* 3. Open the comments panel.
* 4. Tap any "N more replies" link at the bottom of a comment, or tap on a timestamp of a comment.
* 5. Pause the video
* 6. Resume the video
* 7. Playback speed will incorrectly change to 1.0x.
*/
@Suppress("unused")
val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch{
dependsOn(
sharedExtensionPatch,
playerTypeHookPatch,
versionCheckPatch,
)
execute {
if (!is_19_34_or_greater) {
return@execute
}
playbackSpeedInFeedsFingerprint.method.apply {
val freeRegister = implementation!!.registerCount - parameters.size - 2
val playbackSpeedIndex = indexOfGetPlaybackSpeedInstruction(this)
val playbackSpeedRegister = getInstruction<TwoRegisterInstruction>(playbackSpeedIndex).registerA
val returnIndex = indexOfFirstInstructionOrThrow(playbackSpeedIndex, Opcode.RETURN_VOID)
addInstructionsWithLabels(
playbackSpeedIndex + 1,
"""
invoke-static { v$playbackSpeedRegister }, $EXTENSION_CLASS_DESCRIPTOR->playbackSpeedChanged(F)Z
move-result v$freeRegister
if-nez v$freeRegister, :do_not_change
""",
ExternalLabel("do_not_change", getInstruction(returnIndex))
)
}
}
}

View File

@@ -0,0 +1,38 @@
package app.revanced.patches.youtube.misc.fix.playbackspeed
import app.revanced.patcher.fingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversed
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
/**
* This method is usually used to set the initial speed (1.0x) when playback starts from the feed.
* For some reason, in the latest YouTube, it is invoked even after the video has already started.
*/
internal val playbackSpeedInFeedsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("L")
opcodes(
Opcode.IGET,
Opcode.MUL_INT_LIT16,
Opcode.IGET_WIDE,
Opcode.CONST_WIDE_16,
Opcode.CMP_LONG,
Opcode.IF_EQZ,
Opcode.IF_LEZ,
Opcode.SUB_LONG_2ADDR,
)
custom { method, _ ->
indexOfGetPlaybackSpeedInstruction(method) >= 0
}
}
internal fun indexOfGetPlaybackSpeedInstruction(method: Method) =
method.indexOfFirstInstructionReversed {
opcode == Opcode.IGET &&
getReference<FieldReference>()?.type == "F"
}

View File

@@ -105,3 +105,15 @@ internal val pivotBarConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
strings("com.google.android.apps.youtube.app.endpoint.flags")
}
internal val imageEnumConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
strings("TAB_ACTIVITY_CAIRO")
}
internal val setEnumMapFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
literal {
ytFillBellId
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.misc.navigation
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.patch.PatchException
@@ -12,13 +13,16 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_35_or_greater
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
@@ -26,6 +30,8 @@ internal var imageOnlyTabResourceId = -1L
private set
internal var actionBarSearchResultsViewMicId = -1L
private set
internal var ytFillBellId = -1L
private set
private val navigationBarHookResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
@@ -33,6 +39,7 @@ private val navigationBarHookResourcePatch = resourcePatch {
execute {
imageOnlyTabResourceId = resourceMappings["layout", "image_only_tab"]
actionBarSearchResultsViewMicId = resourceMappings["layout", "action_bar_search_results_view_mic"]
ytFillBellId = resourceMappings["drawable", "yt_fill_bell_black_24"]
}
}
@@ -144,6 +151,36 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
"(${EXTENSION_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V",
)
}
// Fix YT bug of notification tab missing the filled icon.
if (is_19_35_or_greater) {
val cairoNotificationEnumReference = with(imageEnumConstructorFingerprint) {
val stringIndex = stringMatches!!.first().index
val cairoNotificationEnumIndex = method.indexOfFirstInstructionOrThrow(stringIndex) {
opcode == Opcode.SPUT_OBJECT
}
method.getInstruction<ReferenceInstruction>(cairoNotificationEnumIndex).reference
}
setEnumMapFingerprint.method.apply {
val enumMapIndex = indexOfFirstInstructionReversedOrThrow {
val reference = getReference<MethodReference>()
opcode == Opcode.INVOKE_VIRTUAL &&
reference?.definingClass == "Ljava/util/EnumMap;" &&
reference.name == "put" &&
reference.parameterTypes.firstOrNull() == "Ljava/lang/Enum;"
}
val instruction = getInstruction<FiveRegisterInstruction>(enumMapIndex)
addInstructions(
enumMapIndex + 1,
"""
sget-object v${instruction.registerD}, $cairoNotificationEnumReference
invoke-static { v${instruction.registerC}, v${instruction.registerD} }, $EXTENSION_CLASS_DESCRIPTOR->setCairoNotificationFilledIcon(Ljava/util/EnumMap;Ljava/lang/Enum;)V
"""
)
}
}
}
}

View File

@@ -12,13 +12,23 @@ internal val playerTopControlsInflateFingerprint = fingerprint {
literal { controlsLayoutStub }
}
internal val playerControlsExtensionHookListenersExistFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("Z")
parameters()
custom { methodDef, classDef ->
methodDef.name == "fullscreenButtonVisibilityCallbacksExist" &&
classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}
internal val playerControlsExtensionHookFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("V")
parameters("Z")
custom { methodDef, classDef ->
methodDef.name == "fullscreenButtonVisibilityChanged" &&
classDef.type == "Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;"
classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}

View File

@@ -189,13 +189,18 @@ fun injectVisibilityCheckCall(descriptor: String) {
"invoke-static { p1 , p2 }, $descriptor->changeVisibility(ZZ)V",
)
if (!visibilityImmediateCallbacksExistModified) {
visibilityImmediateCallbacksExistModified = true
visibilityImmediateCallbacksExistMethod.returnEarly(true)
}
visibilityImmediateMethod.addInstruction(
visibilityImmediateInsertIndex++,
"invoke-static { p0 }, $descriptor->changeVisibilityImmediate(Z)V",
)
}
private const val EXTENSION_CLASS_DESCRIPTOR =
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/PlayerControlsPatch;"
private lateinit var inflateTopControlMethod: MutableMethod
@@ -209,6 +214,9 @@ private var inflateBottomControlRegister: Int = -1
private lateinit var visibilityMethod: MutableMethod
private var visibilityInsertIndex: Int = 0
private var visibilityImmediateCallbacksExistModified = false
private lateinit var visibilityImmediateCallbacksExistMethod : MutableMethod
private lateinit var visibilityImmediateMethod: MutableMethod
private var visibilityImmediateInsertIndex: Int = 0
@@ -266,6 +274,7 @@ val playerControlsPatch = bytecodePatch(
)
}
visibilityImmediateCallbacksExistMethod = playerControlsExtensionHookListenersExistFingerprint.method
visibilityImmediateMethod = playerControlsExtensionHookFingerprint.method
// A/B test for a slightly different bottom overlay controls,

View File

@@ -41,6 +41,8 @@ var is_19_46_or_greater = false
private set
var is_19_47_or_greater = false
private set
var is_19_49_or_greater = false
private set
val versionCheckPatch = resourcePatch(
description = "Uses the Play Store service version to find the major/minor version of the YouTube target app.",
@@ -74,5 +76,6 @@ val versionCheckPatch = resourcePatch(
is_19_43_or_greater = 244405000 <= playStoreServicesVersion
is_19_46_or_greater = 244705000 <= playStoreServicesVersion
is_19_47_or_greater = 244799000 <= playStoreServicesVersion
is_19_49_or_greater = 245005000 <= playStoreServicesVersion
}
}

View File

@@ -21,3 +21,14 @@ internal val setThemeFingerprint = fingerprint {
opcodes(Opcode.RETURN_OBJECT)
literal { appearanceStringId }
}
/**
* Added in YouTube v19.04.38.
*/
internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
internal val cairoFragmentConfigFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
literal { CAIRO_CONFIG_LITERAL_VALUE }
}

View File

@@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -17,10 +18,17 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPref
import app.revanced.patches.shared.misc.settings.settingsPatch
import app.revanced.patches.youtube.misc.check.checkEnvironmentPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.fix.cairo.disableCairoSettingsPatch
import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch
import app.revanced.patches.youtube.misc.playservice.is_19_04_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
import com.android.tools.smali.dexlib2.util.MethodUtil
// Used by a fingerprint() from SettingsPatch.
@@ -37,13 +45,28 @@ private val settingsResourcePatch = resourcePatch {
dependsOn(
resourceMappingPatch,
settingsPatch(
rootPreference = IntentPreference(
titleKey = "revanced_settings_title",
summaryKey = null,
intent = newIntent("revanced_settings_intent"),
) to "settings_fragment",
preferences,
),
listOf(
IntentPreference(
titleKey = "revanced_settings_title",
summaryKey = null,
intent = newIntent("revanced_settings_intent"),
) to "settings_fragment",
PreferenceCategory(
titleKey = "revanced_settings_title",
layout = "@layout/preference_group_title",
preferences = setOf(
IntentPreference(
titleKey = "revanced_settings_submenu_title",
summaryKey = null,
icon = "@drawable/revanced_settings_icon",
layout = "@layout/preference_with_icon",
intent = newIntent("revanced_settings_intent"),
)
)
) to "settings_fragment_cairo",
),
preferences
)
)
execute {
@@ -51,6 +74,7 @@ private val settingsResourcePatch = resourcePatch {
appearanceStringId = resourceMappings["string", "app_theme_appearance_dark"]
arrayOf(
ResourceGroup("drawable", "revanced_settings_icon.xml"),
ResourceGroup("layout", "revanced_settings_with_toolbar.xml"),
).forEach { resourceGroup ->
copyResources("settings", resourceGroup)
@@ -73,7 +97,6 @@ private val settingsResourcePatch = resourcePatch {
// Remove horizontal divider from the settings Preferences
// To better match the appearance of the stock YouTube settings.
document("res/values/styles.xml").use { document ->
arrayOf(
"Theme.YouTube.Settings",
"Theme.YouTube.Settings.Dark",
@@ -93,7 +116,6 @@ private val settingsResourcePatch = resourcePatch {
// Some devices freak out if undeclared data is passed to an intent,
// and this change appears to fix the issue.
document("AndroidManifest.xml").use { document ->
val licenseElement = document.childNodes.findElementByAttributeValueOrThrow(
"android:name",
"com.google.android.libraries.social.licenses.LicenseActivity",
@@ -117,7 +139,8 @@ val settingsPatch = bytecodePatch(
sharedExtensionPatch,
settingsResourcePatch,
addResourcesPatch,
disableCairoSettingsPatch,
versionCheckPatch,
fixPlaybackSpeedWhilePlayingPatch,
// Currently there is no easy way to make a mandatory patch,
// so for now this is a dependent of this patch.
checkEnvironmentPatch,
@@ -140,6 +163,12 @@ val settingsPatch = bytecodePatch(
selectable = true,
)
if (is_19_34_or_greater) {
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
SwitchPreference("revanced_restore_old_settings_menus")
)
}
PreferenceScreen.MISC.addPreferences(
TextPreference(
key = null,
@@ -148,6 +177,10 @@ val settingsPatch = bytecodePatch(
inputType = InputType.TEXT_MULTI_LINE,
tag = "app.revanced.extension.shared.settings.preference.ImportExportPreference",
),
ListPreference(
key = "revanced_language",
summaryKey = null
)
)
setThemeFingerprint.method.let { setThemeMethod ->
@@ -187,6 +220,40 @@ val settingsPatch = bytecodePatch(
licenseActivityOnCreateFingerprint.classDef.apply {
methods.removeIf { it.name != "onCreate" && !MethodUtil.isConstructor(it) }
}
// Add context override to force a specific settings language.
licenseActivityOnCreateFingerprint.classDef.apply {
val attachBaseContext = ImmutableMethod(
type,
"attachBaseContext",
listOf(ImmutableMethodParameter("Landroid/content/Context;", null, null)),
"V",
AccessFlags.PROTECTED.value,
null,
null,
MutableMethodImplementation(3),
).toMutable().apply {
addInstructions(
"""
invoke-static { p1 }, $activityHookClassDescriptor->getAttachBaseContext(Landroid/content/Context;)Landroid/content/Context;
move-result-object p1
invoke-super { p0, p1 }, $superclass->attachBaseContext(Landroid/content/Context;)V
return-void
"""
)
}
methods.add(attachBaseContext)
}
// Add setting to force cairo settings fragment on/off.
if (is_19_04_or_greater) {
cairoFragmentConfigFingerprint.method.insertFeatureFlagBooleanOverride(
CAIRO_CONFIG_LITERAL_VALUE,
"$activityHookClassDescriptor->useCairoSettingsFragment(Z)Z"
)
}
}
finalize {
@@ -222,17 +289,15 @@ object PreferenceScreen : BasePreferenceScreen() {
key = "revanced_settings_screen_03_feed",
summaryKey = null,
)
val PLAYER = Screen(
key = "revanced_settings_screen_04_player",
val GENERAL_LAYOUT = Screen(
key = "revanced_settings_screen_04_general",
summaryKey = null,
)
val GENERAL_LAYOUT = Screen(
key = "revanced_settings_screen_05_general",
val PLAYER = Screen(
key = "revanced_settings_screen_05_player",
summaryKey = null,
)
// Don't sort, as related preferences are scattered apart.
// Can use title sorting after PreferenceCategory support is added.
val SHORTS = Screen(
key = "revanced_settings_screen_06_shorts",
summaryKey = null,

View File

@@ -38,10 +38,23 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
preferences = setOf(
SwitchPreference("revanced_spoof_video_streams"),
ListPreference(
"revanced_spoof_video_streams_language",
summaryKey = null
"revanced_spoof_video_streams_client_type",
summaryKey = null,
),
NonInteractivePreference("revanced_spoof_video_streams_about")
NonInteractivePreference(
// Requires a key and title but the actual text is chosen at runtime.
key = "revanced_spoof_video_streams_about_android",
tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference"
),
ListPreference(
key = "revanced_spoof_video_streams_language",
summaryKey = null,
// Language strings are declared in Setting patch.
entriesKey = "revanced_language_entries",
entryValuesKey = "revanced_language_entry_values"
),
SwitchPreference("revanced_spoof_video_streams_ios_force_avc"),
SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"),
),
),
)

View File

@@ -51,7 +51,7 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
}
}
val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint {
internal val rollingNumberTextViewAnimationUpdateFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("Landroid/graphics/Bitmap;")

View File

@@ -31,8 +31,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
@Suppress("unused")
val forceOriginalAudioPatch = bytecodePatch(
name = "Force original audio",
description = "Adds an option to always use the original audio track. " +
"This patch does nothing if 'Spoof video streams' is enabled.",
description = "Adds an option to always use the original audio track.",
) {
dependsOn(
sharedExtensionPatch,
@@ -58,7 +57,10 @@ val forceOriginalAudioPatch = bytecodePatch(
addResources("youtube", "video.audio.forceOriginalAudioPatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_force_original_audio")
SwitchPreference(
key = "revanced_force_original_audio",
tag = "app.revanced.extension.youtube.settings.preference.ForceOriginalAudioSwitchPreference"
)
)
fun Method.firstFormatStreamingModelCall(

View File

@@ -0,0 +1,74 @@
package app.revanced.patches.youtube.video.hdr
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableHdrPatch;"
@Suppress("unused")
val disableHdrPatch = bytecodePatch(
name = "Disable HDR video",
description = "Adds an option to disable video HDR.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
"19.47.53",
),
)
execute {
addResources("youtube", "video.hdr.disableHdrPatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_disable_hdr_video")
)
hdrCapabilityFingerprint.let {
it.originalMethod.apply {
val stringIndex = it.stringMatches!!.first().index
val navigateIndex = indexOfFirstInstructionOrThrow(stringIndex) {
val reference = getReference<MethodReference>()
reference?.parameterTypes == listOf("I", "Landroid/view/Display;") &&
reference.returnType == "Z"
}
// Modify the HDR lookup method (Method is in the same class as the fingerprint).
navigate(this).to(navigateIndex).stop().addInstructionsWithLabels(
0,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->disableHDRVideo()Z
move-result v0
if-nez v0, :useHdr
return v0
:useHdr
nop
"""
)
}
}
}
}

View File

@@ -0,0 +1,12 @@
package app.revanced.patches.youtube.video.hdr
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val hdrCapabilityFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
strings(
"av1_profile_main_10_hdr_10_plus_supported",
"video/av01"
)
}

View File

@@ -17,22 +17,26 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.interaction.seekbar.disableFastForwardNoticeFingerprint
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.litho.filter.addLithoFilter
import app.revanced.patches.youtube.misc.litho.filter.lithoFilterPatch
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTreeHook
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.*
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableField
var speedUnavailableId = -1L
internal set
internal var speedUnavailableId = -1L
private set
private val customPlaybackSpeedResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
@@ -61,6 +65,7 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
recyclerViewTreeHookPatch,
customPlaybackSpeedResourcePatch,
addResourcesPatch,
versionCheckPatch
)
execute {
@@ -71,6 +76,12 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
TextPreference("revanced_custom_playback_speeds", inputType = InputType.TEXT_MULTI_LINE),
)
if (is_19_25_or_greater) {
PreferenceScreen.VIDEO.addPreferences(
TextPreference("revanced_speed_tap_and_hold", inputType = InputType.NUMBER_DECIMAL),
)
}
// Replace the speeds float array with custom speeds.
speedArrayGeneratorFingerprint.method.apply {
val sizeCallIndex = indexOfFirstInstructionOrThrow { getReference<MethodReference>()?.name == "size" }
@@ -166,5 +177,27 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
// endregion
// region Custom tap and hold 2x speed.
if (is_19_25_or_greater) {
disableFastForwardNoticeFingerprint.method.apply {
val index = indexOfFirstInstructionOrThrow {
(this as? NarrowLiteralInstruction)?.narrowLiteral == 2.0f.toRawBits()
}
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->tapAndHoldSpeed()F
move-result v$register
"""
)
}
}
// endregion
}
}

View File

@@ -24,31 +24,31 @@ class StringResource(
if (value.startsWith('"') && value.endsWith('"')) {
// Raw strings allow unescaped single quote but not double quote.
if (!value.substring(1, value.length - 1).contains(Regex("(?<!\\\\)[\"]"))) {
return this;
return this
}
} else {
if (value.contains('\n')) {
// Don't throw an exception, otherwise unnoticed mistakes
// in Crowdin can cause patching failures.
// Incorrectly escaped strings still work but do not display as intended.
Logger.getLogger(StringResource.javaClass.name).severe(
Logger.getLogger(StringResource.javaClass.name).warning(
"String $name is not raw but contains encoded new line characters: $value")
}
if (!value.contains(Regex("(?<!\\\\)['\"]"))) {
return this;
return this
}
}
Logger.getLogger(StringResource.javaClass.name).severe(
Logger.getLogger(StringResource.javaClass.name).warning(
"String $name cannot contain unescaped quotes in value: $value")
return this;
return this
}
// if the string is un-formatted, explicitly add the formatted attribute
if (!formatted) setAttribute("formatted", "false")
textContent = value.validateAndroidStringEscaping();
textContent = value.validateAndroidStringEscaping()
}
companion object {

View File

@@ -136,6 +136,8 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
@@ -155,6 +157,8 @@ Second \"item\" text"</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is 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 -->
@@ -168,8 +172,6 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
</patch>
<patch id="layout.theme.themePatch">
@@ -218,6 +220,7 @@ Second \"item\" text"</string>
<patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
<!-- 'no auth' means no authentication -->
</patch>
</app>
<app id="twitch">

View File

@@ -136,6 +136,8 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
@@ -155,6 +157,8 @@ Second \"item\" text"</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is 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 -->
@@ -168,8 +172,6 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
</patch>
<patch id="layout.theme.themePatch">
@@ -218,6 +220,7 @@ Second \"item\" text"</string>
<patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
<!-- 'no auth' means no authentication -->
</patch>
</app>
<app id="twitch">

View File

@@ -43,6 +43,62 @@ Second \"item\" text"</string>
<string name="revanced_settings_import_reset">ØĨؚاد؊ ØĒØšŲŠŲŠŲ† ØĨؚداداØĒ ReVanced ØĨŲ„Ų‰ Ø§Ų„ŲˆØļØš Ø§Ų„Ø§ŲØĒØąØ§Øļ؊</string>
<string name="revanced_settings_import_success">ØĒŲ… Ø§ØŗØĒŲŠØąØ§Ø¯ %d ØĨؚداداØĒ</string>
<string name="revanced_settings_import_failure_parse">ŲØ´Ų„ Ø§Ų„Ø§ØŗØĒŲŠØąØ§Ø¯: %s</string>
<string name="revanced_language_title">Ų„ØēØŠ ReVanced</string>
<string name="revanced_language_user_dialog_message">"Ų‚Ø¯ ØĒŲƒŲˆŲ† Ø§Ų„ØĒØąØŦŲ…Ø§ØĒ Ų„Ø¨ØšØļ Ø§Ų„Ų„ØēاØĒ Ų…ŲŲ‚ŲˆØ¯ØŠ ØŖŲˆ ØēŲŠØą Ų…ŲƒØĒŲ…Ų„ØŠ.
Ų„ØĒØąØŦŲ…ØŠ Ų„ØēاØĒ ØŦØ¯ŲŠØ¯ØŠØŒ ØĒ؁ØļŲ„ Ø¨Ø˛ŲŠØ§ØąØŠ translate.revanced.app"</string>
<string name="revanced_language_DEFAULT">Ų„ØēØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚</string>
<string name="revanced_language_AR">Ø§Ų„ØšØąØ¨ŲŠØŠ</string>
<string name="revanced_language_AZ">Azerbaijani</string>
<string name="revanced_language_BG">Bulgarian</string>
<string name="revanced_language_BN">Bengali</string>
<string name="revanced_language_CA">Catalan</string>
<string name="revanced_language_CS">Czech</string>
<string name="revanced_language_DA">Danish</string>
<string name="revanced_language_DE">German</string>
<string name="revanced_language_EL">Greek</string>
<string name="revanced_language_EN">English</string>
<string name="revanced_language_ES">Spanish</string>
<string name="revanced_language_ET">Estonian</string>
<string name="revanced_language_FA">ŲØ§ØąØŗŲ‰</string>
<string name="revanced_language_FI">Finnish</string>
<string name="revanced_language_FR">French - Français</string>
<string name="revanced_language_GU">Gujarati</string>
<string name="revanced_language_HI">Hindi</string>
<string name="revanced_language_HR">Croatian</string>
<string name="revanced_language_HU">Hungarian</string>
<string name="revanced_language_ID">Indonesian</string>
<string name="revanced_language_IT">Italian</string>
<string name="revanced_language_JA">Japanese</string>
<string name="revanced_language_KK">Kazakh</string>
<string name="revanced_language_KO">Korean</string>
<string name="revanced_language_LT">Lithuanian</string>
<string name="revanced_language_LV">Latvian</string>
<string name="revanced_language_MK">Macedonian</string>
<string name="revanced_language_MN">Mongolian</string>
<string name="revanced_language_MR">Marathi</string>
<string name="revanced_language_MS">Malay</string>
<string name="revanced_language_MY">Burmese</string>
<string name="revanced_language_NL">Dutch</string>
<string name="revanced_language_OR">Odia</string>
<string name="revanced_language_PA">Punjabi</string>
<string name="revanced_language_PL">Polish</string>
<string name="revanced_language_PT">Portugese</string>
<string name="revanced_language_RO">Romanian</string>
<string name="revanced_language_RU">Russian - Đ ŅƒŅŅĐēиК</string>
<string name="revanced_language_SK">Slovak</string>
<string name="revanced_language_SL">Slovene</string>
<string name="revanced_language_SR">Serbian</string>
<string name="revanced_language_SV">Swedish</string>
<string name="revanced_language_SW">Swahili</string>
<string name="revanced_language_TA">Tamil</string>
<string name="revanced_language_TE">Telugu</string>
<string name="revanced_language_TH">Thai</string>
<string name="revanced_language_TR">Turkish</string>
<string name="revanced_language_UK">Ukrainian</string>
<string name="revanced_language_UR">Urdu</string>
<string name="revanced_language_VI">Vietnamese</string>
<string name="revanced_language_ZH">Chinese</string>
<string name="revanced_pref_import_export_title">Ø§ØŗØĒŲŠØąØ§Ø¯ / ØĒØĩØ¯ŲŠØą</string>
<string name="revanced_pref_import_export_summary">Ø§ØŗØĒŲŠØąØ§Ø¯ / ØĒØĩØ¯ŲŠØą ØĨؚداداØĒ ReVanced</string>
<!-- Settings about dialog. -->
@@ -77,12 +133,13 @@ Second \"item\" text"</string>
<string name="revanced_settings_screen_01_ads_title">Ø§Ų„ØĨØšŲ„Ø§Ų†Ø§ØĒ</string>
<string name="revanced_settings_screen_02_alt_thumbnails_title">Ų…ŲØĩØēŲ‘ŲŽØąØ§ØĒ ŲŲŠØ¯ŲŠŲˆ Ø¨Ø¯ŲŠŲ„ØŠ</string>
<string name="revanced_settings_screen_03_feed_title">Ø§Ų„Ų…ŲˆØŦØ˛</string>
<string name="revanced_settings_screen_04_player_title">Ø§Ų„Ų…Ø´ØēŲ„</string>
<string name="revanced_settings_screen_05_general_title">Ø§Ų„ØĒØĩŲ…ŲŠŲ… Ø§Ų„ØšØ§Ų…</string>
<string name="revanced_settings_screen_04_general_title">ØšØ§Ų…</string>
<string name="revanced_settings_screen_05_player_title">Ų…Ø´ØēŲ„</string>
<string name="revanced_settings_screen_07_seekbar_title">Ø´ØąŲŠØˇ ØĒŲ‚Ø¯Ų… Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_settings_screen_08_swipe_controls_title">Ø§Ų„ØĒØ­ŲƒŲ… ØšŲ† ØˇØąŲŠŲ‚ ØĨŲŠŲ…Ø§ØĄØŠ Ø§Ų„ØĒŲ…ØąŲŠØą</string>
<string name="revanced_settings_screen_11_misc_title">ØĨؚداداØĒ Ų…ØĒŲ†ŲˆØšØŠ</string>
<string name="revanced_settings_screen_11_misc_title">Ų…ØĒŲ†ŲˆØš</string>
<string name="revanced_settings_screen_12_video_title">Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_restore_old_settings_menus_title">ØĨؚاد؊ Ų‚ŲˆØ§ØĻŲ… ØĨؚداداØĒ Ų‚Ø¯ŲŠŲ…ØŠ</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">ØĒØšØˇŲŠŲ„ ØĒØ´ØēŲŠŲ„ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ</string>
@@ -138,12 +195,12 @@ Second \"item\" text"</string>
<string name="revanced_hide_join_membership_button_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„Ø˛Øą</string>
<string name="revanced_hide_join_membership_button_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„Ø˛Øą</string>
<!-- 'For you' should be translated using the same localized wording YouTube displays. -->
<string name="revanced_hide_for_you_shelf_title">ØĨØŽŲØ§ØĄ ØąŲ \"Ų…Ų† ØŖØŦŲ„Ųƒ\" ؁؊ ØĩŲØ­ØŠ Ø§Ų„Ų‚Ų†Ø§ØŠ</string>
<string name="revanced_hide_for_you_shelf_title">ØĨØŽŲØ§ØĄ ØąŲ \"Ų„Ų€Ųƒ\" ؁؊ ØĩŲØ­ØŠ Ø§Ų„Ų‚Ų†Ø§ØŠ</string>
<string name="revanced_hide_for_you_shelf_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„ØąŲ</string>
<string name="revanced_hide_for_you_shelf_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„ØąŲ</string>
<!-- 'Notify me' should be translated using the same localized wording YouTube displays.
This item appear in the subscription feed for future livestreams or unreleased videos. -->
<string name="revanced_hide_notify_me_button_title">ØĨØŽŲØ§ØĄ Ø˛Øą \'Ų†Ø¨Ų‡Ų†ŲŠ\'</string>
<string name="revanced_hide_notify_me_button_title">ØĨØŽŲØ§ØĄ Ø˛Øą \'ØĒŲ†Ø¨ŲŠŲ‡ŲŠ\'</string>
<string name="revanced_hide_notify_me_button_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„Ø˛Øą</string>
<string name="revanced_hide_notify_me_button_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„Ø˛Øą</string>
<!-- 'People also watch' should be translated using the same localized wording YouTube displays. -->
@@ -164,9 +221,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_chips_shelf_title">ØĨØŽŲØ§ØĄ ØąŲ Ø§Ų„Ø´ØąØ§ØĻØ­</string>
<string name="revanced_hide_chips_shelf_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ ØąŲ Ø§Ų„Ø´ØąØ§ØĻØ­</string>
<string name="revanced_hide_chips_shelf_summary_off">؊ØĒŲ… ØšØąØļ ØąŲ Ø§Ų„Ø´ØąØ§ØĻØ­</string>
<string name="revanced_hide_expandable_chip_title">ØĨØŽŲØ§ØĄ Ø§Ų„Ø´ØąŲŠØ­ØŠ Ø§Ų„Ų‚Ø§Ø¨Ų„ØŠ Ų„Ų„ØĒŲˆØŗŲŠØš ØĒØ­ØĒ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_expandable_chip_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„ØąŲ‚Ø§ØĻŲ‚ Ø§Ų„Ų‚Ø§Ø¨Ų„ØŠ Ų„Ų„ØĒŲˆØŗŲŠØš</string>
<string name="revanced_hide_expandable_chip_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„ØąŲ‚Ø§ØĻŲ‚ Ø§Ų„Ų‚Ø§Ø¨Ų„ØŠ Ų„Ų„ØĒŲˆØŗŲŠØš</string>
<string name="revanced_hide_expandable_chip_title">ØĨØŽŲØ§ØĄ Ø§Ų„Ø´ØąŲŠØ­ØŠ Ø§Ų„Ų‚Ø§Ø¨Ų„ØŠ Ų„Ų„ØĒŲˆØŗŲŠØš ØĒØ­ØĒ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ</string>
<string name="revanced_hide_expandable_chip_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„Ø´ØąØ§ØĻØ­ Ø§Ų„Ų‚Ø§Ø¨Ų„ØŠ Ų„Ų„ØĒŲˆØŗŲŠØš</string>
<string name="revanced_hide_expandable_chip_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„Ø´ØąØ§ØĻØ­ Ø§Ų„Ų‚Ø§Ø¨Ų„ØŠ Ų„Ų„ØĒŲˆØŗŲŠØš</string>
<string name="revanced_hide_community_posts_title">ØĨØŽŲØ§ØĄ Ų…Ø´Ø§ØąŲƒØ§ØĒ Ø§Ų„Ų…ØŦØĒŲ…Øš</string>
<string name="revanced_hide_community_posts_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ų…Ø´Ø§ØąŲƒØ§ØĒ Ø§Ų„Ų…ØŦØĒŲ…Øš</string>
<string name="revanced_hide_community_posts_summary_off">؊ØĒŲ… ØšØąØļ Ų…Ø´Ø§ØąŲƒØ§ØĒ Ø§Ų„Ų…ØŦØĒŲ…Øš</string>
@@ -207,8 +264,8 @@ Second \"item\" text"</string>
<string name="revanced_hide_quick_actions_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„ØĨØŦØąØ§ØĄØ§ØĒ Ø§Ų„ØŗØąŲŠØšØŠ</string>
<string name="revanced_hide_quick_actions_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„ØĨØŦØąØ§ØĄØ§ØĒ Ø§Ų„ØŗØąŲŠØšØŠ</string>
<string name="revanced_hide_related_videos_title">ØĨØŽŲØ§ØĄ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ذاØĒ Ø§Ų„ØĩŲ„ØŠ ؁؊ Ø§Ų„ØĨØŦØąØ§ØĄØ§ØĒ Ø§Ų„ØŗØąŲŠØšØŠ</string>
<string name="revanced_hide_related_videos_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_related_videos_summary_off">؊ØĒŲ… ØšØąØļ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_related_videos_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_related_videos_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_image_shelf_title">ØĨØŽŲØ§ØĄ ØąŲŲˆŲ Ø§Ų„ØĩŲˆØą ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_image_shelf_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ ØąŲŲˆŲ Ø§Ų„ØĩŲˆØąØŠ</string>
<string name="revanced_hide_image_shelf_summary_off">؊ØĒŲ… ØšØąØļ ØąŲŲˆŲ Ø§Ų„ØĩŲˆØąØŠ</string>
@@ -242,7 +299,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_description_components_screen_title">؈Øĩ؁ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_description_components_screen_summary">ØĨØŽŲØ§ØĄ ØŖŲˆ ØšØąØļ Ų…ŲƒŲˆŲ†Ø§ØĒ ؈Øĩ؁ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_filter_bar_screen_title">Ø´ØąŲŠØˇ Ø§Ų„ØĒØĩŲŲŠØŠ</string>
<string name="revanced_hide_filter_bar_screen_summary">ØĨØŽŲØ§ØĄ Ø´ØąŲŠØˇ Ø§Ų„ØĒØĩŲŲŠØŠ ØŖŲˆ ØšØąØļŲ‡ ؁؊ Ø§Ų„Ų…ŲˆØŦØ˛ ŲˆØ§Ų„Ø¨Ø­ØĢ ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_filter_bar_screen_summary">ØĨØŽŲØ§ØĄ Ø´ØąŲŠØˇ Ø§Ų„ØĒØĩŲŲŠØŠ ØŖŲˆ ØšØąØļŲ‡ ؁؊ Ø§Ų„Ų…ŲˆØŦØ˛ ŲˆØ§Ų„Ø¨Ø­ØĢ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_filter_bar_feed_in_feed_title">ØĨØŽŲØ§ØĄ ؁؊ Ø§Ų„Ų…ŲˆØŦØ˛</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_on">Ų…ØŽŲŲŠ ؁؊ Ø§Ų„Ų…ŲˆØŦØ˛</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_off">ŲŠØšØąØļ ؁؊ Ø§Ų„Ų…ŲˆØŦØ˛</string>
@@ -293,10 +350,10 @@ Second \"item\" text"</string>
<string name="revanced_custom_filter_toast_invalid_syntax">؁؄ØĒØą Ų…ØŽØĩØĩ ØēŲŠØą ØĩØ§Ų„Ø­: %s</string>
<string name="revanced_hide_keyword_content_screen_title">ØĨØŽŲØ§ØĄ Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ</string>
<string name="revanced_hide_keyword_content_screen_summary">ØĨØŽŲØ§ØĄ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ø§Ų„Ø¨Ø­ØĢ ŲˆØ§Ų„Ų…ŲˆØŦØ˛ Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų… ŲŲ„Ø§ØĒØą Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_home_title">ØĨØŽŲØ§ØĄ Ų…Ų‚Ø§ØˇØš ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ Ø¨ŲˆØ§ØŗØˇØŠ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_home_summary_on">ØĒØĒŲ… ØĒØĩŲŲŠØŠ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ؁؊ ØšŲ„Ø§Ų…ØŠ Ø§Ų„ØĒØ¨ŲˆŲŠØ¨ \"Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ\" Ø­ØŗØ¨ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_home_summary_off">Ų„Ø§ ØĒØĒŲ… ØĒØĩŲŲŠØŠ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ؁؊ ØšŲ„Ø§Ų…ØŠ Ø§Ų„ØĒØ¨ŲˆŲŠØ¨ \"Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ\" Ø­ØŗØ¨ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_subscriptions_title">ØĨØŽŲØ§ØĄ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ø§Ų„Ø§Ø´ØĒØąØ§Ųƒ ØšŲ† ØˇØąŲŠŲ‚ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_home_title">ØĨØŽŲØ§ØĄ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ Ø¨ŲˆØ§ØŗØˇØŠ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_home_summary_on">ØĒØĒŲ… ØĒØĩŲŲŠØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ؁؊ ØšŲ„Ø§Ų…ØŠ Ø§Ų„ØĒØ¨ŲˆŲŠØ¨ \"Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ\" Ø­ØŗØ¨ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_home_summary_off">Ų„Ø§ ØĒØĒŲ… ØĒØĩŲŲŠØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ؁؊ ØšŲ„Ø§Ų…ØŠ Ø§Ų„ØĒØ¨ŲˆŲŠØ¨ \"Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ\" Ø­ØŗØ¨ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_subscriptions_title">ØĨØŽŲØ§ØĄ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ø§Ų„ØŽØ§ØĩØŠ Ø¨Ø§Ų„Ø§Ø´ØĒØąØ§Ųƒ ØšŲ† ØˇØąŲŠŲ‚ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_on">؊ØĒŲ… ØĒØĩŲŲŠØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ؁؊ ØšŲ„Ø§Ų…ØŠ Ø§Ų„ØĒØ¨ŲˆŲŠØ¨ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ Ø­ØŗØ¨ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_subscriptions_summary_off">Ų„Ø§ ؊ØĒŲ… ØĒØĩŲŲŠØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ؁؊ ØšŲ„Ø§Ų…ØŠ Ø§Ų„ØĒØ¨ŲˆŲŠØ¨ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ Ø­ØŗØ¨ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
<string name="revanced_hide_keyword_content_search_title">ØĨØŽŲØ§ØĄ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ ØšŲ† ØˇØąŲŠŲ‚ Ø§Ų„ŲƒŲ„Ų…Ø§ØĒ Ø§Ų„Ų…ŲØĒØ§Ø­ŲŠØŠ</string>
@@ -445,6 +502,9 @@ Second \"item\" text"</string>
<string name="revanced_swipe_overlay_background_alpha_summary">Ų‚ŲŠŲ…ØŠ Ø´ŲØ§ŲŲŠØŠ ØŽŲ„ŲŲŠØŠ ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„ØĒŲ…ØąŲŠØą</string>
<string name="revanced_swipe_threshold_title">Ų…Ų‚Ø¯Ø§Øą حد Ø§Ų„ØĒŲ…ØąŲŠØą</string>
<string name="revanced_swipe_threshold_summary">Ø§Ų„Ø­Ø¯ Ø§Ų„ØŖØ¯Ų†Ų‰ Ų…Ų† Ø§Ų„ØĒŲ…ØąŲŠØą Ų‚Ø¨Ų„ Ø§ŲƒØĒØ´Ø§Ų Ø§Ų„ØĨŲŠŲ…Ø§ØĄØŠ</string>
<string name="revanced_swipe_change_video_title">ØĒŲ…ŲƒŲŠŲ† ØĨŲŠŲ…Ø§ØĄØŠ Ø§Ų„ØĒŲ…ØąŲŠØą Ų„ØĒØēŲŠŲŠØą Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_swipe_change_video_summary_on">ØŗŲŠØ¤Ø¯ŲŠ Ø§Ų„ØĒŲ…ØąŲŠØą ؁؊ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ ØĨŲ„Ų‰ Ø§Ų„ØĒØēŲŠŲŠØą Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĒØ§Ų„ŲŠ/Ø§Ų„ØŗØ§Ø¨Ų‚</string>
<string name="revanced_swipe_change_video_summary_off">Ų„Ų† ŲŠØ¤Ø¯ŲŠ Ø§Ų„ØĒŲ…ØąŲŠØą ؁؊ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ ØĨŲ„Ų‰ Ø§Ų„ØĒØēŲŠŲŠØą Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĒØ§Ų„ŲŠ/Ø§Ų„ØŗØ§Ø¨Ų‚</string>
</patch>
<patch id="layout.autocaptions.autoCaptionsPatch">
<string name="revanced_auto_captions_title">ØĒØšØˇŲŠŲ„ Ø§Ų„ØĒŲ‘ŲŽØąŲ’ØŦŲŽŲ…ŲŽØŠ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻŲŠØŠ</string>
@@ -516,14 +576,14 @@ Second \"item\" text"</string>
<string name="revanced_hide_navigation_button_labels_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„ØĒØŗŲ…ŲŠØ§ØĒ</string>
<string name="revanced_hide_navigation_button_labels_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„ØĒØŗŲ…ŲŠØ§ØĒ</string>
<string name="revanced_disable_translucent_status_bar_title">ØĒØšØˇŲŠŲ„ Ø´ØąŲŠØˇ Ø§Ų„Ø­Ø§Ų„ØŠ Ø§Ų„Ø´ŲØ§Ų</string>
<string name="revanced_disable_translucent_status_bar_summary_on">Ø´ØąŲŠØˇ Ø§Ų„Ø­Ø§Ų„ØŠ ØēŲŠØą Ų…ØšØĒŲ…ØŠ</string>
<string name="revanced_disable_translucent_status_bar_summary_off">Ø´ØąŲŠØˇ Ø§Ų„Ø­Ø§Ų„ØŠ ØēŲŠØą Ø´ŲØ§ŲØŠ ØŖŲˆ ØšŲ…ŲŠŲ‚ØŠ</string>
<string name="revanced_disable_translucent_navigation_bar_light_title">ØĒØšØˇŲŠŲ„ Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„ Ø§Ų„Ø´ŲØ§Ų Ø§Ų„ŲØ§ØĒØ­</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„ ؁؊ Ø§Ų„ŲˆØļØš Ø§Ų„ŲØ§ØĒØ­ Ų…ØšØĒŲ…</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">ŲŠŲƒŲˆŲ† Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„ ؁؊ Ø§Ų„ŲˆØļØš Ø§Ų„ŲØ§ØĒØ­ Ų…ØšØĒŲ…Ų‹Ø§ ØŖŲˆ Ų†Øĩ؁ Ø´ŲØ§Ų</string>
<string name="revanced_disable_translucent_navigation_bar_dark_title">ØĒØšØˇŲŠŲ„ Ø§Ų„Ø´ØąŲŠØˇ Ø§Ų„Ø¯Ø§ŲƒŲ†ØŠ</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_on">Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„ ؁؊ Ø§Ų„ŲˆØļØš Ø§Ų„Ø¯Ø§ŲƒŲ† Ų…ØšØĒŲ…</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_off">Ø´ØąŲŠØˇ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„Ų…ØĩŲˆØąŲŠ Ø§Ų„Ø´ŲØ§Ų ØēŲŠØą ØšŲ…ŲŠŲ‚ØŠ ØŖŲˆ ØšŲ…ŲŠŲ‚ØŠ</string>
<string name="revanced_disable_translucent_status_bar_summary_on">Ø´ØąŲŠØˇ Ø§Ų„Ø­Ø§Ų„ØŠ ØēŲŠØą Ø´ŲØ§Ų</string>
<string name="revanced_disable_translucent_status_bar_summary_off">Ø´ØąŲŠØˇ Ø§Ų„Ø­Ø§Ų„ØŠ Ų…ØšØĒŲ… ØŖŲˆ Ø´ŲØ§Ų</string>
<string name="revanced_disable_translucent_navigation_bar_light_title">ØĒØšØˇŲŠŲ„ Ø§Ų„Ø´ØąŲŠØˇ Ø§Ų„Ø´ŲØ§Ų Ø§Ų„ŲØ§ØĒØ­</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„ ؁؊ Ø§Ų„ŲˆØļØš Ø§Ų„ŲØ§ØĒØ­ ØēŲŠØą Ø´ŲØ§Ų</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„ ؁؊ Ø§Ų„ŲˆØļØš Ø§Ų„ŲØ§ØĒØ­ Ų…ØšØĒŲ… Ø§Ųˆ Ø´ŲØ§Ų</string>
<string name="revanced_disable_translucent_navigation_bar_dark_title">ØĒØšØˇŲŠŲ„ Ø§Ų„Ø´ØąŲŠØˇ Ø§Ų„Ø´ŲØ§Ų Ø§Ų„Ø¯Ø§ŲƒŲ†</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_on">Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„ ؁؊ Ø§Ų„ŲˆØļØš Ø§Ų„Ø¯Ø§ŲƒŲ† ØēŲŠØą Ø´ŲØ§Ų</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_off">Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ†Ų‚Ų„ ؁؊ Ø§Ų„ŲˆØļØš Ø§Ų„Ø¯Ø§ŲƒŲ† Ų…ØšØĒŲ… Ø§Ųˆ Ø´ŲØ§Ų</string>
</patch>
<patch id="layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch">
<string name="revanced_hide_player_flyout_title">Ø§Ų„Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ų…Ų†Ø¨ØĢŲ‚ØŠ</string>
@@ -628,15 +688,18 @@ Second \"item\" text"</string>
<string name="revanced_shorts_player_screen_summary">ØĨØŽŲØ§ØĄ ØŖŲˆ ØšØąØļ Ø§Ų„Ų…ŲƒŲˆŲ†Ø§ØĒ ؁؊ Ų…Ø´ØēŲ„ Shorts</string>
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">ØĨØŽŲØ§ØĄ Shorts ؁؊ Ų…ŲˆØŦØ˛ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ</string>
<string name="revanced_hide_shorts_home_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Shorts ؁؊ Ų…ŲˆØŦØ˛ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ</string>
<string name="revanced_hide_shorts_home_summary_off">؊ØĒŲ… ØšØąØļ Shorts ؁؊ Ų…ŲˆØŦØ˛ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ</string>
<string name="revanced_hide_shorts_home_summary_on">Ų…ØŽŲŲŠØŠ ؁؊ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ ŲˆØ§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_shorts_home_summary_off">ØĒØšØąØļ ؁؊ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ ŲˆØ§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_hide_shorts_subscriptions_title">ØĨØŽŲØ§ØĄ Shorts ؁؊ Ų…ŲˆØŦØ˛ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Shorts ؁؊ Ų…ŲˆØŦØ˛ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">؊ØĒŲ… ØšØąØļ Shorts ؁؊ Ų…ŲˆØŦØ˛ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Ų…ØŽŲŲŠØŠ ؁؊ Ų…ŲˆØŦØ˛ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">ØĒØšØąØļ ؁؊ Ų…ŲˆØŦØ˛ Ø§Ų„Ø§Ø´ØĒØąØ§ŲƒØ§ØĒ</string>
<string name="revanced_hide_shorts_search_title">ØĨØŽŲØ§ØĄ Shorts ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_shorts_search_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Shorts ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_shorts_search_summary_off">؊ØĒŲ… ØšØąØļ Shorts ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_shorts_search_summary_on">Ų…ØŽŲŲŠØŠ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_shorts_search_summary_off">ØĒØšØąØļ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_shorts_history_title">ØĨØŽŲØ§ØĄ Shorts ؁؊ ØŗØŦŲ„ Ø§Ų„Ų…Ø´Ø§Ų‡Ø¯ØŠ</string>
<string name="revanced_hide_shorts_history_summary_on">Ų…ØŽŲŲŠØŠ ؁؊ ØŗØŦŲ„ Ø§Ų„Ų…Ø´Ø§Ų‡Ø¯ØŠ</string>
<string name="revanced_hide_shorts_history_summary_off">ØĒØšØąØļ ؁؊ ØŗØŦŲ„ Ø§Ų„Ų…Ø´Ø§Ų‡Ø¯ØŠ</string>
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_join_button_title">ØĨØŽŲØ§ØĄ Ø˛Øą Ø§Ų„Ø§Ų†ØļŲ…Ø§Ų…</string>
<string name="revanced_hide_shorts_join_button_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø˛Øą Ø§Ų„Ø§Ų†ØļŲ…Ø§Ų…</string>
@@ -738,8 +801,15 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_popup_panels_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ų„ŲˆØ­Ø§ØĒ Ø§Ų„Ų…Ø´ØēŲ„ Ø§Ų„Ų…Ų†Ø¨ØĢŲ‚ØŠ</string>
<string name="revanced_hide_player_popup_panels_summary_off">؊ØĒŲ… ØšØąØļ Ų„ŲˆØ­Ø§ØĒ Ø§Ų„Ų…Ø´ØēŲ„ Ø§Ų„Ų…Ų†Ø¨ØĢŲ‚ØŠ</string>
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
<string name="revanced_exit_fullscreen_title">Ø§Ų„ØŽØąŲˆØŦ Ų…Ų† ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ ØšŲ†Ø¯ Ø§Ų†ØĒŲ‡Ø§ØĄ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_exit_fullscreen_entry_1">Ų…ØšØˇŲ„</string>
<string name="revanced_exit_fullscreen_entry_2">Ø¨Ø§Ų„ØˇŲˆŲ„</string>
<string name="revanced_exit_fullscreen_entry_3">Ø¨Ø§Ų„ØšØąØļ</string>
<string name="revanced_exit_fullscreen_entry_4">Ø¨Ø§Ų„ØˇŲˆŲ„ ŲˆØ§Ų„ØšØąØļ</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">؁ØĒØ­ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ؁؊ Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ</string>
<string name="revanced_open_videos_fullscreen_portrait_title">؁ØĒØ­ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ؁؊ Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">؊ØĒŲ… ؁ØĒØ­ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ؁؊ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_off">Ų„Ø§ ؊ØĒŲ… ؁ØĒØ­ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ؁؊ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ</string>
</patch>
@@ -758,12 +828,12 @@ Second \"item\" text"</string>
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">ØŖØšØ¯ ØĒØ­Ų…ŲŠŲ„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų„Ų„ØĒØĩ؈؊ØĒ Ø¨Ų€ Return YouTube Dislike</string>
<string name="revanced_ryd_enable_summary_on">؊ØĒŲ… ØšØąØļ Ų„Ų… ŲŠØšØŦØ¨Ų†ŲŠ</string>
<string name="revanced_ryd_enable_summary_off">Ų„Ø§ ؊ØĒŲ… ØšØąØļ Ų„Ų… ŲŠØšØŦØ¨Ų†ŲŠ</string>
<string name="revanced_ryd_shorts_title">ØšØąØļ Ų„Ų… ŲŠØšØŦŲ†ŲŠ ؁؊ Ų…Ų‚Ø§ØˇØš Shorts</string>
<string name="revanced_ryd_shorts_summary_on">؊ØĒŲ… ØšØąØļ Ų„Ų… ŲŠØšØŦŲ†ŲŠ ؁؊ Ų…Ų‚Ø§ØˇØš Shorts</string>
<string name="revanced_ryd_shorts_title">ØšØąØļ Ų„Ų… ŲŠØšØŦŲ†ŲŠ ؁؊ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts</string>
<string name="revanced_ryd_shorts_summary_on">؊ØĒŲ… ØšØąØļ Ų„Ų… ŲŠØšØŦŲ†ŲŠ ؁؊ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">"ØĨØ¨Ø¯Ø§ØĄØ§ØĒ Ų„Ų… ŲŠØšØŦØ¨Ų†ŲŠ Ø§Ų„ØĒ؊ ØĒØ¸Ų‡Øą ØšŲ„Ų‰ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts
Ø§Ų„ØĒŲ‚ŲŠŲŠØ¯: Ų‚Ø¯ Ų„Ø§ ØĒØ¸Ų‡Øą ØĨØ¨Ø¯Ø§ØĄØ§ØĒ Ų„Ų… ŲŠØšØŦØ¨Ų†ŲŠ ؁؊ ؈ØļØš Ø§Ų„ØĒØĩŲØ­ Ø§Ų„Ų…ØĒØŽŲŲŠ"</string>
<string name="revanced_ryd_shorts_summary_off">ØĒŲ… ØĨØŽŲØ§ØĄ Ų„Ų… ŲŠØšØŦŲ†ŲŠ ؁؊ Ų…Ų‚Ø§ØˇØš Shorts</string>
<string name="revanced_ryd_shorts_summary_off">ØĒŲ… ØĨØŽŲØ§ØĄ Ų„Ų… ŲŠØšØŦŲ†ŲŠ ؁؊ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts</string>
<string name="revanced_ryd_dislike_percentage_title">Ų„Ų… ŲŠØšØŦØ¨Ų†ŲŠ ŲƒŲ€Ų€ Ų†ØŗØ¨ØŠ Ų…ØĻŲˆŲŠØŠ</string>
<string name="revanced_ryd_dislike_percentage_summary_on">ŲŠØšØąØļ ؚدد Ų„Ų… ŲŠØšØŦØ¨Ų†ŲŠ ŲƒŲ€ Ų†ØŗØ¨ØŠ Ų…ØĻŲˆŲŠØŠ</string>
<string name="revanced_ryd_dislike_percentage_summary_off">ŲŠØšØąØļ ؚدد Ų„Ų… ŲŠØšØŦØ¨Ų†ŲŠ ŲƒŲ€ ØąŲŽŲ‚ŲŽŲ…</string>
@@ -816,7 +886,7 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.sponsorblock.sponsorBlockResourcePatch">
<string name="revanced_sb_enable_sb">ØĒŲ…ŲƒŲŠŲ† SponsorBlock</string>
<string name="revanced_sb_enable_sb_sum">Ų…Ø§Ų†ŲØš Ø§Ų„ØąŲØšŲŽØ§ØŠ Ų‡Ųˆ Ų†Ø¸Ø§Ų… ØŦŲ…Ø§ØšŲŠ Ų„ØĒØŽØˇŲŠ Ø§Ų„ØŖØŦØ˛Ø§ØĄ Ø§Ų„Ų…ŲŲ…ŲŲ„Ų‘ŲŽØŠ ؁؊ Ų…Ų‚Ø§ØˇØš YouTube</string>
<string name="revanced_sb_enable_sb_sum">SponsorBlock Ų…Ø§Ų†ŲØš Ø§Ų„ØąŲØšŲŽØ§ØŠ Ų‡Ųˆ Ų†Ø¸Ø§Ų… ØŦŲ…Ø§ØšŲŠ Ų„ØĒØŽØˇŲŠ Ø§Ų„ØŖØŦØ˛Ø§ØĄ Ø§Ų„Ų…ŲŲ…ŲŲ„Ų‘ŲŽØŠ ؁؊ Ų…Ų‚Ø§ØˇØš YouTube</string>
<string name="revanced_sb_appearance_category">Ø§Ų„Ų…Ø¸Ų‡Øą</string>
<string name="revanced_sb_enable_voting">ØšØąØļ Ø˛Øą Ø§Ų„ØĒØĩ؈؊ØĒ</string>
<string name="revanced_sb_enable_voting_sum_on">؊ØĒŲ… ØšØąØļ Ø˛Øą Ø§Ų„ØĒØĩ؈؊ØĒ ØšŲ„Ų‰ Ø§Ų„Ų…Ų‚ØˇØš</string>
@@ -895,7 +965,7 @@ Second \"item\" text"</string>
<string name="revanced_sb_segments_filler">ØŽØ§ØąØŦ Ø§Ų„Ų…ŲˆØļŲˆØš/Ø§Ų„Ų†ŲƒØ§ØĒ</string>
<string name="revanced_sb_segments_filler_sum">ØĒŲ… ØĨØļØ§ŲØŠ Ų…Ø´Ø§Ų‡Ø¯ Ų…Ų„ØĒŲ‚ØˇØŠ ØŽØ§ØąØŦ Ø§Ų„Ų…ŲˆØļŲˆØš ØŖŲˆ Ø§Ų„ŲŲƒØ§Ų‡ØŠ Ø§Ų„ØĒ؊ Ų„ŲŠØŗØĒ Ų…ØˇŲ„ŲˆØ¨ØŠ ؄؁؇؅ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ Ø§Ų„ØąØĻŲŠØŗŲŠ Ų„Ų„ŲŲŠØ¯ŲŠŲˆ. Ų„Ø§ ØĒØĒØļŲ…Ų† Ų…Ų‚Ø§ØˇØš ØĒŲˆŲØą ØĒŲŽØšØ¨ŲŲŠØą ØŖŲˆ ØĒŲØ§ØĩŲŠŲ„ Ø§Ų„ØŽŲ„ŲŲŠØŠ</string>
<string name="revanced_sb_segments_nomusic">Ø§Ų„Ų…ŲˆØŗŲŠŲ‚Ų‰: Ų…Ų‚ØˇØš ØēŲŠØą Ų…ŲˆØŗŲŠŲ‚ŲŠ</string>
<string name="revanced_sb_segments_nomusic_sum">ŲŲ‚Øˇ Ų„Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… ؁؊ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…ŲˆØŗŲŠŲ‚ŲŠØŠ. ØŖŲ‚ØŗØ§Ų… Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ų…ŲˆØŗŲŠŲ‚ŲŠØŠ Ø¨Ø¯ŲˆŲ† Ų…ŲˆØŗŲŠŲ‚Ų‰ØŒ ŲˆØ§Ų„ØĒ؊ Ų„Ų… ؊ØĒŲ… ØĒØēØˇŲŠØĒŲ‡Ø§ Ø¨Ø§Ų„ŲØšŲ„ Ų…Ų† Ų‚Ø¨Ų„ ؁ØĻØŠ ØŖØŽØąŲ‰</string>
<string name="revanced_sb_segments_nomusic_sum">ŲŲ‚Øˇ Ų„Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… ؁؊ Ø§Ų„Ų…Ų‚Ø§ØˇØš Ø§Ų„Ų…ŲˆØŗŲŠŲ‚ŲŠØŠ. ØŖŲ‚ØŗØ§Ų… Ø§Ų„Ų…Ų‚Ø§ØˇØš Ø§Ų„Ų…ŲˆØŗŲŠŲ‚ŲŠØŠ Ø¨Ø¯ŲˆŲ† Ų…ŲˆØŗŲŠŲ‚Ų‰ØŒ ŲˆØ§Ų„ØĒ؊ Ų„Ų… ؊ØĒŲ… ØĒØēØˇŲŠØĒŲ‡Ø§ Ø¨Ø§Ų„ŲØšŲ„ Ų…Ų† Ų‚Ø¨Ų„ ؁ØĻØŠ ØŖØŽØąŲ‰</string>
<string name="revanced_sb_skip_button_compact">ØĒØŽØˇŲŠ</string>
<string name="revanced_sb_skip_button_compact_highlight">Ø§Ų„ØŖØ¨ØąØ˛</string>
<string name="revanced_sb_skip_button_sponsor">ØĒØŽØˇŲŠ Ø§Ų„ØąØ§ØšŲŠ</string>
@@ -1006,6 +1076,23 @@ Second \"item\" text"</string>
<string name="revanced_sb_reset">ØĨؚاد؊ Ø§Ų„ØĒØšŲŠŲŠŲ†</string>
<string name="revanced_sb_about">Ų„Ų…Ø­ØŠ</string>
<string name="revanced_sb_about_api_sum">؊ØĒŲ… ØĒŲˆŲŲŠØą Ø§Ų„Ø¨ŲŠØ§Ų†Ø§ØĒ Ø¨ŲˆØ§ØŗØˇØŠ SponsorBlock API. Ø§Ų†Ų‚Øą Ų‡Ų†Ø§ Ų„Ų…ØšØąŲØŠ Ø§Ų„Ų…Ø˛ŲŠØ¯ ŲˆŲ…Ø´Ø§Ų‡Ø¯ØŠ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„Ø§ØĒ Ų„Ų…Ų†ØĩاØĒ ØŖØŽØąŲ‰</string>
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
<string name="revanced_change_form_factor_title">Ø´ŲƒŲ„ Ų†Ų…ŲˆØ°ØŦ Ø§Ų„ØĒØŽØˇŲŠØˇ</string>
<string name="revanced_change_form_factor_entry_1">Ø§Ų„Ø§ŲØĒØąØ§Øļ؊</string>
<string name="revanced_change_form_factor_entry_2">Ø§Ų„ØŦŲˆŲ‘Ø§Ų„</string>
<string name="revanced_change_form_factor_entry_3">Ø§Ų„ØŦŲ‡Ø§Ø˛ Ø§Ų„Ų„ŲˆØ­ŲŠ</string>
<string name="revanced_change_form_factor_entry_4">Automotive</string>
<string name="revanced_change_form_factor_user_dialog_message">"ØĒØĒØļŲ…Ų† Ø§Ų„ØĒØēŲŠŲŠØąØ§ØĒ:
ØĒØŽØˇŲŠØˇ Ø§Ų„ØŦŲ‡Ø§Ø˛ Ø§Ų„Ų„ŲˆØ­ŲŠ
â€ĸ ØĨØŽŲØ§ØĄ Ų…Ų†Ø´ŲˆØąØ§ØĒ Ø§Ų„Ų…ØŦØĒŲ…Øš
ØĒØŽØˇŲŠØˇ Automotive
â€ĸ ØĨØŽŲØ§ØĄ Ų‚Ø§ØĻŲ…ØŠ ØŗØŦŲ„ Ø§Ų„Ų…Ø´Ø§Ų‡Ø¯ØŠ
â€ĸ Ø§ØŗØĒؚاد؊ ØšŲ„Ø§Ų…ØŠ Ø§Ų„ØĒØ¨ŲˆŲŠØ¨ \"Ø§ØŗØĒŲƒØ´Ø§Ų\"
â€ĸ ؁ØĒØ­ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts ؁؊ Ø§Ų„Ų…Ø´ØēŲ„ Ø§Ų„ØšØ§Ø¯ŲŠ
â€ĸ ØĒŲ†Ø¸ŲŠŲ… Ø§Ų„ØŽŲ„Ø§ØĩØŠ Ø­ØŗØ¨ Ø§Ų„Ų…ŲˆØļŲˆØšØ§ØĒ ŲˆØ§Ų„Ų‚Ų†Ø§ØŠ"</string>
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<string name="revanced_spoof_app_version_title">ØŽŲØ¯Ø§Øš ØĨØĩØ¯Ø§Øą Ø§Ų„ØĒØˇØ¨ŲŠŲ‚</string>
@@ -1020,6 +1107,7 @@ Second \"item\" text"</string>
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">Ø§Ų„Ų‡Ø¯Ų Ų…Ų† ØĒØēŲŠŲŠØą ØĨØĩØ¯Ø§Øą Ø§Ų„ØĒØˇØ¨ŲŠŲ‚</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 - Ø§ØŗØĒؚاد؊ ØŖŲŠŲ‚ŲˆŲ†Ø§ØĒ Ų…Ø´ØēŲ„ Shorts Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ</string>
<string name="revanced_spoof_app_version_target_entry_2">19.26.42 - Ø§ØŗØĒؚاد؊ ØŖŲŠŲ‚ŲˆŲ†Ø§ØĒ Ø§Ų„ØĒŲ†Ų‚Ų„ ŲˆØ´ØąŲŠØˇ Ø§Ų„ØŖØ¯ŲˆØ§ØĒ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ</string>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_legacy_entry_1">18.33.40 - Ø§ØŗØĒؚاد؊ RYD ØšŲ„Ų‰ Shorts Ø¨ŲˆØļØš Ø§Ų„ØĒØŽŲŲŠ</string>
<string name="revanced_spoof_app_version_target_legacy_entry_2">18.20.39 - Ø§ØŗØĒؚاد؊ Ų‚Ø§ØĻŲ…ØŠ ØŗØąØšØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØšØąŲŠØļØŠ &amp; Ø§Ų„ØŦŲˆØ¯ØŠ</string>
@@ -1050,10 +1138,10 @@ Second \"item\" text"</string>
<string name="revanced_disable_resuming_shorts_player_summary_off">ØŗŲŠØĒŲ… Ø§ØŗØĒØĻŲ†Ø§Ų ØĒØ´ØēŲŠŲ„ Ų…Ø´ØēŲ„ Shorts ØšŲ†Ø¯ Ø¨Ø¯ØĄ ØĒØ´ØēŲŠŲ„ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚</string>
</patch>
<patch id="layout.shortsplayer.shortsPlayerTypePatch">
<string name="revanced_shorts_player_type_title">؁ØĒØ­ Ų…Ų‚Ø§ØˇØš Ø§Ų„Ų€Shorts Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų…</string>
<string name="revanced_shorts_player_type_title">؁ØĒØ­ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts Ø¨Ø§ØŗØĒØŽØ¯Ø§Ų…</string>
<string name="revanced_shorts_player_type_shorts">Ų…Ø´ØēŲ„ Shorts</string>
<string name="revanced_shorts_player_type_regular_player">Ų…Ø´ØēŲ„ ØšØ§Ø¯ŲŠ</string>
<string name="revanced_shorts_player_type_regular_player_fullscreen">شاش؊ ŲƒØ§Ų…Ų„ØŠ - Ų…Ø´ØēŲ„ ØšØ§Ø¯ŲŠ</string>
<string name="revanced_shorts_player_type_regular_player">Ø§Ų„Ų…Ø´ØēŲ„ Ø§Ų„ØšØ§Ø¯ŲŠ</string>
<string name="revanced_shorts_player_type_regular_player_fullscreen">Ø§Ų„Ų…Ø´ØēŲ„ Ø§Ų„ØšØ§Ø¯ŲŠ ؁؊ ؈ØļØš Ų…Ų„ØĄ Ø§Ų„Ø´Ø§Ø´ØŠ</string>
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
<string name="revanced_shorts_autoplay_title">Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„ØĒŲ„Ų‚Ø§ØĻ؊ Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts</string>
@@ -1063,12 +1151,6 @@ Second \"item\" text"</string>
<string name="revanced_shorts_autoplay_background_summary_on">ØŗŲŠØĒŲ… ØĒØ´ØēŲŠŲ„ ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§ ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ</string>
<string name="revanced_shorts_autoplay_background_summary_off">ØŗŲŠØĒŲ… ØĒŲƒØąØ§Øą ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Shorts ؁؊ Ø§Ų„ØŽŲ„ŲŲŠØŠ</string>
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
<string name="revanced_tablet_layout_title">ØĒŲ…ŲƒŲŠŲ† ØĒØĩŲ…ŲŠŲ… Ø§Ų„ØŦŲ‡Ø§Ø˛ Ø§Ų„Ų„ŲˆØ­ŲŠ</string>
<string name="revanced_tablet_layout_summary_on">ØĒŲ… ØĒŲ…ŲƒŲŠŲ† ØĒØĩŲ…ŲŠŲ… Ø§Ų„ØŦŲ‡Ø§Ø˛ Ø§Ų„Ų„ŲˆØ­ŲŠ</string>
<string name="revanced_tablet_layout_summary_off">ØĒŲ… ØĒØšØˇŲŠŲ„ ØĒØĩŲ…ŲŠŲ… Ø§Ų„ØŦŲ‡Ø§Ø˛ Ø§Ų„Ų„ŲˆØ­ŲŠ</string>
<string name="revanced_tablet_layout_user_dialog_message">Ų„Ø§ ØĒØ¸Ų‡Øą Ų…Ų†Ø´ŲˆØąØ§ØĒ Ø§Ų„Ų…ØŦØĒŲ…Øš ØšŲ„Ų‰ ØĒØŽØˇŲŠØˇØ§ØĒ Ø§Ų„ØŦŲ‡Ø§Ø˛ Ø§Ų„Ų„ŲˆØ­ŲŠ</string>
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">Ø§Ų„Ų…Ø´ØēŲ„ Ø§Ų„Ų…ØĩØēØą</string>
<string name="revanced_miniplayer_screen_summary">ØĒØēŲŠŲŠØą Ų†Ų…Øˇ Ø§Ų„Ų…Ø´ØēŲ„ Ø§Ų„Ų…ØĩØēØą Ø¯Ø§ØŽŲ„ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚</string>
@@ -1166,7 +1248,7 @@ Second \"item\" text"</string>
<string name="revanced_alt_thumbnail_stills_about_title">Ų„Ų‚ØˇØ§ØĒ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĢابØĒØŠ</string>
<string name="revanced_alt_thumbnail_stills_about_summary">؊ØĒŲ… Ø§Ų„ØĒŲ‚Ø§Øˇ Ø§Ų„Ų„Ų‚ØˇØ§ØĒ Ø§Ų„ØĢابØĒØŠ Ų…Ų† Ø¨Ø¯Ø§ŲŠØŠ/ŲˆØŗØˇ/Ų†Ų‡Ø§ŲŠØŠ ŲƒŲ„ ŲŲŠØ¯ŲŠŲˆ. Ų‡Ø°Ų‡ Ø§Ų„ØĩŲˆØą Ų…Ø¯Ų…ØŦØŠ ؁؊ YouTube ŲˆŲ„Ø§ ؊ØĒŲ… Ø§ØŗØĒØŽØ¯Ø§Ų… ØŖŲŠ ŲˆØ§ØŦŲ‡ØŠ Ø¨ØąŲ…ØŦØŠ ØĒØˇØ¨ŲŠŲ‚Ø§ØĒ ØŽØ§ØąØŦŲŠØŠ</string>
<string name="revanced_alt_thumbnail_stills_fast_title">Ø§ØŗØĒØŽØ¯Ų… Ø§Ų„Ų„Ų‚ØˇØ§ØĒ Ø§Ų„ØĢابØĒØŠ Ø§Ų„ØŗØąŲŠØšØŠ</string>
<string name="revanced_alt_thumbnail_stills_fast_summary_on">Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ų„Ų‚ØˇØ§ØĒ Ų…ØĒŲˆØŗØˇØŠ Ø§Ų„ØŦŲˆØ¯ØŠ. ØŗŲŠØĒŲ… ØĒØ­Ų…ŲŠŲ„ Ø§Ų„Ų…ŲØĩØēŲ‘ŲŽØąØ§ØĒ Ø¨Ø´ŲƒŲ„ ØŖØŗØąØšØŒ ŲˆŲ„ŲƒŲ† Ø§Ų„Ø¨ØĢ Ø§Ų„Ų…Ø¨Ø§Ø´Øą ؈ Ø§Ų„Ų…Ų‚Ø§ØˇØš Ø§Ų„ØĒ؊ Ų„Ų… ؊ØĒŲ… ØĨØĩØ¯Ø§ØąŲ‡Ø§ ØŖŲˆ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ ØŦØ¯Ų‹Ø§ Ų‚Ø¯ ØĒØšØąØļ Ų…ŲØĩØēŲ‘ŲŽØąØ§ØĒ ŲØ§ØąØēØŠ</string>
<string name="revanced_alt_thumbnail_stills_fast_summary_on">Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Ų„Ų‚ØˇØ§ØĒ Ų…ØĒŲˆØŗØˇØŠ Ø§Ų„ØŦŲˆØ¯ØŠ. ØŗŲŠØĒŲ… ØĒØ­Ų…ŲŠŲ„ Ø§Ų„Ų…ŲØĩØēŲ‘ŲŽØąØ§ØĒ Ø¨Ø´ŲƒŲ„ ØŖØŗØąØšØŒ ŲˆŲ„ŲƒŲ† Ø§Ų„Ø¨ØĢ Ø§Ų„Ų…Ø¨Ø§Ø´Øą ؈ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ø§Ų„ØĒ؊ Ų„Ų… ؊ØĒŲ… ØĨØĩØ¯Ø§ØąŲ‡Ø§ ØŖŲˆ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ ØŦØ¯Ų‹Ø§ Ų‚Ø¯ ØĒØšØąØļ Ų…ŲØĩØēŲ‘ŲŽØąØ§ØĒ ŲØ§ØąØēØŠ</string>
<string name="revanced_alt_thumbnail_stills_fast_summary_off">Ø§ØŗØĒØŽØ¯Ø§Ų… Ų„Ų‚ØˇØ§ØĒ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„ØĢابØĒØŠ بØŦŲˆØ¯ØŠ ØšØ§Ų„ŲŠØŠ</string>
<string name="revanced_alt_thumbnail_stills_time_title">ŲˆŲ‚ØĒ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų„ØŖØŽØ° Ø§Ų„Ų„Ų‚ØˇØ§ØĒ Ø§Ų„ØĢابØĒØŠ Ų…Ų†Ų‡</string>
<string name="revanced_alt_thumbnail_stills_time_entry_1">Ø¨Ø¯Ø§ŲŠØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
@@ -1232,12 +1314,13 @@ Second \"item\" text"</string>
<string name="revanced_force_original_audio_title">ŲØąØļ Ø§Ų„Øĩ؈ØĒ Ø§Ų„ØŖØĩŲ„ŲŠ</string>
<string name="revanced_force_original_audio_summary_on">Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Øĩ؈ØĒ Ø§Ų„ØŖØĩŲ„ŲŠ</string>
<string name="revanced_force_original_audio_summary_off">Ø§ØŗØĒØŽØ¯Ø§Ų… Ø§Ų„Øĩ؈ØĒ Ø§Ų„Ø§ŲØĒØąØ§Øļ؊</string>
<string name="revanced_force_original_audio_not_available">Ų„Ø§ØŗØĒØŽØ¯Ø§Ų… Ų‡Ø°Ų‡ Ø§Ų„Ų…ŲŠØ˛ØŠØŒ Ų‚Ų… بØĒØēŲŠŲŠØą Ų…Ø­Ø§ŲƒØ§ØŠ بØĢ Ø§Ų„Ų…Ø­ØĒŲˆŲ‰ ØĨŲ„Ų‰ Ų†ŲˆØš Ø§Ų„ØšŲ…ŲŠŲ„ iOS</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
<string name="revanced_video_quality_default_entry_1">ØĒŲ„Ų‚Ø§ØĻ؊</string>
<string name="revanced_remember_video_quality_last_selected_title">ØĒØ°ŲƒØą ØĒØēŲŠŲŠØąØ§ØĒ ØŦŲˆØ¯ØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_remember_video_quality_last_selected_summary_on">ØĒŲ†ØˇØ¨Ų‚ ØĒØēŲŠŲŠØąØ§ØĒ Ø§Ų„ØŦŲˆØ¯ØŠ ØšŲ„Ų‰ ØŦŲ…ŲŠØš Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_remember_video_quality_last_selected_summary_on">ØĒŲ†ØˇØ¨Ų‚ ØĒØēŲŠŲŠØąØ§ØĒ Ø§Ų„ØŦŲˆØ¯ØŠ ØšŲ„Ų‰ ØŦŲ…ŲŠØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ</string>
<string name="revanced_remember_video_quality_last_selected_summary_off">ØĒŲ†ØˇØ¨Ų‚ ØĒØēŲŠŲŠØąØ§ØĒ Ø§Ų„ØŦŲˆØ¯ØŠ ØšŲ„Ų‰ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ø­Ø§Ų„ŲŠ ŲŲ‚Øˇ</string>
<string name="revanced_video_quality_default_wifi_title">ØŦŲˆØ¯ØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ø§ŲØĒØąØ§ØļŲŠØŠ ØšŲ„Ų‰ Ø´Ø¨ŲƒØŠ Wi-Fi</string>
<string name="revanced_video_quality_default_mobile_title">ØŦŲˆØ¯ØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ø§ŲØĒØąØ§ØļŲŠØŠ ØšŲ„Ų‰ Ø´Ø¨ŲƒØŠ Ø§Ų„ØŦŲˆŲ‘ŲŽØ§Ų„</string>
@@ -1259,6 +1342,8 @@ Second \"item\" text"</string>
<string name="revanced_custom_playback_speeds_invalid">؊ØŦب ØŖŲ† ØĒŲƒŲˆŲ† ØŗØąØšØ§ØĒ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…ØŽØĩØĩØŠ ØŖŲ‚Ų„ Ų…Ų† %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…ØŽØĩØĩØŠ ØēŲŠØą ØĩØ§Ų„Ø­ØŠ</string>
<string name="revanced_custom_playback_speeds_auto">ØĒŲ„Ų‚Ø§ØĻ؊</string>
<string name="revanced_speed_tap_and_hold_title">\"ØŗØąØšØŠ Ø§Ų„Ų†Ų‚Øą Ų…Øš Ø§Ų„Ø§ØŗØĒŲ…ØąØ§Øą Ø§Ų„Ų…ØŽØĩØĩØŠ\"</string>
<string name="revanced_speed_tap_and_hold_summary">ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø¨ŲŠŲ† 0-8</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
<string name="revanced_remember_playback_speed_last_selected_title">ØĒØ°ŲƒØą Ø§Ų„ØĒØēŲŠØąØ§ØĒ ؁؊ ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„</string>
@@ -1287,65 +1372,27 @@ Second \"item\" text"</string>
Ų‚Ø¯ Ų„Ø§ ŲŠØšŲ…Ų„ ØĒØ´ØēŲŠŲ„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">ØĨŲŠŲ‚Ø§Ų ØĒØ´ØēŲŠŲ„ Ų‡Ø°Ø§ Ø§Ų„ØĨؚداد Ų‚Ø¯ ŲŠØŗØ¨Ø¨ Ų…Ø´Ø§ŲƒŲ„ ؁؊ ØĒØ´ØēŲŠŲ„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ.</string>
<string name="revanced_spoof_video_streams_client_type_title">Ø§Ų„ØšŲ…ŲŠŲ„ Ø§Ų„Ø§ŲØĒØąØ§Øļ؊</string>
<string name="revanced_spoof_video_streams_about_title">ØĸØĢØ§Øą ØŦØ§Ų†Ø¨ŲŠØŠ ŲˆŲ‡Ų…ŲŠØŠ</string>
<string name="revanced_spoof_video_streams_about_summary">"- Ų‚Ø¯ Ų„Ø§ ؊ØĒŲ… ØĒØ´ØēŲŠŲ„ Ų…Ų‚Ø§ØˇØš ŲŲŠØ¯ŲŠŲˆ Ų„Ų„ØŖØˇŲØ§Ų„
- Ø§Ų„Ų‚Ø§ØĻŲ…ØŠ Øĩ؈ØĒ Ø§Ų„Ų…ØŗØ§Øą Ų…ŲŲ‚ŲˆØ¯ØŠ
- Ų…ØŗØĒŲˆŲ‰ Ø§Ų„Øĩ؈ØĒ Ø§Ų„ØĢابØĒ ØēŲŠØą Ų…ØĒاح
- ØŽŲŠØ§Øą ŲØąØļ Ø§Ų„Øĩ؈ØĒ Ø§Ų„ØŖØĩŲ„ŲŠ ØēŲŠØą Ų…ØĒاح"</string>
<string name="revanced_spoof_video_streams_language_title">Ų„ØēØŠ Ø§Ų„Ø¨ØĢ Ø§Ų„Øĩ؈ØĒ؊ Ø§Ų„Ø§ŲØĒØąØ§ØļŲŠØŠ</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">Ų„ØēØŠ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚</string>
<string name="revanced_spoof_video_streams_language_AR">Ø§Ų„ØšØąØ¨ŲŠØŠ</string>
<string name="revanced_spoof_video_streams_language_AZ">Azerbaijani</string>
<string name="revanced_spoof_video_streams_language_BG">Bulgarian</string>
<string name="revanced_spoof_video_streams_language_BN">Bengali</string>
<string name="revanced_spoof_video_streams_language_CA">Catalan</string>
<string name="revanced_spoof_video_streams_language_CS">Czech</string>
<string name="revanced_spoof_video_streams_language_DA">Danish</string>
<string name="revanced_spoof_video_streams_language_DE">German</string>
<string name="revanced_spoof_video_streams_language_EL">Greek</string>
<string name="revanced_spoof_video_streams_language_EN">English</string>
<string name="revanced_spoof_video_streams_language_ES">Spanish</string>
<string name="revanced_spoof_video_streams_language_ET">Estonian</string>
<string name="revanced_spoof_video_streams_language_FA">ŲØ§ØąØŗŲ‰</string>
<string name="revanced_spoof_video_streams_language_FI">Finnish</string>
<string name="revanced_spoof_video_streams_language_FR">French</string>
<string name="revanced_spoof_video_streams_language_GU">Gujarati</string>
<string name="revanced_spoof_video_streams_language_HI">Hindi</string>
<string name="revanced_spoof_video_streams_language_HR">Croatian</string>
<string name="revanced_spoof_video_streams_language_HU">Hungarian</string>
<string name="revanced_spoof_video_streams_language_ID"> Indonesian</string>
<string name="revanced_spoof_video_streams_language_IT">Italian</string>
<string name="revanced_spoof_video_streams_language_JA">Japanese</string>
<string name="revanced_spoof_video_streams_language_KK">Kazakh</string>
<string name="revanced_spoof_video_streams_language_KO">Korean</string>
<string name="revanced_spoof_video_streams_language_LT">Lithuanian</string>
<string name="revanced_spoof_video_streams_language_LV">Latvian</string>
<string name="revanced_spoof_video_streams_language_MK">Macedonian</string>
<string name="revanced_spoof_video_streams_language_MN">Mongolian</string>
<string name="revanced_spoof_video_streams_language_MR">Marathi</string>
<string name="revanced_spoof_video_streams_language_MS">Malay</string>
<string name="revanced_spoof_video_streams_language_MY">Burmese</string>
<string name="revanced_spoof_video_streams_language_NL">Dutch</string>
<string name="revanced_spoof_video_streams_language_OR">Odia</string>
<string name="revanced_spoof_video_streams_language_PA">Punjabi</string>
<string name="revanced_spoof_video_streams_language_PL">Polish</string>
<string name="revanced_spoof_video_streams_language_PT_BR">Portuguese (Brazil)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">Portuguese (Portugal)</string>
<string name="revanced_spoof_video_streams_language_RO">Romanian</string>
<string name="revanced_spoof_video_streams_language_RU">Russian</string>
<string name="revanced_spoof_video_streams_language_SK">Slovak</string>
<string name="revanced_spoof_video_streams_language_SL">Slovene</string>
<string name="revanced_spoof_video_streams_language_SR">Serbian</string>
<string name="revanced_spoof_video_streams_language_SV">Swedish</string>
<string name="revanced_spoof_video_streams_language_SW">Swahili</string>
<string name="revanced_spoof_video_streams_language_TA">Tamil</string>
<string name="revanced_spoof_video_streams_language_TE">Telugu</string>
<string name="revanced_spoof_video_streams_language_TH">Thai</string>
<string name="revanced_spoof_video_streams_language_TR">Turkish</string>
<string name="revanced_spoof_video_streams_language_UK">Ukrainian</string>
<string name="revanced_spoof_video_streams_language_UR">Urdu</string>
<string name="revanced_spoof_video_streams_language_VI">Vietnamese</string>
<string name="revanced_spoof_video_streams_language_ZH">Chinese</string>
<!-- 'no auth' means no authentication -->
<string name="revanced_spoof_video_streams_client_type_android_vr_no_auth">Android VR (Ø¨Ø¯ŲˆŲ† Ų…ØĩØ§Ø¯Ų‚ØŠ)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">ŲØąØļ iOS AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">؊ØĒŲ… ŲØąØļ ØĒØąŲ…ŲŠØ˛ ŲŲŠØ¯ŲŠŲˆ ØšŲ„Ų‰ AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">؊ØĒŲ… ØĒØ­Ø¯ŲŠØ¯ ØĒØąŲ…ŲŠØ˛ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Ų‚Ø¯ ŲŠØ¤Ø¯ŲŠ ØĒŲ…ŲƒŲŠŲ† Ų‡Ø°Ø§ ØĨŲ„Ų‰ ØĒØ­ØŗŲŠŲ† ØšŲ…Øą Ø§Ų„Ø¨ØˇØ§ØąŲŠØŠ ؈ØĨØĩŲ„Ø§Ø­ ØĒŲ‚ØˇŲŠØš Ø§Ų„ØĒØ´ØēŲŠŲ„.
AVC Ų„Ø¯ŲŠŲ‡ حد ØŖŲ‚ØĩŲ‰ Ų„Ų„Ø¯Ų‚ØŠ 1080p، Ų„Ø§ ؊ØĒŲˆŲØą ØĒØąŲ…ŲŠØ˛ Ø§Ų„Øĩ؈ØĒ Opus، ŲˆØŗŲˆŲ ŲŠØŗØĒØŽØ¯Ų… ØĒØ´ØēŲŠŲ„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø¨ŲŠØ§Ų†Ø§ØĒ ØĨŲ†ØĒØąŲ†ØĒ ØŖŲƒØĢØą Ų…Ų† VP9 ØŖŲˆ AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">Ø§Ų„ØĸØĢØ§Øą Ø§Ų„ØŦØ§Ų†Ø¨ŲŠØŠ Ų„Ų…Ø­Ø§ŲƒØ§ØŠ Ų‡ŲˆŲŠØŠ iOS</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"â€ĸ Ų‚Ø¯ Ų„Ø§ ؊ØĒŲ… ØĒØ´ØēŲŠŲ„ Ø§Ų„ØŖŲŲ„Ø§Ų… ØŖŲˆ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ø§Ų„Ų…Ø¯ŲŲˆØšØŠ
â€ĸ Ų…ØŗØĒŲˆŲ‰ Ø§Ų„Øĩ؈ØĒ Ø§Ų„ØĢابØĒ ØēŲŠØą Ų…ØĒŲˆŲØą
â€ĸ ØĒŲ†ØĒŲ‡ŲŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ Ų‚Ø¨Ų„ ب 1 ØĢØ§Ų†ŲŠØŠ"</string>
<string name="revanced_spoof_video_streams_about_android_title">Ø§Ų„ØĸØĢØ§Øą Ø§Ų„ØŦØ§Ų†Ø¨ŲŠØŠ Ų„Ų…Ø­Ø§ŲƒØ§ØŠ Ų‡ŲˆŲŠØŠ Android</string>
<string name="revanced_spoof_video_streams_about_android_summary">"â€ĸ Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„Ų…Ų‚ØˇØš Ø§Ų„Øĩ؈ØĒ؊ Ų…ŲŲ‚ŲˆØ¯ØŠ
â€ĸ Ų…ØŗØĒŲˆŲ‰ Ø§Ų„Øĩ؈ØĒ Ø§Ų„ØĢابØĒ ØēŲŠØą Ų…ØĒاح
â€ĸ ŲØąØļ Ø§Ų„Øĩ؈ØĒ Ø§Ų„ØŖØĩŲ„ŲŠ ØēŲŠØą Ų…ØĒŲˆŲØą"</string>
<string name="revanced_spoof_video_streams_about_no_av1">â€ĸ Ų„Ø§ ؊؈ØŦد ØĒØąŲ…ŲŠØ˛ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ AV1</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">ØšØąØļ ؁؊ ØĨØ­ØĩØ§ØĄØ§ØĒ ØĒŲ‚Ų†ŲŠØŠ</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">؊ØĒŲ… ØšØąØļ Ų†ŲˆØš Ø§Ų„ØšŲ…ŲŠŲ„ ؁؊ ØĨØ­ØĩØ§ØĄØ§ØĒ ØĒŲ‚Ų†ŲŠØŠ</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">ØĒŲ… ØĨØŽŲØ§ØĄ Ų†ŲˆØš Ø§Ų„ØšŲ…ŲŠŲ„ ؁؊ ØĨØ­ØĩØ§ØĄØ§ØĒ ØĒŲ‚Ų†ŲŠØŠ</string>
<string name="revanced_spoof_video_streams_language_title">Ų„ØēØŠ Ø§Ų„Ø¨ØĢ Ø§Ų„Øĩ؈ØĒ؊ Ø§Ų„Ø§ŲØĒØąØ§ØļŲŠØŠ Ų„Ų„ŲˆØ§Ų‚Øš Ø§Ų„Ø§ŲØĒØąØ§Øļ؊ VR</string>
</patch>
</app>
<app id="twitch">

View File

@@ -136,6 +136,8 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
@@ -155,6 +157,8 @@ Second \"item\" text"</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<!-- Shown in the settings preferences, and translations can be any text length. -->
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<!-- It is ideal, but not required, if the text here appears is 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 -->
@@ -168,8 +172,6 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
</patch>
<patch id="layout.theme.themePatch">
@@ -220,6 +222,7 @@ Second \"item\" text"</string>
<patch id="interaction.seekbar.enableSlideToSeekPatch">
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
<!-- 'no auth' means no authentication -->
</patch>
</app>
<app id="twitch">

View File

@@ -22,8 +22,8 @@ Second \"item\" text"</string>
<resources>
<app id="shared">
<patch id="misc.checks.checkEnvironmentPatch">
<string name="revanced_check_environment_failed_title">\"ĐŸŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐŋŅ€Đ°Đ˛ĐĩŅ€ĐēŅ– Đ°ŅŅŅ€ĐžĐ´Đ´ĐˇŅ ĐŊĐĩ ŅžĐ´Đ°ĐģĐžŅŅ\"</string>
<string name="revanced_check_environment_dialog_open_official_source_button">\"АдĐēŅ€Ņ‹Ņ†ŅŒ Đ°Ņ„Ņ–Ņ†Ņ‹ĐšĐŊŅ‹ Đ˛ŅĐą-ŅĐ°ĐšŅ‚\"</string>
<string name="revanced_check_environment_failed_title">ĐŸŅ€Đ°Đ˛ĐĩŅ€ĐēŅ– ĐŊĐĩ ĐŋŅ€ĐžĐšĐ´ĐˇĐĩĐŊŅ‹</string>
<string name="revanced_check_environment_dialog_open_official_source_button">АдĐēŅ€Ņ‹Ņ†ŅŒ Đ°Ņ„Ņ–Ņ†Ņ‹ĐšĐŊŅ‹ Đ˛ŅĐą-ŅĐ°ĐšŅ‚</string>
<string name="revanced_check_environment_dialog_ignore_button">Đ†ĐŗĐŊĐ°Ņ€Đ°Đ˛Đ°Ņ†ŅŒ</string>
<string name="revanced_check_environment_failed_message">&lt;h5&gt;Đ“ŅŅ‚Đ° Đ´Đ°Đ´Đ°Ņ‚Đ°Đē Đ˛Ņ–Đ´Đ°Đ˛ĐžŅ‡ĐŊа ĐŊĐĩ С\'ŅŅžĐģŅĐĩŅ†Ņ†Đ° ĐŋĐ°Ņ‚Ņ‡Đ°Đŧ.&lt;/h5&gt;&lt;br&gt;Đ“ŅŅ‚Đ° Đ´Đ°Đ´Đ°Ņ‚Đ°Đē ĐŧĐžĐļа ĐŋŅ€Đ°Ņ†Đ°Đ˛Đ°Ņ†ŅŒ ĐŊŅĐŋŅ€Đ°Đ˛Ņ–ĐģҌĐŊа, а Ņ‚Đ°ĐēŅĐ°Đŧа ĐŧĐžĐļа ĐąŅ‹Ņ†ŅŒ &lt;b&gt;ĐŊĐĩĐąŅŅĐŋĐĩ҇ĐŊŅ‹Đŧ айО ĐŊĐ°Đ˛Đ°Ņ‚ ĐŊĐĩĐąŅŅĐŋĐĩ҇ĐŊŅ‹Đŧ ҃ Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐŊĐŊŅ–&lt;/b&gt;.&lt;br&gt;&lt;br&gt;Đ“ŅŅ‚Ņ‹Ņ ĐŋŅ€Đ°Đ˛ĐĩŅ€ĐēŅ– аСĐŊĐ°Ņ‡Đ°ŅŽŅ†ŅŒ, ŅˆŅ‚Đž ĐŗŅŅ‚Đ° Đ´Đ°Đ´Đ°Ņ‚Đ°Đē ĐąŅ‹ĐģĐž ĐˇĐ°ĐŗĐ°Đ´Đ´ĐˇŅ ĐŋĐĩŅ€Đ°Ņ€ĐžĐąĐģĐĩĐŊа айО Đ°Ņ‚Ņ€Ņ‹ĐŧаĐŊа ад ĐēĐ°ĐŗĐžŅŅŒŅ†Ņ– Ņ–ĐŊŅˆĐ°ĐŗĐ°:&lt;br&gt;&lt;br&gt;&lt;small&gt;%1$s&lt;/small&gt;&lt;br&gt;ĐĐ°ŅŅ‚ĐžĐšĐģŅ–Đ˛Đ° Ņ€ŅĐēаĐŧĐĩĐŊĐ´ŅƒĐĩŅ†Ņ†Đ° &lt;b&gt;Đ˛Ņ‹Đ´Đ°ĐģŅ–Ņ†ŅŒ ĐŗŅŅ‚Ņ‹ Đ´Đ°Đ´Đ°Ņ‚Đ°Đē Ņ– ĐŋĐĩŅ€Đ°Ņ€Đ°ĐąŅ–Ņ†ŅŒ ŅĐŗĐž ŅĐ°ĐŧĐ°ŅŅ‚ĐžĐšĐŊа&lt;/b&gt;, Đēай ĐŋĐĩŅ€Đ°ĐēаĐŊĐ°Ņ†Ņ†Đ°, ŅˆŅ‚Đž Đ˛Ņ‹ Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°Đĩ҆Đĩ ĐŋŅ€Đ°Đ˛ĐĩŅ€Đ°ĐŊŅ‹ Ņ– ĐąŅŅĐŋĐĩ҇ĐŊŅ‹ Đ´Đ°Đ´Đ°Ņ‚Đ°Đē.&lt;p&gt;&lt;br&gt;КаĐģŅ– Ņ–ĐŗĐŊĐ°Ņ€Đ°Đ˛Đ°Ņ†ŅŒ, ĐŗŅŅ‚Đ° ĐŋаĐŋŅŅ€ŅĐ´ĐļаĐŊĐŊĐĩ ĐąŅƒĐ´ĐˇĐĩ ĐŋаĐēаСаĐŊа Ņ‚ĐžĐģҌĐēŅ– два Ņ€Đ°ĐˇŅ‹.</string>
<string name="revanced_check_environment_not_same_patching_device">АдĐēĐ°Ņ€ŅĐēŅ‚Đ°Đ˛Đ°ĐŊа ĐŊа Ņ–ĐŊŅˆĐ°Đš ĐŋҀҋĐģадСĐĩ</string>
@@ -33,6 +33,7 @@ Second \"item\" text"</string>
<string name="revanced_check_environment_not_near_patch_time_invalid">Đ”Đ°Ņ‚Đ° ŅŅ‚Đ˛Đ°Ņ€ŅĐŊĐŊŅ APK ĐŋĐ°ŅˆĐēОдĐļаĐŊа</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_submenu_title">НаĐģĐ°Đ´Ņ‹</string>
<string name="revanced_settings_title">ReVanced</string>
<string name="revanced_settings_confirm_user_dialog_title">Đ’Ņ‹ Ņ…ĐžŅ‡Đ°Ņ†Đĩ ĐŋŅ€Đ°Ņ†ŅĐŗĐŊŅƒŅ†ŅŒ?</string>
<string name="revanced_settings_reset">ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ</string>
@@ -43,6 +44,62 @@ Second \"item\" text"</string>
<string name="revanced_settings_import_reset">НаĐģĐ°Đ´Ņ‹ ReVanced ҁĐēŅ–ĐŊŅƒŅ‚Ņ‹ да ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊҋ҅</string>
<string name="revanced_settings_import_success">ІĐŧĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°ĐŊа %d ĐŊаĐģад</string>
<string name="revanced_settings_import_failure_parse">ПаĐŧŅ‹ĐģĐēа Ņ–ĐŧĐŋĐ°Ņ€Ņ‚Ņƒ: %s</string>
<string name="revanced_language_title">Мова ReVanced</string>
<string name="revanced_language_user_dialog_message">"ПĐĩŅ€Đ°ĐēĐģĐ°Đ´Ņ‹ Đ´ĐģŅ ĐŊĐĩĐēĐ°Ņ‚ĐžŅ€Ņ‹Ņ… ĐŧĐžŅž ĐŧĐžĐŗŅƒŅ†ŅŒ ĐąŅ‹Ņ†ŅŒ Đ°Đ´ŅŅƒŅ‚ĐŊŅ‹ĐŧŅ– айО ĐŊŅĐŋĐžŅžĐŊŅ‹ĐŧŅ–.
Каб Đ´Đ°Đ´Đ°Ņ†ŅŒ ĐŊĐžĐ˛Ņ‹Ņ ĐŧĐžĐ˛Ņ‹, ĐŊавĐĩĐ´Đ°ĐšŅ†Đĩ translate.revanced.app"</string>
<string name="revanced_language_DEFAULT">Мова ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧŅ‹</string>
<string name="revanced_language_AR">ĐŅ€Đ°ĐąŅĐēĐ°Ņ</string>
<string name="revanced_language_AZ">АСĐĩŅ€ĐąĐ°ĐšĐ´ĐļаĐŊҁĐēŅ–</string>
<string name="revanced_language_BG">БаĐģĐŗĐ°Ņ€ŅĐēĐ°Ņ</string>
<string name="revanced_language_BN">БĐĩĐŊĐŗĐ°ĐģҌҁĐēĐ°Ņ</string>
<string name="revanced_language_CA">ĐšĐ°Ņ‚Đ°ĐģĐžĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_CS">Đ§ŅŅˆŅĐēŅ–</string>
<string name="revanced_language_DA">Đ”Đ°Ņ†ĐēŅ–</string>
<string name="revanced_language_DE">ĐŅĐŧĐĩ҆ĐēŅ–</string>
<string name="revanced_language_EL">Đ“Ņ€ŅŅ†ĐēŅ–</string>
<string name="revanced_language_EN">АĐŊĐŗĐģŅ–ĐšŅĐēĐ°Ņ</string>
<string name="revanced_language_ES">Đ†ŅĐŋаĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_ET">Đ­ŅŅ‚ĐžĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_FA">ПĐĩŅ€ŅŅ–Đ´ŅĐēĐ°Ņ</string>
<string name="revanced_language_FI">Đ¤Ņ–ĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_FR">Đ¤Ņ€Đ°ĐŊŅ†ŅƒĐˇŅĐēĐ°Ņ</string>
<string name="revanced_language_GU">Đ“ŅƒĐ´ĐļĐ°Ņ€Đ°Ņ†Ņ–</string>
<string name="revanced_language_HI">ĐĨŅ–ĐŊĐ´ĐˇŅ–</string>
<string name="revanced_language_HR">ĐĨĐ°Ņ€Đ˛Đ°Ņ†ĐēĐ°Ņ</string>
<string name="revanced_language_HU">ВĐĩĐŊĐŗĐĩҀҁĐēĐ°Ņ</string>
<string name="revanced_language_ID">ІĐŊдаĐŊĐĩĐˇŅ–ĐšŅĐēĐ°Ņ</string>
<string name="revanced_language_IT">Đ†Ņ‚Đ°ĐģŅŒŅĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_JA">Đ¯ĐŋĐžĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_KK">ĐšĐ°ĐˇĐ°Ņ…ŅĐēĐ°Ņ</string>
<string name="revanced_language_KO">ĐšĐ°Ņ€ŅĐšŅĐēĐ°Ņ</string>
<string name="revanced_language_LT">Đ›Ņ–Ņ‚ĐžŅžŅĐēĐ°Ņ</string>
<string name="revanced_language_LV">Đ›Đ°Ņ‚Ņ‹ŅˆŅĐēĐ°Ņ</string>
<string name="revanced_language_MK">МаĐēĐĩĐ´ĐžĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_MN">МаĐŊĐŗĐžĐģҌҁĐēĐ°Ņ</string>
<string name="revanced_language_MR">МаĐģĐ°ŅĐģаĐŧ</string>
<string name="revanced_language_MS">МаĐģĐ°ĐšŅĐēĐ°Ņ</string>
<string name="revanced_language_MY">Đ‘Ņ–Ņ€ĐŧаĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_NL">ĐŅ–Đ´ŅŅ€ĐģаĐŊĐ´ŅĐēĐ°Ņ</string>
<string name="revanced_language_OR">ĐžĐ´Ņ‹Ņ</string>
<string name="revanced_language_PA">ПĐĩĐŊĐ´ĐļĐ°ĐąŅ–</string>
<string name="revanced_language_PL">ПоĐģҌҁĐēĐ°Ņ</string>
<string name="revanced_language_PT">ĐŸĐ°Ņ€Ņ‚ŅƒĐŗĐ°ĐģҌҁĐēĐ°Ņ</string>
<string name="revanced_language_RO">Đ ŅƒĐŧŅ‹ĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_RU">Đ ŅƒŅĐēĐ°Ņ</string>
<string name="revanced_language_SK">ĐĄĐģĐ°Đ˛Đ°Ņ†ĐēĐ°Ņ</string>
<string name="revanced_language_SL">ĐĄĐģавĐĩĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_SR">ĐĄĐĩŅ€ĐąŅĐēĐ°Ņ</string>
<string name="revanced_language_SV">ШвĐĩĐ´ŅĐēĐ°Ņ</string>
<string name="revanced_language_SW">ĐĄŅƒĐ°Ņ…Ņ–ĐģŅ–</string>
<string name="revanced_language_TA">ĐĸаĐŧŅ–ĐģҌҁĐēĐ°Ņ</string>
<string name="revanced_language_TE">ĐĸŅĐģŅƒĐŗŅƒ</string>
<string name="revanced_language_TH">ĐĸĐ°ĐšŅĐēĐ°Ņ</string>
<string name="revanced_language_TR">ĐĸŅƒŅ€Đĩ҆ĐēĐ°Ņ</string>
<string name="revanced_language_UK">ĐŖĐēŅ€Đ°Ņ–ĐŊҁĐēĐ°Ņ</string>
<string name="revanced_language_UR">ĐŖŅ€Đ´Ņƒ</string>
<string name="revanced_language_VI">В\'ĐĩŅ‚ĐŊаĐŧҁĐēĐ°Ņ</string>
<string name="revanced_language_ZH">ĐšŅ–Ņ‚Đ°ĐšŅĐēĐ°Ņ</string>
<string name="revanced_pref_import_export_title">ІĐŧĐŋĐ°Ņ€Ņ‚ / Đ­ĐēҁĐŋĐ°Ņ€Ņ‚</string>
<string name="revanced_pref_import_export_summary">ІĐŧĐŋĐ°Ņ€Ņ‚ / Đ­ĐēҁĐŋĐ°Ņ€Ņ‚ ĐŊаĐģад ReVanced</string>
<!-- Settings about dialog. -->
@@ -77,13 +134,16 @@ Second \"item\" text"</string>
<string name="revanced_settings_screen_01_ads_title">Ай\"ŅĐ˛Ņ‹</string>
<string name="revanced_settings_screen_02_alt_thumbnails_title">АĐģŅŒŅ‚ŅŅ€ĐŊĐ°Ņ‚Ņ‹ŅžĐŊŅ‹Ņ ĐŧŅ–ĐŊŅ–ŅŅ†ŅŽŅ€Ņ‹</string>
<string name="revanced_settings_screen_03_feed_title">ĐšĐ°Ņ€ĐŧŅ–Ņ†ŅŒ</string>
<string name="revanced_settings_screen_04_player_title">Đ“ŅƒĐģĐĩ҆</string>
<string name="revanced_settings_screen_05_general_title">ГĐĩĐŊĐĩŅ€Đ°ĐģҌĐŊĐ°Ņ ĐŋĐģаĐŊŅ–Ņ€ĐžŅžĐēа</string>
<string name="revanced_settings_screen_04_general_title">ĐĐŗŅƒĐģҌĐŊаĐĩ</string>
<string name="revanced_settings_screen_05_player_title">ПĐģŅĐĩŅ€</string>
<string name="revanced_settings_screen_06_shorts_title">Shorts</string>
<string name="revanced_settings_screen_07_seekbar_title">ПаĐŊŅĐģҌ ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_settings_screen_08_swipe_controls_title">Đ­ĐģĐĩĐŧĐĩĐŊ҂ҋ ĐēŅ–Ņ€Đ°Đ˛Đ°ĐŊĐŊŅ ĐŋаĐģŅŒŅ†Đ°Đŧ</string>
<string name="revanced_settings_screen_11_misc_title">РОСĐŊаĐĩ</string>
<string name="revanced_settings_screen_12_video_title">Đ’Ņ–Đ´ŅĐ°</string>
<string name="revanced_restore_old_settings_menus_title">АдĐŊĐ°Đ˛Ņ–Ņ†ŅŒ ŅŅ‚Đ°Ņ€ĐžĐĩ ĐŧĐĩĐŊŅŽ ĐŊаĐģад</string>
<string name="revanced_restore_old_settings_menus_summary_on">ĐĄŅ‚Đ°Ņ€Ņ‹Ņ ĐŧĐĩĐŊŅŽ ĐŊаĐģад ĐŋаĐēĐ°ĐˇĐ˛Đ°ŅŽŅ†Ņ†Đ°</string>
<string name="revanced_restore_old_settings_menus_summary_off">ĐĄŅ‚Đ°Ņ€Ņ‹Ņ ĐŧĐĩĐŊŅŽ ĐŊаĐģад ĐŊĐĩ ĐŋаĐēĐ°ĐˇĐ˛Đ°ŅŽŅ†Ņ†Đ°</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">АдĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊĐĩ Shorts ҃ Ņ„ĐžĐŊĐ°Đ˛Ņ‹Đŧ</string>
@@ -446,6 +506,9 @@ Second \"item\" text"</string>
<string name="revanced_swipe_overlay_background_alpha_summary">Đ‘Đ°Ņ‡ĐŊĐ°ŅŅ†ŅŒ Ņ„ĐžĐŊ҃ ĐŊаĐēĐģадаĐŊĐŊŅ ĐŋаĐģŅŒŅ†Đ°Đŧ</string>
<string name="revanced_swipe_threshold_title">ĐŸĐ°Ņ€ĐžĐŗ вĐĩĐģҖ҇ҋĐŊŅ– ĐŋаĐģŅŒŅ†Đ°Đŧ</string>
<string name="revanced_swipe_threshold_summary">ВĐĩĐģҖ҇ҋĐŊŅ ĐŋĐ°Ņ€ĐžĐŗĐ°Đ˛Đ°ĐŗĐ° СĐŊĐ°Ņ‡ŅĐŊĐŊŅ Đ´ĐģŅ ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊŅ ĐŋаĐģŅŒŅ†Đ°Đŧ</string>
<string name="revanced_swipe_change_video_title">ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐˇŅŒĐŧĐĩĐŊ҃ Đ˛Ņ–Đ´ŅĐ° ĐŋŅ€Đ°Đˇ ĐŋŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐŋаĐģŅŒŅ†Đ°Đŧ</string>
<string name="revanced_swipe_change_video_summary_on">ĐŸŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐŋаĐģŅŒŅ†Đ°Đŧ ҃ Ņ€ŅĐļŅ‹ĐŧĐĩ ĐŋĐžŅžĐŊĐ°ĐŗĐ° ŅĐēŅ€Đ°ĐŊа ĐˇŅŒĐŧĐĩĐŊŅ–Ņ†ŅŒ Đ˛Ņ–Đ´ŅĐ° ĐŊа ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊаĐĩ/ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅĐĩ</string>
<string name="revanced_swipe_change_video_summary_off">ĐŸŅ€Đ°Đ˛ŅĐ´ĐˇĐĩĐŊĐŊĐĩ ĐŋаĐģŅŒŅ†Đ°Đŧ ҃ Ņ€ŅĐļŅ‹ĐŧĐĩ ĐŋĐžŅžĐŊĐ°ĐŗĐ° ŅĐēŅ€Đ°ĐŊа ĐŊĐĩ ĐˇŅŒĐŧĐĩĐŊŅ–Ņ†ŅŒ Đ˛Ņ–Đ´ŅĐ° ĐŊа ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊаĐĩ/ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅĐĩ</string>
</patch>
<patch id="layout.autocaptions.autoCaptionsPatch">
<string name="revanced_auto_captions_title">АдĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ Đ°ŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊŅ‹Ņ ҆Җ҂Ҁҋ</string>
@@ -629,15 +692,18 @@ Second \"item\" text"</string>
<string name="revanced_shorts_player_screen_summary">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ айО ĐŋаĐēĐ°ĐˇĐ°Ņ†ŅŒ ĐēаĐŧĐŋаĐŊĐĩĐŊ҂ҋ Ņž ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐģҌĐŊŅ–Đē҃ Shorts</string>
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ŅˆĐžŅ€Ņ‚Ņ‹ Ņž Ņ…Đ°Ņ‚ĐŊŅĐš ŅŅ‚ŅƒĐļ҆ҋ</string>
<string name="revanced_hide_shorts_home_summary_on">Shorts ҃ ŅŅ‚ŅƒĐļ҆ҋ Ņ…Đ°Ņ‚ĐŊŅĐš ŅŅ‚Đ°Ņ€ĐžĐŊĐēŅ– ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_hide_shorts_home_summary_off">Shorts ҃ ŅŅ‚ŅƒĐļ҆ҋ Ņ…Đ°Ņ‚ĐŊŅĐš ŅŅ‚Đ°Ņ€ĐžĐŊĐēŅ– ĐŋаĐēаСаĐŊŅ‹</string>
<string name="revanced_hide_shorts_home_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊŅ‹ Ņž ŅŅ‚ŅƒĐļ҆ҋ Ņ– ĐˇĐ˛ŅĐˇĐ°ĐŊҋ҅ Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_hide_shorts_home_summary_off">ПаĐēаСаĐŊŅ‹ Ņž ŅŅ‚ŅƒĐļ҆ҋ Ņ– ĐˇĐ˛ŅĐˇĐ°ĐŊҋ҅ Đ˛Ņ–Đ´ŅĐ°</string>
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_hide_shorts_subscriptions_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ Shorts ҃ ŅŅ‚ŅƒĐļ҆ҋ ĐŋадĐŋҖҁĐēŅ–</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Shorts ҃ ŅŅ‚ŅƒĐļ҆ҋ ĐŋадĐŋҖҁĐēŅ– ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">Shorts ҃ ŅŅ‚ŅƒĐļ҆ҋ ĐŋадĐŋҖҁĐēŅ– ĐŋаĐēаСаĐŊŅ‹</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊа Ņž ŅŅ‚ŅƒĐļ҆ҋ ĐŋадĐŋŅ–ŅĐ°Đē</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">ПаĐēаСаĐŊа Ņž ŅŅ‚ŅƒĐļ҆ҋ ĐŋадĐŋŅ–ŅĐ°Đē</string>
<string name="revanced_hide_shorts_search_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ Shorts ҃ Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_shorts_search_summary_on">Shorts ҃ Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃ ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_hide_shorts_search_summary_off">Shorts ҃ Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃ ĐŋаĐēаСаĐŊŅ‹</string>
<string name="revanced_hide_shorts_search_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊŅ‹ Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_shorts_search_summary_off">ПаĐēаСаĐŊа Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_shorts_history_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ Shorts С ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ– ĐŋŅ€Đ°ĐŗĐģŅĐ´Đ°Ņž</string>
<string name="revanced_hide_shorts_history_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊа Ņž ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ– ĐŋŅ€Đ°ĐŗĐģŅĐ´Đ°Ņž</string>
<string name="revanced_hide_shorts_history_summary_off">ПаĐēаСаĐŊŅ‹ Ņž ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ– ĐŋŅ€Đ°ĐŗĐģŅĐ´Đ°Ņž</string>
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_join_button_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐēĐŊĐžĐŋĐē҃ даĐģŅƒŅ‡Ņ‹Ņ†Ņ†Đ°</string>
<string name="revanced_hide_shorts_join_button_summary_on">КĐŊĐžĐŋĐēа ÂĢДаĐģŅƒŅ‡Ņ‹Ņ†Ņ†Đ°Âģ ŅŅ…Đ°Đ˛Đ°ĐŊа</string>
@@ -739,6 +805,13 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_popup_panels_summary_on">ĐŖŅĐŋĐģŅ‹Đ˛Đ°ĐģҌĐŊŅ‹Ņ ĐŋаĐŊŅĐģŅ– ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐģҌĐŊŅ–Đēа ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹Ņ</string>
<string name="revanced_hide_player_popup_panels_summary_off">ПаĐēĐ°ĐˇĐ˛Đ°ŅŽŅ†Ņ†Đ° ŅžŅĐŋĐģŅ‹Đ˛Đ°ĐģҌĐŊŅ‹Ņ ĐŋаĐŊŅĐģŅ– ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐģҌĐŊŅ–Đēа</string>
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
<string name="revanced_exit_fullscreen_title">Đ’Ņ‹ĐšŅŅ†Ņ– С ĐŋĐžŅžĐŊĐ°ŅĐēŅ€Đ°ĐŊĐŊĐ°ĐŗĐ° Ņ€ŅĐļŅ‹Đŧ҃ Ņž ĐēаĐŊ҆ҋ Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_exit_fullscreen_entry_1">АдĐēĐģŅŽŅ‡Đ°ĐŊа</string>
<string name="revanced_exit_fullscreen_entry_2">ĐŸĐ°Ņ€Ņ‚Ņ€ŅŅ‚</string>
<string name="revanced_exit_fullscreen_entry_3">ЛаĐŊĐ´ŅˆĐ°Ņ„Ņ‚</string>
<string name="revanced_exit_fullscreen_entry_4">ĐŸĐ°Ņ€Ņ‚Ņ€ŅŅ‚ Ņ– ĐģаĐŊĐ´ŅˆĐ°Ņ„Ņ‚</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">ĐžŅ‚ĐēŅ€Ņ‹Đ˛Đ°Ņ‚ŅŒ видĐĩĐž ĐŊа вĐĩҁҌ ŅĐēŅ€Đ°ĐŊ в ĐŋĐžŅ€Ņ‚Ņ€ĐĩŅ‚ĐŊĐžĐŧ Ņ€ĐĩĐļиĐŧĐĩ</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">ВидĐĩĐž ĐžŅ‚ĐēŅ€Ņ‹Đ˛Đ°ŅŽŅ‚ŅŅ ĐŊа вĐĩҁҌ ŅĐēŅ€Đ°ĐŊ</string>
@@ -1008,6 +1081,23 @@ Second \"item\" text"</string>
<string name="revanced_sb_reset">ĐĄĐēŅ–ĐŊŅƒŅ†ŅŒ</string>
<string name="revanced_sb_about">ĐŸŅ€Đ° ĐŋŅ€Đ°ĐŗŅ€Đ°Đŧ҃</string>
<string name="revanced_sb_about_api_sum">ДадзĐĩĐŊŅ‹Ņ ĐŋŅ€Đ°Đ´Đ°ŅŅ‚Đ°ŅžĐģŅŅŽŅ†Ņ†Đ° API SponsorBlock. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ Ņ‚ŅƒŅ‚, Đēай давĐĩĐ´Đ°Ņ†Ņ†Đ° йОĐģҌ҈ Ņ– ĐŋĐ°ĐŗĐģŅĐ´ĐˇĐĩŅ†ŅŒ ҁĐŋаĐŧĐŋĐžŅžĐēŅ– Đ´ĐģŅ Ņ–ĐŊŅˆŅ‹Ņ… ĐŋĐģĐ°Ņ‚Ņ„ĐžŅ€ĐŧĐ°Ņž</string>
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
<string name="revanced_change_form_factor_title">Đ¤Đ°Ņ€ĐŧĐ°Ņ‚ ŅĐēŅ€Đ°ĐŊа</string>
<string name="revanced_change_form_factor_entry_1">Па СĐŧĐ°ŅžŅ‡Đ°ĐŊĐŊŅŽ</string>
<string name="revanced_change_form_factor_entry_2">ĐĸŅĐģĐĩŅ„ĐžĐŊ</string>
<string name="revanced_change_form_factor_entry_3">ПĐģаĐŊŅˆŅŅ‚</string>
<string name="revanced_change_form_factor_entry_4">ĐŅžŅ‚Đ°ĐŧĐ°ĐąŅ–ĐģҌĐŊŅ‹</string>
<string name="revanced_change_form_factor_user_dialog_message">"ЗĐŧĐĩĐŊŅ‹ ŅžĐēĐģŅŽŅ‡Đ°ŅŽŅ†ŅŒ:
Đ Đ°ŅĐēĐģадĐēа ĐŋĐģаĐŊŅˆŅŅ‚Đ°
â€ĸ ПавĐĩдаĐŧĐģĐĩĐŊĐŊŅ– ҁ҃ĐŋĐžĐģҌĐŊĐ°ŅŅ†Ņ– ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹
Đ Đ°ŅĐēĐģадĐēа Đ°ŅžŅ‚Đ°ĐŧĐ°ĐąŅ–ĐģŅ
â€ĸ МĐĩĐŊŅŽ ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ– ĐŋŅ€Đ°ĐŗĐģŅĐ´Đ°Ņž ŅŅ…Đ°Đ˛Đ°ĐŊа
â€ĸ АдĐŊĐžŅžĐģĐĩĐŊа ŅžĐēĐģадĐēа ÂĢĐ”Đ°ŅĐģĐĩĐ´Đ°Đ˛Đ°Ņ†ŅŒÂģ
â€ĸ Đ ĐžĐģŅ–ĐēŅ– Shorts адĐēŅ€Ņ‹Đ˛Đ°ŅŽŅ†Ņ†Đ° Ņž ĐˇĐ˛Ņ‹Ņ‡Đ°ĐšĐŊŅ‹Đŧ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐģҌĐŊŅ–Đē҃
â€ĸ ĐĄŅ‚ŅƒĐļĐēа Đ°Ņ€ĐŗĐ°ĐŊŅ–ĐˇĐ°Đ˛Đ°ĐŊа Đŋа Ņ‚ŅĐŧĐ°Ņ… Ņ– ĐēаĐŊаĐģĐ°Ņ…"</string>
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<string name="revanced_spoof_app_version_title">ВĐĩŅ€ŅŅ–Ņ ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧŅ‹ Spoof</string>
@@ -1022,6 +1112,7 @@ Second \"item\" text"</string>
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">ĐŸĐ°Đ´Ņ€ĐžĐąĐēа ĐŧŅŅ‚Đ°Đ˛Đ°Đš вĐĩҀҁҖҖ ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧŅ‹</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 — Đ’ĐžŅŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚ŅŒ ŅŅ‚Đ°Ņ€Ņ‹Đĩ СĐŊĐ°Ņ‡Đēи ĐŋĐģĐĩĐĩŅ€Đ° Shorts</string>
<string name="revanced_spoof_app_version_target_entry_2">19.26.42 - АдĐŊĐ°ŅžĐģĐĩĐŊĐŊĐĩ ŅŅ‚Đ°Ņ€Ņ‹Ņ… СĐŊĐ°Ņ‡ĐēĐžŅž ĐŊĐ°Đ˛Ņ–ĐŗĐ°Ņ†Ņ‹Ņ–</string>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_legacy_entry_1">18.33.40 - АдĐŊĐ°ŅžĐģĐĩĐŊĐŊĐĩ RYD ĐŊа Shorts ҃ Ņ€ŅĐļŅ‹ĐŧĐĩ Ņ–ĐŊĐēĐžĐŗĐŊŅ–Ņ‚Đ°</string>
<string name="revanced_spoof_app_version_target_legacy_entry_2">18.20.39 - АдĐŊĐ°Đ˛Ņ–Ņ†ŅŒ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†ŅŒ ŅˆŅ‹Ņ€ĐžĐēĐ°ĐŗĐ° Đ˛Ņ–Đ´ŅĐ° &amp; ŅĐēĐ°ŅĐŊаĐĩ ĐŧĐĩĐŊŅŽ</string>
@@ -1065,12 +1156,6 @@ Second \"item\" text"</string>
<string name="revanced_shorts_autoplay_background_summary_on">Shorts в Ņ„ĐžĐŊОвОĐŧ Ņ€ĐĩĐļиĐŧĐĩ ĐąŅƒĐ´ŅƒŅ‚ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐžĐ´Đ¸Ņ‚ŅŒŅŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐĩҁĐēи</string>
<string name="revanced_shorts_autoplay_background_summary_off">Shorts в Ņ„ĐžĐŊОвОĐŧ Ņ€ĐĩĐļиĐŧĐĩ ĐąŅƒĐ´ŅƒŅ‚ ĐŋĐžĐ˛Ņ‚ĐžŅ€ŅŅ‚ŅŒŅŅ</string>
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
<string name="revanced_tablet_layout_title">ĐŖĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐŧаĐēĐĩŅ‚ ĐŋĐģаĐŊŅˆŅŅ‚Đ°</string>
<string name="revanced_tablet_layout_summary_on">МаĐēĐĩŅ‚ ĐŋĐģаĐŊŅˆŅŅ‚Đ° ŅžĐēĐģŅŽŅ‡Đ°ĐŊŅ‹</string>
<string name="revanced_tablet_layout_summary_off">МаĐēĐĩŅ‚ ĐŋĐģаĐŊŅˆŅŅ‚Đ° адĐēĐģŅŽŅ‡Đ°ĐŊŅ‹</string>
<string name="revanced_tablet_layout_user_dialog_message">ПавĐĩдаĐŧĐģĐĩĐŊĐŊŅ– Ņž ҁ҃ĐŋĐžĐģҌĐŊĐ°ŅŅ†Ņ– ĐŊĐĩ адĐģŅŽŅŅ‚Ņ€ĐžŅžĐ˛Đ°ŅŽŅ†Ņ†Đ° ĐŊа ĐŋĐģаĐŊŅˆŅŅ†Đĩ</string>
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">ĐœŅ–ĐŊŅ–-ĐŋĐģŅĐĩŅ€</string>
<string name="revanced_miniplayer_screen_summary">ЗĐŧŅĐŊҖ҆Đĩ ҁ҂ҋĐģҌ ĐŧŅ–ĐŊŅ–ĐŧŅ–ĐˇĐ°Đ˛Đ°ĐŊĐ°ĐŗĐ° ĐŋĐģŅĐĩŅ€Đ° Ņž ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧĐĩ</string>
@@ -1234,6 +1319,7 @@ Second \"item\" text"</string>
<string name="revanced_force_original_audio_title">Đ’Ņ‹ĐŧŅƒŅˆĐ°ĐŊаĐĩ Đ°Ņ€Ņ‹ĐŗŅ–ĐŊаĐģҌĐŊаĐĩ Đ°ŅžĐ´Ņ‹Ņ‘</string>
<string name="revanced_force_original_audio_summary_on">Đ’Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐŊĐŊĐĩ Đ°Ņ€Ņ‹ĐŗŅ–ĐŊаĐģҌĐŊĐ°ĐŗĐ° Đ°ŅžĐ´Ņ‹Ņ‘</string>
<string name="revanced_force_original_audio_summary_off">Đ’Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐŊĐŊĐĩ Đ°ŅžĐ´Ņ‹Ņ‘ Đŋа СĐŧĐ°ŅžŅ‡Đ°ĐŊĐŊŅ–</string>
<string name="revanced_force_original_audio_not_available">Каб Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°Ņ†ŅŒ ĐŗŅŅ‚ŅƒŅŽ Ņ„ŅƒĐŊĐēŅ†Ņ‹ŅŽ, СĐŧĐĩĐŊҖ҆Đĩ ҁĐŋĐ°Ņ„Ņ–ĐŊĐŗ ĐŋĐ°Ņ‚ĐžĐē҃ ĐŊа ҂ҋĐŋ ĐēĐģŅ–ĐĩĐŊŅ‚Đ° iOS</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
@@ -1261,6 +1347,8 @@ Second \"item\" text"</string>
<string name="revanced_custom_playback_speeds_invalid">НĐĩŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊŅ‹Ņ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†Ņ– ĐŋĐ°Đ˛Ņ–ĐŊĐŊŅ‹ ĐąŅ‹Ņ†ŅŒ ĐŧĐĩĐŊ҈ Са %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">НĐĩŅĐ°ĐŋŅ€Đ°ŅžĐ´ĐŊŅ‹Ņ ĐŊĐĩŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊŅ‹Ņ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†Ņ– ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊŅ</string>
<string name="revanced_custom_playback_speeds_auto">ĐŅžŅ‚Đž</string>
<string name="revanced_speed_tap_and_hold_title">ĐŖĐģĐ°ŅĐŊŅ‹ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†ŅŒ ĐŊĐ°Ņ†Ņ–ŅĐē҃ Ņ– ŅžŅ‚Ņ€Ņ‹ĐŧаĐŊĐŊŅ</string>
<string name="revanced_speed_tap_and_hold_summary">ĐĨŅƒŅ‚ĐēĐ°ŅŅ†ŅŒ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊŅ ĐŧŅ–Đļ 0-8</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
<string name="revanced_remember_playback_speed_last_selected_title">ЗаĐŋĐžĐŧĐŊҖ҆Đĩ СĐŧĐĩĐŊŅ‹ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†Ņ– ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊŅ</string>
@@ -1289,65 +1377,27 @@ Second \"item\" text"</string>
ĐŸŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊĐĩ Đ˛Ņ–Đ´ŅĐ° ĐŧĐžĐļа ĐŊĐĩ ĐŋŅ€Đ°Ņ†Đ°Đ˛Đ°Ņ†ŅŒ"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">АдĐēĐģŅŽŅ‡ŅĐŊĐŊĐĩ ĐŗŅŅ‚Đ°Đš ĐŊаĐģĐ°Đ´Ņ‹ ĐŧĐžĐļа Đ˛Ņ‹ĐēĐģŅ–ĐēĐ°Ņ†ŅŒ ĐŋŅ€Đ°ĐąĐģĐĩĐŧŅ‹ С ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊĐĩĐŧ Đ˛Ņ–Đ´ŅĐ°.</string>
<string name="revanced_spoof_video_streams_client_type_title">КĐģиĐĩĐŊŅ‚ ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ</string>
<string name="revanced_spoof_video_streams_about_title">ĐŸĐ°Đ´Ņ€ĐžĐąĐēа ĐŋĐ°ĐąĐžŅ‡ĐŊҋ҅ ŅŅ„ĐĩĐēŅ‚Đ°Ņž</string>
<string name="revanced_spoof_video_streams_about_summary">"â€ĸ Đ”ĐˇŅ–Ņ†ŅŅ‡Ņ‹Ņ Đ˛Ņ–Đ´ŅĐ° ĐŧĐžĐŗŅƒŅ†ŅŒ ĐŊĐĩ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°Ņ†Ņ†Đ°
â€ĸ ПаĐŊŅĐģҌ ĐŗŅƒĐēĐ°Đ˛Ņ‹Ņ… Đ´Đ°Ņ€ĐžĐļаĐē Đ°Đ´ŅŅƒŅ‚ĐŊŅ–Ņ‡Đ°Đĩ
<!-- 'no auth' means no authentication -->
<string name="revanced_spoof_video_streams_client_type_android_vr_no_auth">Android VR (ĐŊŅĐŧа Đ°ŅžŅ‚ŅĐŊ҂ҋ҄ҖĐēĐ°Ņ†Ņ‹Ņ–)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">Đ’Ņ‹ĐŧŅƒŅŅ–Ņ†ŅŒ iOS AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Đ’Ņ–Đ´ŅĐ°ĐēaĐ´ŅĐē ĐˇĐ°Ņ„Ņ–ĐēŅĐ°Đ˛Đ°ĐŊŅ‹ Ņž AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Đ’Ņ–Đ´ŅĐ°ĐēaĐ´ŅĐē Đ˛Ņ‹ĐˇĐŊĐ°Ņ‡Đ°ĐĩŅ†Ņ†Đ° Đ°ŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊа</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"ĐŖĐēĐģŅŽŅ‡ŅĐŊĐŊĐĩ ĐŗŅŅ‚Đ°ĐŗĐ° ĐŧĐžĐļа ĐŋаĐģĐĩĐŋŅˆŅ‹Ņ†ŅŒ Ņ‡Đ°Ņ Đ°ŅžŅ‚Đ°ĐŊĐžĐŧĐŊаК ĐŋŅ€Đ°Ņ†Ņ‹ Ņ– Đ˛Ņ‹ĐŋŅ€Đ°Đ˛Ņ–Ņ†ŅŒ ĐˇĐ°Ņ–ĐēаĐŊĐŊĐĩ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊŅ.
AVC ĐŧаĐĩ ĐŧаĐēҁҖĐŧаĐģҌĐŊаĐĩ даСвОĐģ 1080p, Đ°ŅžĐ´Ņ‹Ņ‘ĐēĐ°Đ´ŅĐē Opus ĐŊĐĩĐ´Đ°ŅŅ‚ŅƒĐŋĐŊŅ‹, а ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊĐĩ Đ˛Ņ–Đ´ŅĐ° ĐąŅƒĐ´ĐˇĐĩ Đ˛Ņ‹ĐēĐ°Ņ€Ņ‹ŅŅ‚ĐžŅžĐ˛Đ°Ņ†ŅŒ йОĐģҌ҈ Ņ–ĐŊŅ‚ŅŅ€ĐŊŅŅ‚-даĐŊҋ҅, ҇ҋĐŧ VP9 айО AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">ĐŸĐ°ĐąĐžŅ‡ĐŊŅ‹Ņ ŅŅ„ĐĩĐē҂ҋ ĐŋĐ°Đ´Ņ€ĐžĐąĐēŅ– iOS</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"â€ĸ Đ¤Ņ–ĐģҌĐŧŅ‹ айО ĐŋĐģĐ°Ņ‚ĐŊŅ‹Ņ Đ˛Ņ–Đ´ŅĐ° ĐŧĐžĐŗŅƒŅ†ŅŒ ĐŊĐĩ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°Ņ†Ņ†Đ°
â€ĸ ĐĄŅ‚Đ°ĐąŅ–ĐģҌĐŊĐ°Ņ ĐŗŅƒŅ‡ĐŊĐ°ŅŅ†ŅŒ ĐŊĐĩĐ´Đ°ŅŅ‚ŅƒĐŋĐŊĐ°Ņ
â€ĸ ĐŸŅ€Ņ‹ĐŧŅƒŅĐžĐ˛Đ° Đ°Ņ€Ņ‹ĐŗŅ–ĐŊаĐģҌĐŊĐ°Ņ Đ°ŅžĐ´Ņ‹Ņ‘Đ´Đ°Ņ€ĐžĐļĐēа ĐŊĐĩĐ´Đ°ŅŅ‚ŅƒĐŋĐŊĐ°Ņ"</string>
<string name="revanced_spoof_video_streams_language_title">Đ¯ĐˇŅ‹Đē ĐŋĐžŅ‚ĐžĐēа Đ°ŅƒĐ´Đ¸Đž ĐŋĐž ҃ĐŧĐžĐģŅ‡Đ°ĐŊĐ¸ŅŽ</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">Đ¯ĐˇŅ‹Đē ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊĐ¸Ņ</string>
<string name="revanced_spoof_video_streams_language_AR">ĐŅ€Đ°ĐąŅĐēиК</string>
<string name="revanced_spoof_video_streams_language_AZ">АСĐĩŅ€ĐąĐ°ĐšĐ´ĐļаĐŊҁĐēиК</string>
<string name="revanced_spoof_video_streams_language_BG">БоĐģĐŗĐ°Ņ€ŅĐēиК</string>
<string name="revanced_spoof_video_streams_language_BN">БĐĩĐŊĐŗĐ°ĐģҌҁĐēиК</string>
<string name="revanced_spoof_video_streams_language_CA">ĐšĐ°Ņ‚Đ°ĐģĐžĐŊҁĐēиК</string>
<string name="revanced_spoof_video_streams_language_CS">ЧĐĩ҈ҁĐēиК</string>
<string name="revanced_spoof_video_streams_language_DA">Đ”Đ°Ņ‚ŅĐēиК</string>
<string name="revanced_spoof_video_streams_language_DE">НĐĩĐŧĐĩ҆ĐēиК</string>
<string name="revanced_spoof_video_streams_language_EL">Đ“Ņ€Đĩ҇ĐĩҁĐēиК</string>
<string name="revanced_spoof_video_streams_language_EN">АĐŊĐŗĐģŅ–ĐšŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_ES">Đ†ŅĐŋаĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_ET">Đ­ŅŅ‚ĐžĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_FA">Đ¤Đ°Ņ€ŅŅ–</string>
<string name="revanced_spoof_video_streams_language_FI">Đ¤Ņ–ĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_FR">Đ¤Ņ€Đ°ĐŊŅ†ŅƒĐˇŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_GU">Đ“ŅƒŅŅ€Đ°Ņ‚Ņ–</string>
<string name="revanced_spoof_video_streams_language_HI">ĐĨŅĐŊĐ´Ņ–</string>
<string name="revanced_spoof_video_streams_language_HR">ĐĨĐžŅ€Đ˛Đ°Ņ‚ŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_HU">ВĐĩĐŊĐŗĐĩҀҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_ID">ИĐŊдаĐŊĐĩĐˇŅ–ĐšŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_IT">Đ˜Ņ‚Đ°ĐģŅŒŅĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_JA">Đ¯ĐŋĐžĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_KK">ĐšĐ°ĐˇĐ°Ņ…ŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_KO">ĐšĐžŅ€ŅĐšŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_LT">Đ›Ņ–Ņ‚ĐžŅƒŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_LV">Đ›Đ°Ņ‚Ņ‹ŅˆŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_MK">МаĐēĐĩĐ´ĐžĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_MN">МоĐŊĐŗĐžĐģҌҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_MR">ĐœĐ°Ņ€Đ°Ņ‚Ņ…Ņ–</string>
<string name="revanced_spoof_video_streams_language_MS">МаĐģĐ°ĐšŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_MY">Đ‘Ņ€Ņ‹ĐŧаĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_NL">ĐŅ–Đ´Ņ€ĐģаĐŊĐ´ŅĐēиК</string>
<string name="revanced_spoof_video_streams_language_OR">ĐžŅ€Ņ–Ņ</string>
<string name="revanced_spoof_video_streams_language_PA">ПаĐŊĐ´ĐļĐ°ĐąŅĐēиК</string>
<string name="revanced_spoof_video_streams_language_PL">ПоĐģҌҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_PT_BR">ĐŸĐ°Ņ€Ņ‚ŅƒĐŗĐ°ĐģҌҁĐēĐ°Ņ (Đ‘Ņ€Đ°ĐˇŅ–ĐģŅ–Ņ)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">ĐŸĐ°Ņ€Ņ‚ŅƒĐŗĐ°ĐģҌҁĐēĐ°Ņ (ĐŸĐ°Ņ€Ņ‚ŅƒĐŗĐ°ĐģŅ–Ņ)</string>
<string name="revanced_spoof_video_streams_language_RO">Đ ŅƒĐŧŅ‹ĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_RU">Đ ŅƒŅŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_SK">ĐĄĐģĐžĐ˛Đ°Ņ†ĐēĐ¸Ņ</string>
<string name="revanced_spoof_video_streams_language_SL">ĐĄĐģОвĐĩĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_SR">ĐĄĐĩŅ€ĐąŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_SV">ШвĐĩ҆ĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_SW">ĐĄŅƒĐ°Ņ…Ņ–ĐģŅ–</string>
<string name="revanced_spoof_video_streams_language_TA">ĐĸаĐŧŅ–ĐģҌҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_TE">ĐĸŅĐģŅƒĐŗŅƒ</string>
<string name="revanced_spoof_video_streams_language_TH">ĐĸĐ°ĐšŅĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_TR">ĐĸŅƒŅ€ŅŅ†ĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_UK">ĐŖĐēŅ€Đ°Ņ‘ĐŊҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_UR">ĐŖŅ€Đ´Ņƒ</string>
<string name="revanced_spoof_video_streams_language_VI">Đ’Ņ–ĐĩŅ‚ĐŊаĐŧҁĐēĐ°Ņ</string>
<string name="revanced_spoof_video_streams_language_ZH">ĐšŅ–Ņ‚Đ°ĐšŅĐēĐ°Ņ</string>
â€ĸ Đ’Ņ–Đ´ŅĐ° СаĐēаĐŊŅ‡Đ˛Đ°ŅŽŅ†Ņ†Đ° ĐŊа 1 ҁĐĩĐē҃ĐŊĐ´Ņƒ Ņ€Đ°ĐŊĐĩĐš"</string>
<string name="revanced_spoof_video_streams_about_android_title">ĐŸĐžĐąŅ–Ņ‡ĐŊŅ‹Ņ ŅŅ„ĐĩĐē҂ҋ ĐŋĐ°Đ´Ņ€ĐžĐąĐēŅ– Android</string>
<string name="revanced_spoof_video_streams_about_android_summary">"â€ĸ МĐĩĐŊŅŽ ĐŗŅƒĐēавОК Đ´Đ°Ņ€ĐžĐļĐēŅ– Đ°Đ´ŅŅƒŅ‚ĐŊŅ–Ņ‡Đ°Đĩ
â€ĸ ĐĄŅ‚Đ°ĐąŅ–ĐģҌĐŊŅ‹ ĐŗŅƒĐē ĐŊĐĩĐ´Đ°ŅŅ‚ŅƒĐŋĐŊŅ‹
â€ĸ ĐŸŅ€Ņ‹ĐŧŅƒŅĐžĐ˛Đ°Đĩ Đ°Ņ€Ņ‹ĐŗŅ–ĐŊаĐģҌĐŊаĐĩ Đ°ŅžĐ´Ņ‹Ņ‘ ĐŊĐĩĐ´Đ°ŅŅ‚ŅƒĐŋĐŊа"</string>
<string name="revanced_spoof_video_streams_about_no_av1">â€ĸ ĐŅĐŧа Đ˛Ņ–Đ´ŅĐ°ĐēŅ–Đ´Đ°Đ˛Đ°ĐŊĐŊŅ AV1</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">ПаĐēĐ°ĐˇĐ°Ņ†ŅŒ ҃ ŅŅ‚Đ°Ņ‚Ņ‹ŅŅ‚Ņ‹Ņ†Ņ‹ Đ´ĐģŅ ҁĐŋĐĩŅ†Ņ‹ŅĐģŅ–ŅŅ‚Đ°Ņž</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">ĐĸŅ‹Đŋ ĐēĐģŅ–ĐĩĐŊŅ‚Đ° адĐģŅŽŅŅ‚Ņ€ĐžŅžĐ˛Đ°ĐĩŅ†Ņ†Đ° Ņž ŅŅ‚Đ°Ņ‚Ņ‹ŅŅ‚Ņ‹Ņ†Ņ‹ Đ´ĐģŅ ҁĐŋĐĩŅ†Ņ‹ŅĐģŅ–ŅŅ‚Đ°Ņž</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">КĐģŅ–ĐĩĐŊŅ‚ ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹ Ņž ŅŅ‚Đ°Ņ‚Ņ‹ŅŅ‚Ņ‹Ņ†Ņ‹ Đ´ĐģŅ ҁĐŋĐĩŅ†Ņ‹ŅĐģŅ–ŅŅ‚Đ°Ņž</string>
<string name="revanced_spoof_video_streams_language_title">Мова ĐŗŅƒĐēавОК Đ´Đ°Ņ€ĐžĐļĐēŅ– Đŋа СĐŧĐ°ŅžŅ‡Đ°ĐŊĐŊŅ– Đ´ĐģŅ VR</string>
</patch>
</app>
<app id="twitch">

View File

@@ -33,6 +33,7 @@ Second \"item\" text"</string>
<string name="revanced_check_environment_not_near_patch_time_invalid">Đ”Đ°Ņ‚Đ°Ņ‚Đ° ĐŊа ĐēĐžĐŧĐŋиĐģĐ°Ņ†Đ¸Ņ ĐŊа APK Đĩ ĐŋĐžĐ˛Ņ€ĐĩĐ´ĐĩĐŊа</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_submenu_title">ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēи</string>
<string name="revanced_settings_title">ReVanced</string>
<string name="revanced_settings_confirm_user_dialog_title">Đ˜ŅĐēĐ°Ņ‚Đĩ Đģи да ĐŋŅ€ĐžĐ´ŅŠĐģĐļĐ¸Ņ‚Đĩ?</string>
<string name="revanced_settings_reset">Đ’ŅŠĐˇŅŅ‚Đ°ĐŊОви</string>
@@ -43,6 +44,62 @@ Second \"item\" text"</string>
<string name="revanced_settings_import_reset">ĐĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ ĐŊа ReVanced ĐąŅŅ…Đ° ĐŊ҃ĐģĐ¸Ņ€Đ°ĐŊи</string>
<string name="revanced_settings_import_success">ĐĄĐģĐĩĐ´ĐŊĐ¸Ņ‚Đĩ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐąŅŅ…Đ° иĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊи ҃ҁĐŋĐĩ҈ĐŊĐž: %d</string>
<string name="revanced_settings_import_failure_parse">ИĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩŅ‚Đž ĐąĐĩ҈Đĩ ĐŊĐĩ҃ҁĐŋĐĩ҈ĐŊĐž: %s</string>
<string name="revanced_language_title">ЕзиĐē ĐŊа ReVanced</string>
<string name="revanced_language_user_dialog_message">"ĐŸŅ€ĐĩĐ˛ĐžĐ´Đ¸Ņ‚Đĩ ĐŊа ĐŊŅĐēОи ĐĩĐˇĐ¸Ņ†Đ¸ ĐŧĐžĐļĐĩ да ĐģиĐŋŅĐ˛Đ°Ņ‚ иĐģи да ŅĐ° ĐŊĐĩĐŋҊĐģĐŊи.
За да ĐŋŅ€ĐĩвĐĩĐ´ĐĩŅ‚Đĩ ĐŊОви ĐĩĐˇĐ¸Ņ†Đ¸, ĐŋĐžŅĐĩŅ‚ĐĩŅ‚Đĩ translate.revanced.app"</string>
<string name="revanced_language_DEFAULT">ЕзиĐē ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž</string>
<string name="revanced_language_AR">Đ°Ņ€Đ°ĐąŅĐēи</string>
<string name="revanced_language_AZ">АСĐĩŅ€ĐąĐ°ĐšĐ´ĐļаĐŊҁĐēи</string>
<string name="revanced_language_BG">ĐąŅŠĐģĐŗĐ°Ņ€ŅĐēи</string>
<string name="revanced_language_BN">ĐąĐĩĐŊĐŗĐ°ĐģҁĐēи</string>
<string name="revanced_language_CA">ĐēĐ°Ņ‚Đ°ĐģĐžĐŊҁĐēи</string>
<string name="revanced_language_CS">ЧĐĩ҈Đēи</string>
<string name="revanced_language_DA">Đ”Đ°Ņ‚ŅĐēи</string>
<string name="revanced_language_DE">НĐĩĐŧҁĐēи</string>
<string name="revanced_language_EL">Đ“Ņ€ŅŠŅ†Đēи</string>
<string name="revanced_language_EN">АĐŊĐŗĐģĐ¸ĐšŅĐēи</string>
<string name="revanced_language_ES">Đ˜ŅĐŋаĐŊҁĐēи</string>
<string name="revanced_language_ET">Đ•ŅŅ‚ĐžĐŊҁĐēи</string>
<string name="revanced_language_FA">ПĐĩŅ€ŅĐ¸ĐšŅĐēи</string>
<string name="revanced_language_FI">ФиĐŊĐģаĐŊĐ´ŅĐēи</string>
<string name="revanced_language_FR">Đ¤Ņ€ĐĩĐŊҁĐēи</string>
<string name="revanced_language_GU">Đ“ŅƒĐ´ĐļĐ°Ņ€Đ°Ņ‚Đ¸</string>
<string name="revanced_language_HI">ĐĨиĐŊди</string>
<string name="revanced_language_HR">ĐĨŅŠŅ€Đ˛Đ°Ņ‚ŅĐēи</string>
<string name="revanced_language_HU">ĐŖĐŊĐŗĐ°Ņ€ŅĐēи</string>
<string name="revanced_language_ID">ИĐŊĐ´ĐžĐŊĐĩĐˇĐ¸ĐšŅĐēи</string>
<string name="revanced_language_IT">Đ˜Ņ‚Đ°ĐģиаĐŊҁĐēи</string>
<string name="revanced_language_JA">Đ¯ĐŋĐžĐŊҁĐēи</string>
<string name="revanced_language_KK">ĐšĐ°ĐˇĐ°Ņ…ŅĐēи</string>
<string name="revanced_language_KO">ĐšĐžŅ€ĐĩĐšŅĐēи</string>
<string name="revanced_language_LT">Đ›Đ¸Ņ‚ĐžĐ˛ŅĐēи</string>
<string name="revanced_language_LV">Đ›Đ°Ņ‚Đ˛Đ¸ĐšŅĐēи</string>
<string name="revanced_language_MK">МаĐēĐĩĐ´ĐžĐŊҁĐēи</string>
<string name="revanced_language_MN">МоĐŊĐŗĐžĐģҁĐēи</string>
<string name="revanced_language_MR">ĐœĐ°Ņ€Đ°Ņ‚Ņ…Đ¸</string>
<string name="revanced_language_MS">МаĐģĐ°ĐšŅĐēи</string>
<string name="revanced_language_MY">Đ‘Đ¸Ņ€ĐŧаĐŊҁĐēи</string>
<string name="revanced_language_NL">ĐĨĐžĐģаĐŊĐ´ŅĐēи</string>
<string name="revanced_language_OR">ĐžĐ´Đ¸Ņ</string>
<string name="revanced_language_PA">ПĐĩĐŊĐ´Đļайи</string>
<string name="revanced_language_PL">ПоĐģҁĐēи</string>
<string name="revanced_language_PT">ĐŸĐžŅ€Ņ‚ŅƒĐŗĐ°ĐģҁĐēи</string>
<string name="revanced_language_RO">Đ ŅƒĐŧҊĐŊҁĐēи</string>
<string name="revanced_language_RU">Đ ŅƒŅĐēи</string>
<string name="revanced_language_SK">ĐĄĐģĐžĐ˛Đ°ŅˆĐēи</string>
<string name="revanced_language_SL">ĐĄĐģОвĐĩĐŊҁĐēи</string>
<string name="revanced_language_SR">ĐĄŅ€ŅŠĐąŅĐēи</string>
<string name="revanced_language_SV">ШвĐĩĐ´ŅĐēи</string>
<string name="revanced_language_SW">ĐĄŅƒĐ°Ņ…Đ¸Đģи</string>
<string name="revanced_language_TA">ĐĸаĐŧиĐģҁĐēи</string>
<string name="revanced_language_TE">ĐĸĐĩĐģŅƒĐŗŅƒ</string>
<string name="revanced_language_TH">ĐĸаКĐģаĐŊĐ´ŅĐēи</string>
<string name="revanced_language_TR">ĐĸŅƒŅ€ŅĐēи</string>
<string name="revanced_language_UK">ĐŖĐēŅ€Đ°Đ¸ĐŊҁĐēи</string>
<string name="revanced_language_UR">ĐŖŅ€Đ´Ņƒ</string>
<string name="revanced_language_VI">ВиĐĩŅ‚ĐŊаĐŧҁĐēи</string>
<string name="revanced_language_ZH">ĐšĐ¸Ņ‚Đ°ĐšŅĐēи</string>
<string name="revanced_pref_import_export_title">ИĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ / ЕĐēҁĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ</string>
<string name="revanced_pref_import_export_summary">ИĐŧĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ / ЕĐēҁĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ReVanced ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēĐ¸Ņ‚Đĩ</string>
<!-- Settings about dialog. -->
@@ -77,12 +134,15 @@ Second \"item\" text"</string>
<string name="revanced_settings_screen_01_ads_title">Đ ĐĩĐēĐģаĐŧи</string>
<string name="revanced_settings_screen_02_alt_thumbnails_title">АĐģŅ‚ĐĩŅ€ĐŊĐ°Ņ‚Đ¸Đ˛ĐŊи ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸</string>
<string name="revanced_settings_screen_03_feed_title">ĐŸĐžŅ‚ĐžĐē</string>
<string name="revanced_settings_screen_04_player_title">ПĐģĐĩĐšŅŠŅ€</string>
<string name="revanced_settings_screen_05_general_title">ĐžĐąŅ‰Đž ĐžŅ„ĐžŅ€ĐŧĐģĐĩĐŊиĐĩ</string>
<string name="revanced_settings_screen_04_general_title">ĐžĐąŅ‰</string>
<string name="revanced_settings_screen_05_player_title">ПĐģĐĩŅŠŅ€</string>
<string name="revanced_settings_screen_07_seekbar_title">ЛĐĩĐŊŅ‚Đ° Са ĐŋŅ€ĐžĐŗŅ€Đĩҁ ĐŊа видĐĩĐžŅ‚Đž</string>
<string name="revanced_settings_screen_08_swipe_controls_title">КоĐŊŅ‚Ņ€ĐžĐģи ҁ ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩ</string>
<string name="revanced_settings_screen_11_misc_title">РаСĐŊи</string>
<string name="revanced_settings_screen_12_video_title">ВидĐĩĐž</string>
<string name="revanced_restore_old_settings_menus_title">Đ’ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ŅŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ ĐŧĐĩĐŊŅŽŅ‚Đ° Са ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи</string>
<string name="revanced_restore_old_settings_menus_summary_on">ĐĄŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ ĐŧĐĩĐŊŅŽŅ‚Đ° ҁ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
<string name="revanced_restore_old_settings_menus_summary_off">ĐĄŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ ĐŧĐĩĐŊŅŽŅ‚Đ° ҁ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēи ĐŊĐĩ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">Đ’ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ ĐŊа Shorts в Ņ„ĐžĐŊОв Ņ€ĐĩĐļиĐŧ</string>
@@ -445,6 +505,9 @@ Second \"item\" text"</string>
<string name="revanced_swipe_overlay_background_alpha_summary">ВидиĐŧĐžŅŅ‚Ņ‚Đ° ĐŊа Ņ„ĐžĐŊа ĐŊа ĐŋĐģŅŠĐˇĐŗĐ°Ņ‰Đ¸Ņ‚Đĩ ĐēĐžĐŊŅ‚Ņ€ĐžĐģи.</string>
<string name="revanced_swipe_threshold_title">ĐŸŅ€Đ°Đŗ ĐŊа вĐĩĐģĐ¸Ņ‡Đ¸ĐŊĐ°Ņ‚Đ° ĐŊа ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩ</string>
<string name="revanced_swipe_threshold_summary">ĐŸŅ€Đ°Đŗ ĐŋŅ€Đĩди да ҁĐĩ ĐžŅŅŠŅ‰ĐĩŅŅ‚Đ˛Đ¸ ĐŋĐģŅŠĐˇĐŗĐ°ĐŊĐĩŅ‚Đž</string>
<string name="revanced_swipe_change_video_title">ВĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩ ĐŊа видĐĩĐžŅ‚Đž ҇ҀĐĩС ĐŋĐģŅŠĐˇĐ˛Đ°ĐŊĐĩ</string>
<string name="revanced_swipe_change_video_summary_on">ПĐģŅŠĐˇĐ˛Đ°ĐŊĐĩŅ‚Đž в Ņ€ĐĩĐļиĐŧ ĐŊа Ņ†ŅĐģ ĐĩĐēŅ€Đ°ĐŊ ҉Đĩ ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ¸ ĐēҊĐŧ ҁĐģĐĩĐ´Đ˛Đ°Ņ‰ĐžŅ‚Đž/ĐŋŅ€ĐĩĐ´Đ¸ŅˆĐŊĐž видĐĩĐž</string>
<string name="revanced_swipe_change_video_summary_off">ПĐģŅŠĐˇĐ˛Đ°ĐŊĐĩŅ‚Đž в Ņ€ĐĩĐļиĐŧ ĐŊа Ņ†ŅĐģ ĐĩĐēŅ€Đ°ĐŊ ĐŊŅĐŧа да ĐŋŅ€ĐĩвĐēĐģŅŽŅ‡Đ¸ ĐēҊĐŧ ҁĐģĐĩĐ´Đ˛Đ°Ņ‰ĐžŅ‚Đž/ĐŋŅ€ĐĩĐ´Đ¸ŅˆĐŊĐž видĐĩĐž</string>
</patch>
<patch id="layout.autocaptions.autoCaptionsPatch">
<string name="revanced_auto_captions_title">ĐĐ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊи ĐĄŅƒĐąŅ‚Đ¸Ņ‚Ņ€Đ¸</string>
@@ -628,15 +691,18 @@ Second \"item\" text"</string>
<string name="revanced_shorts_player_screen_summary">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ иĐģи ĐŋĐžĐēаСваĐŊĐĩ ĐŊа ĐēĐžĐŧĐŋĐžĐŊĐĩĐŊŅ‚Đ¸ в Shorts ĐŋĐģĐĩĐšŅŠŅ€Đ°</string>
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Shorts в ĐŊĐ°Ņ‡Đ°ĐģĐŊĐ°Ņ‚Đ° ĐģĐĩĐŊŅ‚Đ°</string>
<string name="revanced_hide_shorts_home_summary_on">Shorts в ĐŊĐ°Ņ‡Đ°ĐģĐŊĐ°Ņ‚Đ° ĐģĐĩĐŊŅ‚Đ° ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_shorts_home_summary_off">Shorts в ĐŊĐ°Ņ‡Đ°ĐģĐŊĐ°Ņ‚Đ° ĐģĐĩĐŊŅ‚Đ° ŅĐ° ĐŋĐžĐēаСаĐŊи</string>
<string name="revanced_hide_shorts_home_summary_on">ĐĄĐēŅ€Đ¸Ņ‚ в ĐŊĐ°Ņ‡Đ°ĐģĐŊĐ¸Ņ ĐēаĐŊаĐģ и ŅĐ˛ŅŠŅ€ĐˇĐ°ĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ</string>
<string name="revanced_hide_shorts_home_summary_off">ПоĐēаСаĐŊ в ĐŊĐ°Ņ‡Đ°ĐģĐŊĐ¸Ņ ĐēаĐŊаĐģ и ŅĐ˛ŅŠŅ€ĐˇĐ°ĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ</string>
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_hide_shorts_subscriptions_title">Shorts в Ņ€Đ°ĐˇĐ´ĐĩĐģ „АбоĐŊаĐŧĐĩĐŊŅ‚Đ¸â€œ</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Shorts в Ņ€Đ°ĐˇĐ´ĐĩĐģ „АбоĐŊаĐŧĐĩĐŊŅ‚Đ¸â€œ ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">Shorts в Ņ€Đ°ĐˇĐ´ĐĩĐģ „АбоĐŊаĐŧĐĩĐŊŅ‚Đ¸â€œ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">ĐĄĐēŅ€Đ¸Ņ‚Đž в айОĐŊаĐŧĐĩĐŊŅ‚ĐŊĐ°Ņ‚Đ° ĐĩĐŧĐ¸ŅĐ¸Ņ</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">ПоĐēаСаĐŊĐž в айОĐŊаĐŧĐĩĐŊŅ‚ĐŊĐ°Ņ‚Đ° ĐĩĐŧĐ¸ŅĐ¸Ņ</string>
<string name="revanced_hide_shorts_search_title">Shorts в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_shorts_search_summary_on">Shorts в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_shorts_search_summary_off">Shorts в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
<string name="revanced_hide_shorts_search_summary_on">ĐĄĐēŅ€Đ¸Ņ‚Đ¸ в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_shorts_search_summary_off">ПоĐēаСаĐŊĐž в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_shorts_history_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ŅˆĐžŅ€Ņ‚Đ¸Ņ‚Đĩ в Đ¸ŅŅ‚ĐžŅ€Đ¸ŅŅ‚Đ° ĐŊа ĐŗĐģĐĩдаĐŊĐĩ</string>
<string name="revanced_hide_shorts_history_summary_on">ĐĄĐēŅ€Đ¸Ņ‚Đž в Đ¸ŅŅ‚ĐžŅ€Đ¸ŅŅ‚Đ° ĐŊа ĐŗĐģĐĩдаĐŊĐĩ</string>
<string name="revanced_hide_shorts_history_summary_off">ПоĐēаСва ҁĐĩ в Đ¸ŅŅ‚ĐžŅ€Đ¸ŅŅ‚Đ° ĐŊа ĐŗĐģĐĩдаĐŊĐĩ</string>
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_join_button_title">Đ‘ŅƒŅ‚ĐžĐŊ Са ĐŋŅ€Đ¸ŅŅŠĐĩдиĐŊŅĐ˛Đ°ĐŊĐĩ</string>
<string name="revanced_hide_shorts_join_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊа Са ĐŋŅ€Đ¸ŅŅŠĐĩдиĐŊŅĐ˛Đ°ĐŊĐĩ Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
@@ -738,6 +804,13 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_popup_panels_summary_on">Đ˜ĐˇŅĐēĐ°Ņ‡Đ°Ņ‰Đ¸Ņ‚Đĩ ĐŋаĐŊĐĩĐģи ĐŊа ĐŋĐģĐĩĐšŅŠŅ€Đ° ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_player_popup_panels_summary_off">Đ˜ĐˇŅĐēĐ°Ņ‡Đ°Ņ‰Đ¸Ņ‚Đĩ ĐŋаĐŊĐĩĐģи ĐŊа ĐŋĐģĐĩĐšŅŠŅ€Đ° ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
<string name="revanced_exit_fullscreen_title">Đ˜ĐˇŅ…ĐžĐ´ ĐžŅ‚ Ņ€ĐĩĐļиĐŧ ĐŊа Ņ†ŅĐģ ĐĩĐēŅ€Đ°ĐŊ в ĐēŅ€Đ°Ņ ĐŊа видĐĩĐžŅ‚Đž</string>
<string name="revanced_exit_fullscreen_entry_1">ДĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž</string>
<string name="revanced_exit_fullscreen_entry_2">ĐŸĐžŅ€Ņ‚Ņ€ĐĩŅ‚</string>
<string name="revanced_exit_fullscreen_entry_3">ПĐĩКСаĐļ</string>
<string name="revanced_exit_fullscreen_entry_4">ĐŸĐžŅ€Ņ‚Ņ€ĐĩŅ‚ и ĐŋĐĩКСаĐļ</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">ĐžŅ‚Đ˛Đ°Ņ€ŅĐŊĐĩ ĐŊа видĐĩĐžĐēĐģиĐŋОвĐĩ в ĐŋĐžŅ€Ņ‚Ņ€ĐĩŅ‚ĐĩĐŊ Ņ€ĐĩĐļиĐŧ ĐŊа Ņ†ŅĐģ ĐĩĐēŅ€Đ°ĐŊ</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">ВидĐĩĐžĐēĐģиĐŋОвĐĩŅ‚Đĩ ҁĐĩ ĐžŅ‚Đ˛Đ°Ņ€ŅŅ‚ ĐŊа Ņ†ŅĐģ ĐĩĐēŅ€Đ°ĐŊ</string>
@@ -1006,6 +1079,23 @@ Second \"item\" text"</string>
<string name="revanced_sb_reset">Đ’ŅŠĐˇŅŅ‚Đ°ĐŊОви</string>
<string name="revanced_sb_about">За ĐŋŅ€ĐžĐŗŅ€Đ°ĐŧĐ°Ņ‚Đ°</string>
<string name="revanced_sb_about_api_sum">ДаĐŊĐŊĐ¸Ņ‚Đĩ ŅĐ° ĐŋŅ€ĐĩĐ´ĐžŅŅ‚Đ°Đ˛ĐĩĐŊи ĐžŅ‚ SponsorBlock API. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ Ņ‚ŅƒĐē Са ĐŋОвĐĩ҇Đĩ иĐŊŅ„ĐžŅ€ĐŧĐ°Ņ†Đ¸Ņ и Đ¸ĐˇŅ‚ĐĩĐŗĐģĐ¸ŅĐŊĐ¸Ņ</string>
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
<string name="revanced_change_form_factor_title">Đ¤ĐžŅ€ĐŧĐ°Ņ‚ ĐŊа ĐĩĐēŅ€Đ°ĐŊа /ĐĸайĐģĐĩŅ‚, ĐĸĐĩĐģŅ„ĐžĐŊ, .../</string>
<string name="revanced_change_form_factor_entry_1">По ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ</string>
<string name="revanced_change_form_factor_entry_2">ĐĸĐĩĐģĐĩŅ„ĐžĐŊ</string>
<string name="revanced_change_form_factor_entry_3">ĐĸайĐģĐĩŅ‚</string>
<string name="revanced_change_form_factor_entry_4">ĐĐ˛Ņ‚ĐžĐŧОйиĐģ</string>
<string name="revanced_change_form_factor_user_dialog_message">"ĐŸŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ вĐēĐģŅŽŅ‡Đ˛Đ°Ņ‚:
ĐžŅ„ĐžŅ€ĐŧĐģĐĩĐŊиĐĩ Са Ņ‚Đ°ĐąĐģĐĩŅ‚
â€ĸ ĐŸŅƒĐąĐģиĐēĐ°Ņ†Đ¸Đ¸Ņ‚Đĩ ĐŊа ĐžĐąŅ‰ĐŊĐžŅŅ‚Ņ‚Đ° ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸
ĐžŅ„ĐžŅ€ĐŧĐģĐĩĐŊиĐĩ Са Đ°Đ˛Ņ‚ĐžĐŧОйиĐģ
â€ĸ МĐĩĐŊŅŽŅ‚Đž â€žĐ˜ŅŅ‚ĐžŅ€Đ¸Ņ ĐŊа ĐŗĐģĐĩдаĐŊĐĩ“ Đĩ ҁĐēŅ€Đ¸Ņ‚Đž
â€ĸ РаСдĐĩĐģŅŠŅ‚ â€žĐ Đ°ĐˇĐŗĐģĐĩдай“ Đĩ Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩĐŊ
â€ĸ Shorts ҁĐĩ ĐžŅ‚Đ˛Đ°Ņ€ŅŅ‚ в ОйиĐēĐŊОвĐĩĐŊĐ¸Ņ ĐŋĐģĐĩĐšŅŠŅ€
â€ĸ ЛĐĩĐŊŅ‚Đ°Ņ‚Đ° Đĩ ĐžŅ€ĐŗĐ°ĐŊĐ¸ĐˇĐ¸Ņ€Đ°ĐŊа ĐŋĐž Ņ‚ĐĩĐŧи и ĐēаĐŊаĐģ"</string>
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<string name="revanced_spoof_app_version_title">ПодĐģŅŠĐŗĐ˛Đ°ĐŊĐĩ Са вĐĩŅ€ŅĐ¸ŅŅ‚Đ° ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž</string>
@@ -1020,6 +1110,7 @@ Second \"item\" text"</string>
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">ПодĐģŅŠĐŗĐ˛Đ°ĐŊĐĩ Са вĐĩŅ€ŅĐ¸ŅŅ‚Đ° ĐŊа</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 - Đ’ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩŅ‚Đĩ ŅŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ иĐēĐžĐŊи ĐŊа Shorts в ĐŋĐģĐĩĐšŅŠŅ€Đ°</string>
<string name="revanced_spoof_app_version_target_entry_2">19.26.42 - Đ’ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ŅŅ‚Đ°Ņ€Đ¸ иĐēĐžĐŊи ĐŊа ĐŊĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸ŅŅ‚Đ° и ĐģĐĩĐŊŅ‚Đ°Ņ‚Đ° ҁ иĐŊŅŅ‚Ņ€ŅƒĐŧĐĩĐŊŅ‚Đ¸</string>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_legacy_entry_1">18.33.40 - Đ’ŅŠĐˇŅŅ‚Đ°ĐŊОвĐĩŅ‚Đĩ RYD в Ņ€ĐĩĐļиĐŧ „иĐŊĐēĐžĐŗĐŊĐ¸Ņ‚Đžâ€œ ĐŊа Shorts</string>
<string name="revanced_spoof_app_version_target_legacy_entry_2">18.20.39 - Đ’ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа видĐĩĐž ҁĐēĐžŅ€ĐžŅŅ‚ &amp; в ĐŧĐĩĐŊŅŽŅ‚Đž Са ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž</string>
@@ -1063,12 +1154,6 @@ Second \"item\" text"</string>
<string name="revanced_shorts_autoplay_background_summary_on">Shorts ҉Đĩ ҁĐĩ Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļĐ´Đ°Ņ‚ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž ĐĩдиĐŊ ҁĐģĐĩĐ´ Đ´Ņ€ŅƒĐŗ Đ˛ŅŠĐ˛ Ņ„ĐžĐŊОв Ņ€ĐĩĐļиĐŧ</string>
<string name="revanced_shorts_autoplay_background_summary_off">Shorts ҉Đĩ ҁĐĩ ĐŋĐžĐ˛Ņ‚ĐžŅ€Đ¸ Đ˛ŅŠĐ˛ Ņ„ĐžĐŊОв Ņ€ĐĩĐļиĐŧ</string>
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
<string name="revanced_tablet_layout_title">ВĐēĐģŅŽŅ‡Đ¸ Ņ€ĐĩĐļиĐŧ Са Ņ‚Đ°ĐąĐģĐĩŅ‚</string>
<string name="revanced_tablet_layout_summary_on">Đ ĐĩĐļиĐŧ Са Ņ‚Đ°ĐąĐģĐĩŅ‚ Đĩ вĐēĐģ.</string>
<string name="revanced_tablet_layout_summary_off">Đ ĐĩĐļиĐŧ Са Ņ‚Đ°ĐąĐģĐĩŅ‚ Đĩ иСĐēĐģ.</string>
<string name="revanced_tablet_layout_user_dialog_message">ĐŸŅƒĐąĐģиĐēĐ°Ņ†Đ¸Đ¸Ņ‚Đĩ в ĐžĐąŅ‰ĐŊĐžŅŅ‚Ņ‚Đ° ĐŊĐĩ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚ ĐŊа ĐžŅ„ĐžŅ€ĐŧĐģĐĩĐŊĐ¸Ņ Са Ņ‚Đ°ĐąĐģĐĩŅ‚</string>
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">МиĐŊиĐŧĐ¸ĐˇĐ¸Ņ€Đ°ĐŊ ĐĩĐēŅ€Đ°ĐŊ Са Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ</string>
<string name="revanced_miniplayer_screen_summary">ĐŸŅ€ĐžĐŧĐĩĐŊĐĩŅ‚Đĩ ŅŅ‚Đ¸Đģа ĐŊа ĐŧиĐŊиĐŧĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐ¸Ņ ĐĩĐēŅ€Đ°ĐŊ Са Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ</string>
@@ -1232,6 +1317,7 @@ Second \"item\" text"</string>
<string name="revanced_force_original_audio_title">ĐŸŅ€Đ¸ĐŊŅƒĐ´Đ¸Ņ‚ĐĩĐģĐŊĐž ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐž Đ°ŅƒĐ´Đ¸Đž</string>
<string name="revanced_force_original_audio_summary_on">ИСĐŋĐžĐģСваĐŊĐĩ ĐŊа ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐž Đ°ŅƒĐ´Đ¸Đž</string>
<string name="revanced_force_original_audio_summary_off">ИСĐŋĐžĐģСваĐŊĐĩ ĐŊа Đ°ŅƒĐ´Đ¸Đž ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ</string>
<string name="revanced_force_original_audio_not_available">За да иСĐŋĐžĐģĐˇĐ˛Đ°Ņ‚Đĩ Ņ‚Đ°ĐˇĐ¸ Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ, ĐŋŅ€ĐžĐŧĐĩĐŊĐĩŅ‚Đĩ иĐŧĐ¸Ņ‚Đ°Ņ†Đ¸ŅŅ‚Đ° ĐŊа ĐŋĐžŅ‚ĐžŅ‡ĐŊĐž ĐŋŅ€ĐĩдаваĐŊĐĩ ĐŊа Ņ‚Đ¸Đŋ ĐēĐģиĐĩĐŊŅ‚ ĐŊа iOS</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
@@ -1259,6 +1345,8 @@ Second \"item\" text"</string>
<string name="revanced_custom_playback_speeds_invalid">ПĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐ¸Ņ‚Đĩ ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ Ņ‚Ņ€ŅĐąĐ˛Đ° да ŅĐ° ĐŋĐž-ĐŧаĐģĐēи ĐžŅ‚ %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">НĐĩваĐģидĐŊи ĐŋĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊи ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ ĐŊа Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ</string>
<string name="revanced_custom_playback_speeds_auto">ĐĐ˛Ņ‚Đž</string>
<string name="revanced_speed_tap_and_hold_title">ПĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊа ҁĐēĐžŅ€ĐžŅŅ‚ ĐŋŅ€Đ¸ Đ´ĐžĐēĐžŅĐ˛Đ°ĐŊĐĩ и ĐˇĐ°Đ´ŅŠŅ€ĐļаĐŊĐĩ</string>
<string name="revanced_speed_tap_and_hold_summary">ĐĄĐēĐžŅ€ĐžŅŅ‚ ĐŊа Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ ĐŧĐĩĐļĐ´Ņƒ 0-8</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
<string name="revanced_remember_playback_speed_last_selected_title">ЗаĐŋĐžĐŧĐŊи ĐŋŅ€ĐžĐŧĐĩĐŊĐ¸Ņ‚Đĩ в ҁĐēĐžŅ€ĐžŅŅ‚Ņ‚Đ° ĐŊа Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ</string>
@@ -1287,65 +1375,27 @@ Second \"item\" text"</string>
Đ’ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩŅ‚Đž ĐŊа видĐĩĐžĐēĐģиĐŋОвĐĩ ĐŧĐžĐļĐĩ да ĐŊĐĩ Ņ€Đ°ĐąĐžŅ‚Đ¸"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">ДĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩŅ‚Đž ĐŊа Ņ‚Đ°ĐˇĐ¸ ĐŊĐ°ŅŅ‚Ņ€ĐžĐšĐēа ҉Đĩ дОвĐĩĐ´Đĩ Đ´Đž ĐŋŅ€ĐžĐąĐģĐĩĐŧи ҁ Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩŅ‚Đž ĐŊа видĐĩĐž.</string>
<string name="revanced_spoof_video_streams_client_type_title">КĐģиĐĩĐŊŅ‚ ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ</string>
<string name="revanced_spoof_video_streams_about_title">ФаĐģŅˆĐ¸Đ˛Đ¸ ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ‡ĐŊи ĐĩŅ„ĐĩĐēŅ‚Đ¸</string>
<string name="revanced_spoof_video_streams_about_summary">"â€ĸ ДĐĩ҂ҁĐēĐ¸Ņ‚Đĩ видĐĩĐžĐēĐģиĐŋОвĐĩ ĐŧĐžĐļĐĩ да ĐŊĐĩ ҁĐĩ Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļĐ´Đ°Ņ‚
â€ĸ ЛиĐŋŅĐ˛Đ° ĐŧĐĩĐŊŅŽŅ‚Đž Са Đ°ŅƒĐ´Đ¸Đž СаĐŋĐ¸Ņ
â€ĸ НĐĩ Đĩ ĐŊаĐģĐ¸Ņ‡ĐĩĐŊ ŅŅ‚Đ°ĐąĐ¸ĐģĐĩĐŊ ĐˇĐ˛ŅƒĐē
â€ĸ ĐŸŅ€Đ¸ĐŊŅƒĐ´Đ¸Ņ‚ĐĩĐģĐŊĐž ĐžŅ€Đ¸ĐŗĐ¸ĐŊаĐģĐŊĐž Đ°ŅƒĐ´Đ¸Đž ĐŊĐĩ Đĩ ĐŊаĐģĐ¸Ņ‡ĐŊĐž"</string>
<string name="revanced_spoof_video_streams_language_title">ЕзиĐē ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ ĐŊа Đ°ŅƒĐ´Đ¸Đž ĐŋĐžŅ‚ĐžĐēа</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">ЕзиĐē ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž</string>
<string name="revanced_spoof_video_streams_language_AR">ĐŅ€Đ°ĐąŅĐēи</string>
<string name="revanced_spoof_video_streams_language_AZ">АСĐĩŅ€ĐąĐ°ĐšĐ´ĐļаĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_BG">Đ‘ŅŠĐģĐŗĐ°Ņ€ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_BN">БĐĩĐŊĐŗĐ°ĐģҁĐēи</string>
<string name="revanced_spoof_video_streams_language_CA">ĐšĐ°Ņ‚Đ°ĐģĐžĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_CS">ЧĐĩ҈Đēи</string>
<string name="revanced_spoof_video_streams_language_DA">Đ”Đ°Ņ‚ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_DE">НĐĩĐŧҁĐēи</string>
<string name="revanced_spoof_video_streams_language_EL">Đ“Ņ€ŅŠŅ†Đēи</string>
<string name="revanced_spoof_video_streams_language_EN">АĐŊĐŗĐģĐ¸ĐšŅĐēи</string>
<string name="revanced_spoof_video_streams_language_ES">Đ˜ŅĐŋаĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_ET">Đ•ŅŅ‚ĐžĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_FA">ПĐĩŅ€ŅĐ¸ĐšŅĐēи</string>
<string name="revanced_spoof_video_streams_language_FI">ФиĐŊĐģаĐŊĐ´ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_FR">Đ¤Ņ€ĐĩĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_GU">Đ“ŅƒĐ´ĐļĐ°Ņ€Đ°Ņ‚Đ¸</string>
<string name="revanced_spoof_video_streams_language_HI">ĐĨиĐŊди</string>
<string name="revanced_spoof_video_streams_language_HR">ĐĨŅŠŅ€Đ˛Đ°Ņ‚ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_HU">ĐŖĐŊĐŗĐ°Ņ€ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_ID">ИĐŊĐ´ĐžĐŊĐĩĐˇĐ¸ĐšŅĐēи</string>
<string name="revanced_spoof_video_streams_language_IT">Đ˜Ņ‚Đ°ĐģиаĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_JA">Đ¯ĐŋĐžĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_KK">ĐšĐ°ĐˇĐ°Ņ…ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_KO">ĐšĐžŅ€ĐĩĐšŅĐēи</string>
<string name="revanced_spoof_video_streams_language_LT">Đ›Đ¸Ņ‚ĐžĐ˛ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_LV">Đ›Đ°Ņ‚Đ˛Đ¸ĐšŅĐēи</string>
<string name="revanced_spoof_video_streams_language_MK">МаĐēĐĩĐ´ĐžĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_MN">МоĐŊĐŗĐžĐģҁĐēи</string>
<string name="revanced_spoof_video_streams_language_MR">ĐœĐ°Ņ€Đ°Ņ‚Ņ…Đ¸</string>
<string name="revanced_spoof_video_streams_language_MS">МаĐģĐ°ĐšŅĐēи</string>
<string name="revanced_spoof_video_streams_language_MY">Đ‘Đ¸Ņ€ĐŧаĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_NL">ĐĨĐžĐģаĐŊĐ´ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_OR">ĐžŅ€Đ¸Ņ</string>
<string name="revanced_spoof_video_streams_language_PA">ПĐĩĐŊĐ´ĐļĐ°ĐąŅĐēи</string>
<string name="revanced_spoof_video_streams_language_PL">ПоĐģҁĐēи</string>
<string name="revanced_spoof_video_streams_language_PT_BR">ĐŸĐžŅ€Ņ‚ŅƒĐŗĐ°ĐģҁĐēи (Đ‘Ņ€Đ°ĐˇĐ¸ĐģĐ¸Ņ)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">ĐŸĐžŅ€Ņ‚ŅƒĐŗĐ°ĐģҁĐēи (ĐŸĐžŅ€Ņ‚ŅƒĐŗĐ°ĐģĐ¸Ņ)</string>
<string name="revanced_spoof_video_streams_language_RO">Đ ŅƒĐŧҊĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_RU">Đ ŅƒŅĐēи</string>
<string name="revanced_spoof_video_streams_language_SK">ĐĄĐģĐžĐ˛Đ°ŅˆĐēи</string>
<string name="revanced_spoof_video_streams_language_SL">ĐĄĐģОвĐĩĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_SR">ĐĄŅ€ŅŠĐąŅĐēи</string>
<string name="revanced_spoof_video_streams_language_SV">ШвĐĩĐ´ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_SW">ĐĄŅƒĐ°Ņ…Đ¸Đģи</string>
<string name="revanced_spoof_video_streams_language_TA">ĐĸаĐŧиĐģҁĐēи</string>
<string name="revanced_spoof_video_streams_language_TE">ĐĸĐĩĐģŅƒĐŗŅƒ</string>
<string name="revanced_spoof_video_streams_language_TH">ĐĸаКĐģаĐŊĐ´ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_TR">ĐĸŅƒŅ€ŅĐēи</string>
<string name="revanced_spoof_video_streams_language_UK">ĐŖĐēŅ€Đ°Đ¸ĐŊҁĐēи</string>
<string name="revanced_spoof_video_streams_language_UR">ĐŖŅ€Đ´Ņƒ</string>
<string name="revanced_spoof_video_streams_language_VI">ВиĐĩŅ‚ĐŊаĐŧҁĐēи</string>
<string name="revanced_spoof_video_streams_language_ZH">ĐšĐ¸Ņ‚Đ°ĐšŅĐēи</string>
<!-- 'no auth' means no authentication -->
<string name="revanced_spoof_video_streams_client_type_android_vr_no_auth">Android VR (bez avtorizaciq)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">ĐŸŅ€Đ¸ĐŊŅƒĐ´Đ¸Ņ‚ĐĩĐģĐŊĐž иСĐŋĐžĐģСваĐŊĐĩ ĐŊа AVC (H.264) ĐŊа iOS</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">ВидĐĩĐž ĐēОдĐĩĐēŅŠŅ‚ Đĩ ĐŋŅ€Đ¸ĐŊŅƒĐ´Đ¸Ņ‚ĐĩĐģĐŊĐž СададĐĩĐŊ ĐŊа AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">ВидĐĩĐž ĐēОдĐĩĐēŅŠŅ‚ ҁĐĩ ĐžĐŋŅ€ĐĩĐ´ĐĩĐģŅ Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"ВĐēĐģŅŽŅ‡Đ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа Ņ‚ĐžĐ˛Đ° ĐŧĐžĐļĐĩ да ĐŋĐžĐ´ĐžĐąŅ€Đ¸ ĐļĐ¸Đ˛ĐžŅ‚Đ° ĐŊа ĐąĐ°Ņ‚ĐĩŅ€Đ¸ŅŅ‚Đ° и да ĐŋĐžĐŋŅ€Đ°Đ˛Đ¸ СаĐĩĐēваĐŊĐĩŅ‚Đž ĐŋŅ€Đ¸ Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ.
AVC иĐŧа ĐŧаĐēŅĐ¸ĐŧаĐģĐŊа Ņ€ĐĩСОĐģŅŽŅ†Đ¸Ņ ĐžŅ‚ 1080p, Opus Đ°ŅƒĐ´Đ¸Đž ĐēОдĐĩĐē ĐŊĐĩ Đĩ ĐŊаĐģĐ¸Ņ‡ĐĩĐŊ и Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩŅ‚Đž ĐŊа видĐĩĐž ҉Đĩ иСĐŋĐžĐģСва ĐŋОвĐĩ҇Đĩ иĐŊŅ‚ĐĩŅ€ĐŊĐĩŅ‚ даĐŊĐŊи ĐžŅ‚ VP9 иĐģи AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">Đ•Ņ„ĐĩĐēŅ‚Đ¸ ĐŊа иСĐŧаĐŧĐ°Ņ‚Đ° в iOS</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"â€ĸ ФиĐģĐŧи иĐģи ĐŋĐģĐ°Ņ‚ĐĩĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ ĐŧĐžĐļĐĩ да ĐŊĐĩ ҁĐĩ Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļĐ´Đ°Ņ‚
â€ĸ ĐĄŅ‚Đ°ĐąĐ¸ĐģĐĩĐŊ ĐˇĐ˛ŅƒĐē ĐŊĐĩ Đĩ ĐŊаĐģĐ¸Ņ‡ĐĩĐŊ
â€ĸ ВидĐĩĐžĐēĐģиĐŋОвĐĩŅ‚Đĩ ĐˇĐ°Đ˛ŅŠŅ€ŅˆĐ˛Đ°Ņ‚ 1 ҁĐĩĐē҃ĐŊда ĐŋĐž-Ņ€Đ°ĐŊĐž"</string>
<string name="revanced_spoof_video_streams_about_android_title">Strani4ni efekti na fal6ivoto predstavqne na Android</string>
<string name="revanced_spoof_video_streams_about_android_summary">"â€ĸ Lipsva menju za audio pisti
â€ĸ Ne e nali4na stabilna glasnost
â€ĸ Ne e nali4na forsirana originalna audio pista"</string>
<string name="revanced_spoof_video_streams_about_no_av1">â€ĸ БĐĩС AV1 видĐĩĐž ĐēОдĐĩĐē</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">Poka6i v Statistiki za nerds</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">TipŅŠŅ‚ na klienta se poka6va v Statistiki za nerds</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">KlientŅŠŅ‚ e skriŅ‚ v Statistiki za nerds</string>
<string name="revanced_spoof_video_streams_language_title">Ezik po ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ za audio potok v VR</string>
</patch>
</app>
<app id="twitch">

View File

@@ -33,6 +33,7 @@ Second \"item\" text"</string>
<string name="revanced_check_environment_not_near_patch_time_invalid">APK āϤ⧈āϰāĻŋāϰ āϤāĻžāϰāĻŋāĻ– āĻ¤ā§āϰ⧁āϟāĻŋāĻĒā§‚āĻ°ā§āĻŖ</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_submenu_title">āϏ⧇āϟāĻŋāĻ‚āϏ</string>
<string name="revanced_settings_title">ReVanced</string>
<string name="revanced_settings_confirm_user_dialog_title">āφāĻĒāύāĻŋ āĻ•āĻŋ āĻāĻ—āĻŋā§Ÿā§‡ āϝ⧇āϤ⧇ āχāĻšā§āϛ⧁āĻ•?</string>
<string name="revanced_settings_reset">āφāĻŦāĻžāϰ āϏ⧇āϟ āĻ•āϰ⧁āύ</string>
@@ -43,6 +44,62 @@ Second \"item\" text"</string>
<string name="revanced_settings_import_reset">ReVanced āϏ⧇āϟāĻŋāĻ‚ āĻĄāĻŋāĻĢāĻ˛ā§āϟ āϏ⧇āϟ āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_settings_import_success">%d āϏ⧇āϟāĻŋāĻ‚ āφāĻŽāĻĻāĻžāύāĻŋ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_settings_import_failure_parse">āφāĻŽāĻĻāĻžāύāĻŋ āĻ•āϰāĻž āϝāĻžā§ŸāύāĻŋ: %s</string>
<string name="revanced_language_title">ReVanced āĻ­āĻžāώāĻž</string>
<string name="revanced_language_user_dialog_message">"āĻ•āĻŋāϛ⧁ āĻ­āĻžāώāĻžāϰ āϜāĻ¨ā§āϝ āĻ…āύ⧁āĻŦāĻžāĻĻ āĻ…āύ⧁āĻĒāĻ¸ā§āĻĨāĻŋāϤ āĻŦāĻž āĻ…āϏāĻŽā§āĻĒā§‚āĻ°ā§āĻŖ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤
āύāϤ⧁āύ āĻ­āĻžāώāĻž āĻ…āύ⧁āĻŦāĻžāĻĻ āĻ•āϰāϤ⧇ translate.revanced.app āĻĻ⧇āϖ⧁āύ"</string>
<string name="revanced_language_DEFAULT">āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύ⧇āϰ āĻ­āĻžāώāĻž</string>
<string name="revanced_language_AR">āφāϰāĻŦāĻŋ</string>
<string name="revanced_language_AZ">āφāϜāĻžāϰāĻŦāĻžāχāϜāĻžāύāĻŋ</string>
<string name="revanced_language_BG">āĻŦ⧁āϞāϗ⧇āϰāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_language_BN">āĻŦāĻžāĻ‚āϞāĻž</string>
<string name="revanced_language_CA">āĻ•āĻžāϤāĻžāϞāĻžāύ</string>
<string name="revanced_language_CS">āĻšā§‡āĻ•</string>
<string name="revanced_language_DA">āĻĄā§āϝāĻžāύāĻŋāĻļ</string>
<string name="revanced_language_DE">āϜāĻžāĻ°ā§āĻŽāĻžāύ</string>
<string name="revanced_language_EL">āĻ—ā§āϰāĻŋāĻ•</string>
<string name="revanced_language_EN">āχāĻ‚āϰ⧇āϜāĻŋ</string>
<string name="revanced_language_ES">āĻ¸ā§āĻĒā§āϝāĻžāύāĻŋāĻļ</string>
<string name="revanced_language_ET">āĻāĻ¸ā§āϤ⧋āύāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_language_FA">āĻĢāĻžāĻ°ā§āϏāĻŋ</string>
<string name="revanced_language_FI">āĻĢāĻŋāύāĻŋāĻļ</string>
<string name="revanced_language_FR">āĻĢāϰāĻžāϏāĻŋ</string>
<string name="revanced_language_GU">āϗ⧁āϜāϰāĻžāϟāĻŋ</string>
<string name="revanced_language_HI">āĻšāĻŋāĻ¨ā§āĻĻāĻŋ</string>
<string name="revanced_language_HR">āĻ•ā§āϰ⧋āϝāĻŧ⧇āĻļā§€āϝāĻŧ</string>
<string name="revanced_language_HU">āĻšāĻžāĻ™ā§āϗ⧇āϰāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_language_ID">āχāĻ¨ā§āĻĻā§‹āύ⧇āĻļāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_language_IT">āχāϤāĻžāϞ⧀āϝāĻŧ</string>
<string name="revanced_language_JA">āϜāĻžāĻĒāĻžāύāĻŋ</string>
<string name="revanced_language_KK">āĻ•āĻžāϜāĻžāĻ–</string>
<string name="revanced_language_KO">āϕ⧋āϰāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_language_LT">āϞāĻŋāĻĨ⧁⧟āĻžāύāĻŋ⧟āĻžāύ</string>
<string name="revanced_language_LV">āϞāĻžāϤāĻ­āĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_language_MK">āĻŽā§āϝāĻžāϏ⧇āĻĄā§‹āύāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_language_MN">āĻŽāĻ™ā§āĻ—ā§‹āϞ⧀āϝāĻŧ</string>
<string name="revanced_language_MR">āĻŽāĻžāϰāĻžāĻ āĻŋ</string>
<string name="revanced_language_MS">āĻŽāĻžāϞāϝāĻŧ</string>
<string name="revanced_language_MY">āĻŦāĻ°ā§āĻŽāĻŋ</string>
<string name="revanced_language_NL">āĻĄāĻžāϚ</string>
<string name="revanced_language_OR">āĻ“āĻĄāĻŧāĻŋāϝāĻŧāĻž</string>
<string name="revanced_language_PA">āĻĒāĻžāĻžā§āϜāĻžāĻŦāĻŋ</string>
<string name="revanced_language_PL">āĻĒā§‹āϞāĻŋāĻļ</string>
<string name="revanced_language_PT">āĻĒāĻ°ā§āϤ⧁āĻ—āĻŋāϜ</string>
<string name="revanced_language_RO">āϰ⧋āĻŽāĻžāύ⧀āϝāĻŧ</string>
<string name="revanced_language_RU">āϰ⧁āĻļ</string>
<string name="revanced_language_SK">āĻ¸ā§āϞ⧋āĻ­āĻžāĻ•</string>
<string name="revanced_language_SL">āĻ¸ā§āϞ⧋āϭ⧇āύ</string>
<string name="revanced_language_SR">āϏāĻžāĻ°ā§āĻŦāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_language_SV">āϏ⧁āχāĻĄāĻŋāĻļ</string>
<string name="revanced_language_SW">āϏ⧋āϝāĻŧāĻžāĻšāĻŋāϞāĻŋ</string>
<string name="revanced_language_TA">āϤāĻžāĻŽāĻŋāϞ</string>
<string name="revanced_language_TE">āϤ⧇āϞ⧁āϗ⧁</string>
<string name="revanced_language_TH">āĻĨāĻžāχ</string>
<string name="revanced_language_TR">āϤ⧁āĻ°ā§āĻ•āĻŋ</string>
<string name="revanced_language_UK">āχāωāĻ•ā§āϰ⧇āύ⧀āϝāĻŧ</string>
<string name="revanced_language_UR">āωāĻ°ā§āĻĻ⧁</string>
<string name="revanced_language_VI">āĻ­āĻŋāϝāĻŧ⧇āϤāύāĻžāĻŽā§€</string>
<string name="revanced_language_ZH">āϚāĻžāχāύāĻŋāϜ</string>
<string name="revanced_pref_import_export_title">āφāĻŽāĻĻāĻžāύāĻŋ āĻāĻŦāĻ‚ āϰāĻĒā§āϤāĻžāύāĻŋ</string>
<string name="revanced_pref_import_export_summary">ReVanced āϏ⧇āϟāĻŋāĻ‚ āφāĻŽāĻĻāĻžāύāĻŋ āĻŦāĻž āϰāĻĒā§āϤāĻžāύāĻŋ āĻ•āϰ⧁āύ</string>
<!-- Settings about dialog. -->
@@ -77,13 +134,16 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_settings_screen_01_ads_title">āĻŦāĻŋāĻœā§āĻžāĻžāĻĒāύ</string>
<string name="revanced_settings_screen_02_alt_thumbnails_title">āĻŦāĻŋāĻ•āĻ˛ā§āĻĒ āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ</string>
<string name="revanced_settings_screen_03_feed_title">āĻĢāĻŋāĻĄ</string>
<string name="revanced_settings_screen_04_player_title">āĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ</string>
<string name="revanced_settings_screen_05_general_title">āϏāĻžāϧāĻžāϰāĻŖ āϞ⧇-āφāωāϟ</string>
<string name="revanced_settings_screen_04_general_title">āϏāĻžāϧāĻžāϰāĻŖ</string>
<string name="revanced_settings_screen_05_player_title">āĻĒā§āϞ⧇āϝāĻŧāĻžāϰ</string>
<string name="revanced_settings_screen_06_shorts_title">Shorts</string>
<string name="revanced_settings_screen_07_seekbar_title">āϏāĻŋāĻ•āĻŦāĻžāϰ</string>
<string name="revanced_settings_screen_08_swipe_controls_title">āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻ•āĻ¨ā§āĻŸā§āϰ⧋āϞ</string>
<string name="revanced_settings_screen_11_misc_title">āĻŦāĻŋāĻŦāĻŋāϧ</string>
<string name="revanced_settings_screen_12_video_title">āĻ­āĻŋāĻĄāĻŋāĻ“</string>
<string name="revanced_restore_old_settings_menus_title">āĻĒ⧁āϰāĻžāύ⧋ āϏ⧇āϟāĻŋāĻ‚āϏ āĻŽā§‡āύ⧁ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰ⧁āύ</string>
<string name="revanced_restore_old_settings_menus_summary_on">āĻĒ⧁āϰāĻžāϤāύ āϏ⧇āϟāĻŋāĻ‚āϏ āĻŽā§‡āύ⧁ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāĻšā§āϛ⧇</string>
<string name="revanced_restore_old_settings_menus_summary_off">āĻĒ⧁āϰāĻžāϤāύ āϏ⧇āϟāĻŋāĻ‚āϏ āĻŽā§‡āύ⧁ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāĻšā§āϛ⧇ āύāĻž</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">Shorts āĻŦā§āϝāĻžāĻ•āĻ—ā§āϰāĻžāωāĻ¨ā§āĻĄ āĻĒā§āϞ⧇ āĻ…āĻ•ā§āώāĻŽ āĻ•āϰ⧁āύ</string>
@@ -446,6 +506,9 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_swipe_overlay_background_alpha_summary">āĻ¸ā§‹ā§ŸāĻžāχāĻĒ āĻ“āĻ­āĻžāϰāϞ⧇ āĻŦā§āϝāĻžāĻ•āĻ—ā§āϰāĻžāωāĻ¨ā§āĻĄā§‡āϰ āĻĻ⧃āĻļā§āϝāĻŽāĻžāύāϤāĻž</string>
<string name="revanced_swipe_threshold_title">āĻ¸ā§‹ā§ŸāĻžāχāĻĒ āĻĨā§āϰ⧇āĻļāĻšā§‹āĻ˛ā§āĻĄ āĻāϰ āĻŽāĻžāĻ¤ā§āϰāĻž</string>
<string name="revanced_swipe_threshold_summary">āĻ¸ā§‹ā§ŸāĻžāχāĻĒ āĻ•āϰāĻžāϰ āĻĨā§āϰ⧇āĻļāĻšā§‹āĻ˛ā§āĻĄā§‡āϰ āĻĒāϰāĻŋāĻŽāĻžāĻŖ</string>
<string name="revanced_swipe_change_video_title">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāϤ⧇ āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻ•āϰ⧇ āϏāĻ•ā§āώāĻŽ āĻ•āϰ⧁āύ</string>
<string name="revanced_swipe_change_video_summary_on">āĻĢ⧁āϞāĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻŽā§‹āĻĄā§‡ āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻ•āϰāϞ⧇ āĻĒāϰāĻŦāĻ°ā§āϤ⧀/āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻšāĻŦ⧇</string>
<string name="revanced_swipe_change_video_summary_off">āĻĢ⧁āϞāĻ¸ā§āĻ•ā§āϰāĻŋāύ āĻŽā§‹āĻĄā§‡ āϏ⧋āϝāĻŧāĻžāχāĻĒ āĻ•āϰāϞ⧇ āĻĒāϰāĻŦāĻ°ā§āϤ⧀ /āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻšāĻŦ⧇ āύāĻž</string>
</patch>
<patch id="layout.autocaptions.autoCaptionsPatch">
<string name="revanced_auto_captions_title">āĻ¸ā§āĻŦ⧟āĻ‚āĻ•ā§āϰāĻŋ⧟ āĻ•ā§āϝāĻžāĻĒāĻļāύ āĻŦāĻ¨ā§āϧ āĻ•āϰ⧁āύ</string>
@@ -629,15 +692,18 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_shorts_player_screen_summary">Shorts āĻĒā§āϞ⧇āϝāĻŧāĻžāϰ⧇ āωāĻĒāĻžāĻĻāĻžāύ āϞ⧁āĻ•āĻžāύ⧋ āĻŦāĻž āĻĻ⧇āĻ–āĻžāύ⧋</string>
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">āĻĒā§āϰāϧāĻžāύ āĻĢāĻŋāĻĄā§‡ Shorts āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_shorts_home_summary_on">āĻĒā§āϰāϧāĻžāύ āĻĢāĻŋāĻĄā§‡ Shorts āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_shorts_home_summary_off">āĻĒā§āϰāϧāĻžāύ āĻĢāĻŋāĻĄā§‡ Shorts āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_shorts_home_summary_on">āĻŦāĻžāĻĄāĻŧāĻŋāϰ āĻĢāĻŋāĻĄ āĻāĻŦāĻ‚ āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āϞ⧁āĻ•āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_hide_shorts_home_summary_off">āĻŦāĻžāĻĄāĻŧāĻŋāϰ āĻĢāĻŋāĻĄ āĻāĻŦāĻ‚ āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_hide_shorts_subscriptions_title">āϏāĻĻāĻ¸ā§āϝāϤāĻž āĻĢāĻŋāĻĄā§‡ Shorts āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">āϏāĻĻāĻ¸ā§āϝāϤāĻž āĻĢāĻŋāĻĄā§‡ Shorts āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">āϏāĻĻāĻ¸ā§āϝāϤāĻž āĻĢāĻŋāĻĄā§‡ Shorts āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">āϏāĻžāĻŦāĻ¸ā§āĻ•ā§āϰāĻŋāĻĒāĻļāύ āĻĢāĻŋāĻĄā§‡ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">āϏāĻžāĻŦāĻ¸ā§āĻ•ā§āϰāĻŋāĻĒāĻļāύ āĻĢāĻŋāĻĄā§‡ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_shorts_search_title">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ Shorts āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_shorts_search_summary_on">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ Shorts āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_shorts_search_summary_off">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ Shorts āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_shorts_search_summary_on">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āϞ⧁āĻ•āĻžāύ⧋</string>
<string name="revanced_hide_shorts_search_summary_off">āϏāĻžāĻ°ā§āϚ āϰ⧇āϜāĻžāĻ˛ā§āĻŸā§‡ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_shorts_history_title">Shortsāϗ⧁āϞāĻŋāϕ⧇ āĻ“āϝāĻŧāĻžāϚ āχāϤāĻŋāĻšāĻžāϏ⧇ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_shorts_history_summary_on">āĻ“āϝāĻŧāĻžāϚ āĻšāĻŋāĻ¸ā§āĻŸā§āϰāĻŋāϤ⧇ āϞ⧁āĻ•āĻžāύ⧋</string>
<string name="revanced_hide_shorts_history_summary_off">āĻ“āϝāĻŧāĻžāϚ āχāϤāĻŋāĻšāĻžāϏ⧇ āĻĻ⧇āĻ–āĻžāύ⧋</string>
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_join_button_title">āĻœā§Ÿā§‡āύ āĻ•āϰ⧁āύ āĻŦā§‹āϤāĻžāĻŽ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_shorts_join_button_summary_on">āĻœā§Ÿā§‡āύ āĻ•āϰ⧁āύ āĻŦā§‹āϤāĻžāĻŽ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
@@ -739,6 +805,13 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_player_popup_panels_summary_on">āĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ āĻĒāĻĒāφāĻĒ āĻĒā§āϝāĻžāύ⧇āϞāϗ⧁āϞ⧋ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_player_popup_panels_summary_off">āĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ āĻĒāĻĒāφāĻĒ āĻĒā§āϝāĻžāύ⧇āϞāϗ⧁āϞ⧋ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
</patch>
<patch id="layout.player.fullscreen.exitFullscreenPatch">
<string name="revanced_exit_fullscreen_title">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻļ⧇āώ āĻšāĻ“āϝāĻŧāĻžāϰ āϏāĻŽāϝāĻŧ āĻĒā§‚āĻ°ā§āĻŖ āĻĒāĻ°ā§āĻĻāĻž āĻŽā§‹āĻĄ āĻĨ⧇āϕ⧇ āĻŦ⧇āϰāĻŋāϝāĻŧ⧇ āϝāĻžāύ</string>
<string name="revanced_exit_fullscreen_entry_1">āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋāϝāĻŧ</string>
<string name="revanced_exit_fullscreen_entry_2">āφāύ⧁āĻ­ā§‚āĻŽāĻŋāĻ•</string>
<string name="revanced_exit_fullscreen_entry_3">āĻ˛ā§āϝāĻžāĻ¨ā§āĻĄāĻ¸ā§āϕ⧇āĻĒ</string>
<string name="revanced_exit_fullscreen_entry_4">āφāύ⧁āĻ­ā§‚āĻŽāĻŋāĻ• āĻāĻŦāĻ‚ āĻ˛ā§āϝāĻžāĻ¨ā§āĻĄāĻ¸ā§āϕ⧇āĻĒ</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">āĻĒā§‚āĻ°ā§āĻŖ āĻĒāĻ°ā§āĻĻāĻžāϝāĻŧ āĻ­āĻŋāĻĄāĻŋāĻ“ āϖ⧁āϞ⧁āύ</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§‚āĻ°ā§āĻŖ āĻĒāĻ°ā§āĻĻāĻžāϝāĻŧ āϖ⧁āϞāĻŦ⧇</string>
@@ -1007,6 +1080,23 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_sb_reset">āĻĒ⧁āύāϰāĻžāϝāĻŧ āϏ⧇āϟ āĻ•āϰ⧁āύ</string>
<string name="revanced_sb_about">āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ</string>
<string name="revanced_sb_about_api_sum">āĻĄā§‡āϟāĻž SponsorBlock API āĻĻā§āĻŦāĻžāϰāĻž āϏāϰāĻŦāϰāĻžāĻš āĻ•āϰāĻž āĻšāϝāĻŧāĨ¤ āφāϰāĻ“ āϜāĻžāύāϤ⧇ āĻāĻŦāĻ‚ āĻ…āĻ¨ā§āϝāĻžāĻ¨ā§āϝ āĻĒā§āĻ˛ā§āϝāĻžāϟāĻĢāĻ°ā§āĻŽā§‡āϰ āĻĄāĻžāωāύāϞ⧋āĻĄ āĻĻ⧇āĻ–āϤ⧇ āĻāĻ–āĻžāύ⧇ āĻŸā§āϝāĻžāĻĒ āĻ•āϰ⧁āύ</string>
</patch>
<patch id="layout.formfactor.changeFormFactorPatch">
<string name="revanced_change_form_factor_title">āϞ⧇āφāωāϟ āĻĢāĻ°ā§āĻŽ āĻĢā§āϝāĻžāĻ•ā§āϟāϰ</string>
<string name="revanced_change_form_factor_entry_1">āĻĄāĻŋāĻĢāĻ˛ā§āϟ</string>
<string name="revanced_change_form_factor_entry_2">āĻĢā§‹āύ</string>
<string name="revanced_change_form_factor_entry_3">āĻŸā§āϝāĻžāĻŦāϞ⧇āϟ</string>
<string name="revanced_change_form_factor_entry_4">āĻ¸ā§āĻŦāϝāĻŧāĻ‚āϚāĻžāϞāĻŋāϤ</string>
<string name="revanced_change_form_factor_user_dialog_message">"āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύāϗ⧁āϞ⧋ āĻšāϞ:
āĻŸā§āϝāĻžāĻŦāϞ⧇āϟ āϞ⧇āφāωāϟ
â€ĸ āĻ•āĻŽāĻŋāωāύāĻŋāϟāĻŋ āĻĒā§‹āĻ¸ā§āϟ āĻ—ā§‹āĻĒāύ
āĻ¸ā§āĻŦāϝāĻŧāĻ‚āϚāĻžāϞāĻŋāϤ āϞ⧇āφāωāϟ
â€ĸ āϘāĻĄāĻŧāĻŋāϰ āχāϤāĻŋāĻšāĻžāϏ āĻŽā§‡āύ⧁ āĻ—ā§‹āĻĒāύ
â€ĸ āĻāĻ•ā§āϏāĻĒā§āϞ⧋āϰ āĻŸā§āϝāĻžāĻŦ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇
â€ĸ āĻļāĻ°ā§āϟāϏ āύāĻŋāϝāĻŧāĻŽāĻŋāϤ āĻĒā§āϞ⧇āϝāĻŧāĻžāϰ⧇ āĻ–ā§‹āϞ⧇
â€ĸ āĻĢāĻŋāĻĄ āĻŦāĻŋāώāϝāĻŧ āĻāĻŦāĻ‚ āĻšā§āϝāĻžāύ⧇āϞ āĻĻā§āĻŦāĻžāϰāĻž āϏāĻ‚āĻ—āĻ āĻŋāϤ āĻšāϝāĻŧ"</string>
</patch>
<patch id="layout.spoofappversion.spoofAppVersionPatch">
<string name="revanced_spoof_app_version_title">āĻ…ā§āϝāĻžāĻĒ āϏāĻ‚āĻ¸ā§āĻ•āϰāĻŖ āĻ¸ā§āĻĒ⧁āĻĢ āĻ•āϰ⧁āύ</string>
@@ -1021,6 +1111,7 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
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">āĻ¸ā§āĻĒ⧁āĻĢ āĻ…ā§āϝāĻžāĻĒ āϏāĻ‚āĻ¸ā§āĻ•āϰāĻŖ āϞāĻ•ā§āĻˇā§āϝ</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 - āĻĒ⧁āϰāύ⧋ Shorts āĻĒā§āϞ⧇āϝāĻŧāĻžāϰ āφāχāĻ•āύ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰ⧁āύ</string>
<string name="revanced_spoof_app_version_target_entry_2">19.26.42 - āĻĒ⧁āϰ⧋āύ⧋ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āĻāĻŦāĻ‚ āϟ⧁āϞāĻŦāĻžāϰ āφāχāĻ•āύ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰ⧁āύ</string>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_legacy_entry_1">18.33.40 - āĻ›āĻĻā§āĻŽāĻŦ⧇āĻļāĻŋ āĻŽā§‹āĻĄā§‡ RYD āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰ⧇</string>
<string name="revanced_spoof_app_version_target_legacy_entry_2">18.20.39 - āĻĒā§āϰāĻļā§āĻŦāĻ¸ā§āϤ āĻ­āĻŋāĻĄāĻŋāĻ“ āĻ¸ā§āĻĒāĻŋāĻĄ āĻāĻŦāĻ‚ āϗ⧁āĻŖāĻŽāĻžāύ āĻŽā§‡āύ⧁ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰ⧇</string>
@@ -1064,12 +1155,6 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_shorts_autoplay_background_summary_on">āĻĒāϟāĻ­ā§‚āĻŽāĻŋāϤ⧇ Shorts āĻ…āĻŸā§‹āĻĒā§āϞ⧇ āĻšāĻŦ⧇</string>
<string name="revanced_shorts_autoplay_background_summary_off">āĻĒāϟāĻ­ā§‚āĻŽāĻŋāϤ⧇ Shorts āĻĒ⧁āύāϰāĻžāĻŦ⧃āĻ¤ā§āϤāĻŋ āĻšāĻŦ⧇</string>
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
<string name="revanced_tablet_layout_title">āĻŸā§āϝāĻžāĻŦāϞ⧇āϟ āϞ⧇āφāωāϟ āϏāĻ•ā§āϰāĻŋ⧟ āĻ•āϰ⧁āύ</string>
<string name="revanced_tablet_layout_summary_on">āĻŸā§āϝāĻžāĻŦāϞ⧇āϟ āϞ⧇āφāωāϟ āϏāĻ•ā§āϰāĻŋ⧟ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_tablet_layout_summary_off">āĻŸā§āϝāĻžāĻŦāϞ⧇āϟ āϞ⧇āφāωāϟ āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋ⧟ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_tablet_layout_user_dialog_message">āĻŸā§āϝāĻžāĻŦāϞ⧇āϟ āϞ⧇āφāωāĻŸā§‡ āĻ•āĻŽāĻŋāωāύāĻŋāϟāĻŋ āĻĒā§‹āĻ¸ā§āϟ āĻĻ⧇āĻ–āĻžāĻŦ⧇ āύāĻž</string>
</patch>
<patch id="layout.miniplayer.miniplayerPatch">
<string name="revanced_miniplayer_screen_title">āĻŽāĻŋāύāĻŋāĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ</string>
<string name="revanced_miniplayer_screen_summary">āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻŽāĻ§ā§āϝāĻ•āĻžāϰ āĻŽāĻŋāύāĻŋāĻŽāĻžāχāϜāĻĄ āĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ āĻāϰ āϧāϰāĻŖ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧁āύ</string>
@@ -1234,6 +1319,7 @@ DeArrow āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāϰāĻ“ āϜāĻžāύāϤ⧇ āĻāĻ–āĻžāύ⧇ āϟ
<string name="revanced_force_original_audio_title">āĻŽā§‚āϞ āĻ…āĻĄāĻŋāĻ“ āĻŦāϞāĻĒā§‚āĻ°ā§āĻŦāĻ• āϚāĻžāϞ⧁ āĻ•āϰ⧁āύ</string>
<string name="revanced_force_original_audio_summary_on">āĻŽā§‚āϞ āĻ…āĻĄāĻŋāĻ“ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϛ⧇</string>
<string name="revanced_force_original_audio_summary_off">āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ…āĻĄāĻŋāĻ“ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāϛ⧇</string>
<string name="revanced_force_original_audio_not_available">āĻāχ āĻŦ⧈āĻļāĻŋāĻˇā§āĻŸā§āϝāϟāĻŋ āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻžāϰ āϜāĻ¨ā§āϝ, iOS āĻ•ā§āϞāĻžā§Ÿā§‡āĻ¨ā§āϟ āĻĒā§āϰāĻ•āĻžāϰ⧇ āĻ¸ā§āĻŸā§āϰāĻŋāĻŽ āĻ¸ā§āĻĒ⧁āĻĢāĻŋāĻ‚ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧁āύ</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
@@ -1261,6 +1347,8 @@ DeArrow āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāϰāĻ“ āϜāĻžāύāϤ⧇ āĻāĻ–āĻžāύ⧇ āϟ
<string name="revanced_custom_playback_speeds_invalid">āĻ•āĻžāĻ¸ā§āϟāĻŽ āĻ—āϤāĻŋ %s āĻāϰ āĻšā§‡āϝāĻŧ⧇ āĻ•āĻŽ āĻšāϤ⧇ āĻšāĻŦ⧇</string>
<string name="revanced_custom_playback_speeds_parse_exception">āĻ…āĻŦ⧈āϧ āĻ•āĻžāĻ¸ā§āϟāĻŽ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ—āϤāĻŋ</string>
<string name="revanced_custom_playback_speeds_auto">āĻ¸ā§āĻŦāϤāĻ¸ā§āĻĢā§‚āĻ°ā§āϤāĻ­āĻžāĻŦ⧇</string>
<string name="revanced_speed_tap_and_hold_title">āĻ•āĻžāĻ¸ā§āϟāĻŽ āĻŸā§āϝāĻžāĻĒ āĻāĻ¨ā§āĻĄ āĻšā§‹āĻ˛ā§āĻĄ āĻ¸ā§āĻĒāĻŋāĻĄ</string>
<string name="revanced_speed_tap_and_hold_summary">ā§Ļ-ā§Ž āĻāϰ āĻŽāĻ§ā§āϝ⧇ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ¸ā§āĻĒāĻŋāĻĄ</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
<string name="revanced_remember_playback_speed_last_selected_title">āĻĒā§āϞ⧇āĻŦā§āϝāĻžāϕ⧇āϰ āĻ¸ā§āĻĒāĻŋāĻĄ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻŽāύ⧇ āϰāĻžāϖ⧁āύ</string>
@@ -1289,65 +1377,27 @@ DeArrow āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāϰāĻ“ āϜāĻžāύāϤ⧇ āĻāĻ–āĻžāύ⧇ āϟ
āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ•āĻžāϜ āύāĻžāĻ“ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">āĻāχ āϏ⧇āϟāĻŋāĻ‚āϟāĻŋ āĻŦāĻ¨ā§āϧ āĻ•āϰāĻžāϰ āĻĢāϞ⧇ āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ¤ā§āϰ⧁āϟāĻŋ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤</string>
<string name="revanced_spoof_video_streams_client_type_title">āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ•ā§āϞāĻžā§Ÿā§‡āĻ¨ā§āϟ</string>
<string name="revanced_spoof_video_streams_about_title">āĻ¸ā§āĻĒ⧁āĻĢāĻŋāĻ‚ āĻāϰ āĻĒāĻžāĻ°ā§āĻļā§āĻŦāĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž</string>
<string name="revanced_spoof_video_streams_about_summary">"â€ĸ āĻŦāĻžāĻšā§āϚāĻžāĻĻ⧇āϰ āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āϞ⧇ āύāĻžāĻ“ āĻšāϤ⧇ āĻĒāĻžāϰ⧇
â€ĸ āĻ…āĻĄāĻŋāĻ“ āĻŸā§āĻ°ā§āϝāĻžāĻ• āĻŽā§‡āύ⧁ āĻ…āύ⧁āĻĒāĻ¸ā§āĻĨāĻŋāϤ
â€ĸ āĻ¸ā§āĻĨāĻŋāϤāĻŋāĻļā§€āϞ āĻ­āϞāĻŋāωāĻŽ āωāĻĒāϞāĻŦā§āϧ āύāϝāĻŧ
â€ĸ āĻœā§‹āϰ āĻ•āϰ⧇ āĻŽā§‚āϞ āĻ…āĻĄāĻŋāĻ“ āωāĻĒāϞāĻŦā§āϧ āύāϝāĻŧ⧎"</string>
<string name="revanced_spoof_video_streams_language_title">āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ…āĻĄāĻŋāĻ“ āĻ¸ā§āĻŸā§āϰāĻŋāĻŽ āĻ­āĻžāώāĻž</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">āĻ…ā§āϝāĻžāĻĒ āĻ­āĻžāώāĻž</string>
<string name="revanced_spoof_video_streams_language_AR">āφāϰāĻŦāĻŋ</string>
<string name="revanced_spoof_video_streams_language_AZ">āφāϜāĻžāϰāĻŦāĻžāχāϜāĻžāύāĻŋ</string>
<string name="revanced_spoof_video_streams_language_BG">āĻŦ⧁āϞāϗ⧇āϰāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_BN">āĻŦāĻžāĻ‚āϞāĻž</string>
<string name="revanced_spoof_video_streams_language_CA">āĻ•āĻžāϤāĻžāϞāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_CS">āĻšā§‡āĻ•</string>
<string name="revanced_spoof_video_streams_language_DA">āĻĄā§‡āύāĻŋāĻļ</string>
<string name="revanced_spoof_video_streams_language_DE">āϜāĻžāĻ°ā§āĻŽāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_EL">āĻ—ā§āϰ⧀āĻ•</string>
<string name="revanced_spoof_video_streams_language_EN">āχāĻ‚āϰ⧇āϜāĻŋ</string>
<string name="revanced_spoof_video_streams_language_ES">āĻ¸ā§āĻĒā§āϝāĻžāύāĻŋāĻļ</string>
<string name="revanced_spoof_video_streams_language_ET">āĻāĻ¸ā§āϤ⧋āύāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_FA">āĻĢāĻžāĻ°ā§āϏāĻŋ</string>
<string name="revanced_spoof_video_streams_language_FI">āĻĢāĻŋāύāĻ˛ā§āϝāĻžāĻ¨ā§āĻĄ</string>
<string name="revanced_spoof_video_streams_language_FR">āĻĢāϰāĻžāϏāĻŋ</string>
<string name="revanced_spoof_video_streams_language_GU">āϗ⧁āϜāϰāĻžāϟāĻŋ</string>
<string name="revanced_spoof_video_streams_language_HI">āĻšāĻŋāĻ¨ā§āĻĻāĻŋ</string>
<string name="revanced_spoof_video_streams_language_HR">āĻ•ā§āϰ⧋āϝāĻŧ⧇āĻļāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_HU">āĻšāĻžāĻ™ā§āϗ⧇āϰāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_ID">āχāĻ¨ā§āĻĻā§‹āύ⧇āĻļāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_IT">āχāϤāĻžāϞ⧀āϝāĻŧ</string>
<string name="revanced_spoof_video_streams_language_JA">āϜāĻžāĻĒāĻžāύāĻŋ</string>
<string name="revanced_spoof_video_streams_language_KK">āĻ•āĻžāϜāĻžāĻ–</string>
<string name="revanced_spoof_video_streams_language_KO">āϕ⧋āϰāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_LT">āϞāĻŋāĻĨ⧁āϝāĻŧāĻžāύāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_LV">āϞāĻžāϤāĻ­āĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_MK">āĻŽā§āϝāĻžāϏ⧇āĻĄā§‹āύāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_MN">āĻŽāĻ™ā§āĻ—ā§‹āϞāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_MR">āĻŽāĻžāϰāĻžāĻ āĻŋ</string>
<string name="revanced_spoof_video_streams_language_MS">āĻŽāĻžāϞāϝāĻŧ</string>
<string name="revanced_spoof_video_streams_language_MY">āĻŦāĻžāĻ°ā§āĻŽāĻŋāϜ</string>
<string name="revanced_spoof_video_streams_language_NL">āĻĄāĻžāϚ</string>
<string name="revanced_spoof_video_streams_language_OR">āĻ“āĻĄāĻŧāĻŋāϝāĻŧāĻž</string>
<string name="revanced_spoof_video_streams_language_PA">āĻĒāĻžā§āϜāĻžāĻŦāĻŋ</string>
<string name="revanced_spoof_video_streams_language_PL">āĻĒā§‹āϞāĻŋāĻļ</string>
<string name="revanced_spoof_video_streams_language_PT_BR">āĻĒāĻ°ā§āϤ⧁āĻ—āĻŋāϜ (āĻŦā§āϰāĻžāϜāĻŋāϞ)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">āĻĒāĻ°ā§āϤ⧁āĻ—āĻŋāϜ (āĻĒāĻ°ā§āϤ⧁āĻ—āĻžāϞ)</string>
<string name="revanced_spoof_video_streams_language_RO">āϰ⧋āĻŽāĻžāύ⧀āϝāĻŧ</string>
<string name="revanced_spoof_video_streams_language_RU">āϰāĻžāĻļāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_SK">āĻ¸ā§āϞ⧋āĻ­āĻžāĻ•</string>
<string name="revanced_spoof_video_streams_language_SL">āĻ¸ā§āϞ⧋āϭ⧇āύāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_SR">āϏāĻžāĻ°ā§āĻŦāĻŋāϝāĻŧāĻžāύ</string>
<string name="revanced_spoof_video_streams_language_SV">āϏ⧁āχāĻĄāĻŋāĻļ</string>
<string name="revanced_spoof_video_streams_language_SW">āϏ⧋āϝāĻŧāĻžāĻšāĻŋāϞāĻŋ</string>
<string name="revanced_spoof_video_streams_language_TA">āϤāĻžāĻŽāĻŋāϞ</string>
<string name="revanced_spoof_video_streams_language_TE">āϤ⧇āϞ⧁āϗ⧁</string>
<string name="revanced_spoof_video_streams_language_TH">āĻĨāĻžāχ</string>
<string name="revanced_spoof_video_streams_language_TR">āϤ⧁āĻ°ā§āĻ•āĻŋ</string>
<string name="revanced_spoof_video_streams_language_UK">āχāωāĻ•ā§āϰ⧇āύ⧀āϝāĻŧ</string>
<string name="revanced_spoof_video_streams_language_UR">āωāĻ°ā§āĻĻ⧁</string>
<string name="revanced_spoof_video_streams_language_VI">āĻ­āĻŋāϝāĻŧ⧇āϤāύāĻžāĻŽāĻŋ</string>
<string name="revanced_spoof_video_streams_language_ZH">āĻšā§€āύāĻž</string>
<!-- 'no auth' means no authentication -->
<string name="revanced_spoof_video_streams_client_type_android_vr_no_auth">Android VR (āĻ¸ā§āĻŦā§€āĻ•ā§ƒāϤāĻŋ āĻ›āĻžāĻĄāĻŧāĻž)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">iOS AVC (H.264) āĻŦāĻžāĻ§ā§āϝāϤāĻžāĻŽā§‚āϞāĻ• āĻ•āϰ⧁āύ</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">āĻ­āĻŋāĻĄāĻŋāĻ“ āϕ⧋āĻĄā§‡āĻ• AVC (H.264) āĻ āĻŦāĻžāĻ§ā§āϝāϤāĻžāĻŽā§‚āϞāĻ• āĻ•āϰāĻž āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">āĻ­āĻŋāĻĄāĻŋāĻ“ āϕ⧋āĻĄā§‡āĻ• āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āύāĻŋāĻ°ā§āϧāĻžāϰāĻŋāϤ āĻšā§Ÿ</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"āĻāϟāĻŋ āϏāĻ•ā§āώāĻŽ āĻ•āϰāϞ⧇ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āϞāĻžāχāĻĢ āωāĻ¨ā§āύāϤ āĻšāϤ⧇ āĻĒāĻžāϰ⧇ āĻāĻŦāĻ‚ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ¸ā§āϟāĻžāϟāĻžāϰāĻŋāĻ‚ āϏāĻŽāĻ¸ā§āϝāĻž āϏāĻŽāĻžāϧāĻžāύ āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤
AVC-āĻāϰ āϏāĻ°ā§āĻŦā§‹āĻšā§āϚ āϰ⧇āĻœā§‹āϞāĻŋāωāĻļāύ āĻšāϞ 1080p, Opus āĻ…āĻĄāĻŋāĻ“ āϕ⧋āĻĄā§‡āĻ• āĻĒāĻžāĻ“āϝāĻŧāĻž āϝāĻžāϝāĻŧ āύāĻž āĻāĻŦāĻ‚ VP9 āĻŦāĻž AV1-āĻāϰ āϤ⧁āϞāύāĻžāϝāĻŧ āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāϕ⧇ āĻŦ⧇āĻļāĻŋ āχāĻ¨ā§āϟāĻžāϰāύ⧇āϟ āĻĄā§‡āϟāĻž āĻŦā§āϝāĻŦāĻšāĻžāϰ āĻ•āϰāĻž āĻšāĻŦā§‡ã€‚"</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">āφāχāĻ“āĻāϏ āĻ¸ā§āĻĒ⧁āĻĢāĻŋāĻ‚āϝāĻŧ⧇āϰ āĻĒāĻžāĻ°ā§āĻļā§āĻŦ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"â€ĸ āĻŽā§āĻ­āĻŋ āĻŦāĻž āĻ…āĻ°ā§āĻĨ āĻĒā§āϰāĻĻāĻžāύ⧇āϰ āĻ­āĻŋāĻĄāĻŋāĻ“ āϚāĻžāϞ⧁ āύāĻžāĻ“ āĻšāϤ⧇ āĻĒāĻžāϰ⧇
â€ĸ āĻ¸ā§āĻĨāĻŋāϰ āĻ­āϞāĻŋāωāĻŽ āĻĒāĻžāĻ“āϝāĻŧāĻž āϝāĻžāϝāĻŧ āύāĻž
â€ĸ āĻ­āĻŋāĻĄāĻŋāĻ“āϗ⧁āϞāĻŋ 1 āϏ⧇āϕ⧇āĻ¨ā§āĻĄ āφāϗ⧇ āĻļ⧇āώ āĻšāϝāĻŧ⧇ āϝāĻžāϝāĻŧ"</string>
<string name="revanced_spoof_video_streams_about_android_title">Android āĻ¸ā§āĻĒ⧁āĻĢāĻŋāĻ‚āϝāĻŧ⧇āϰ āĻĒāĻžāĻ°ā§āĻļā§āĻŦāĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž</string>
<string name="revanced_spoof_video_streams_about_android_summary">"â€ĸ āĻ…āĻĄāĻŋāĻ“ āĻŸā§āĻ°ā§āϝāĻžāĻ• āĻŽā§‡āύ⧁ āύ⧇āχ
â€ĸ āĻ¸ā§āĻĨāĻŋāϰ āĻ­āϞāĻŋāωāĻŽ āĻĒāĻžāĻ“āϝāĻŧāĻž āϝāĻžāϝāĻŧ āύāĻž
â€ĸ āĻŽā§‚āϞ āĻ…āĻĄāĻŋāĻ“ āĻœā§‹āϰ āĻ•āϰ⧇ āϚāĻžāϞ⧁ āĻ•āϰāĻž āϝāĻžāϝāĻŧ āύāĻž"</string>
<string name="revanced_spoof_video_streams_about_no_av1">â€ĸ āϕ⧋āύ⧋ AV1 āĻ­āĻŋāĻĄāĻŋāĻ“ āϕ⧋āĻĄā§‡āĻ• āύ⧇āχ</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">āĻ¸ā§āĻŸā§āϝāĻžāϟāϏ āĻĢāϰ āύāĻžāĻ°ā§āĻĄāϏ⧇ āĻĻ⧇āĻ–āĻžāύ</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">āĻ¸ā§āĻŸā§āϝāĻžāϟāϏ āĻĢāϰ āύāĻžāĻ°ā§āĻĄāϏ⧇ āĻ•ā§āϞāĻžāϝāĻŧ⧇āĻ¨ā§āϟ āĻĒā§āϰāĻ•āĻžāϰ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāĻŦ⧇</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">āĻ¸ā§āĻŸā§āϝāĻžāϟāϏ āĻĢāϰ āύāĻžāĻ°ā§āĻĄāϏ⧇ āĻ•ā§āϞāĻžāϝāĻŧ⧇āĻ¨ā§āϟ āϞ⧁āĻ•āĻžāύ⧋ āĻšāĻŦ⧇</string>
<string name="revanced_spoof_video_streams_language_title">VR āĻĄāĻŋāĻĢāĻ˛ā§āϟ āĻ…āĻĄāĻŋāĻ“ āĻ¸ā§āĻŸā§āϰāĻŋāĻŽ āĻ­āĻžāώāĻž</string>
</patch>
</app>
<app id="twitch">

Some files were not shown because too many files have changed in this diff Show More