Compare commits

..

63 Commits

Author SHA1 Message Date
semantic-release-bot
1a8146dbc8 chore: Release v5.33.0-dev.10 [skip ci]
# [5.33.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.9...v5.33.0-dev.10) (2025-08-04)

### Bug Fixes

* **YouTube - Video quality:** Fix wrong qualities sometimes shown in player button dialog ([178eed7](178eed7fcd))
2025-08-04 17:23:50 +00:00
LisoUseInAIKyrios
178eed7fcd fix(YouTube - Video quality): Fix wrong qualities sometimes shown in player button dialog 2025-08-04 13:21:02 -04:00
semantic-release-bot
621292644c chore: Release v5.33.0-dev.9 [skip ci]
# [5.33.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.8...v5.33.0-dev.9) (2025-08-04)

### Bug Fixes

* **YouTube - Force original audio:** Disable a/b feature flag that forces localized audio ([#5582](https://github.com/ReVanced/revanced-patches/issues/5582)) ([1dd01cf](1dd01cf54a))
2025-08-04 01:27:12 +00:00
LisoUseInAIKyrios
1dd01cf54a fix(YouTube - Force original audio): Disable a/b feature flag that forces localized audio (#5582) 2025-08-03 21:23:27 -04:00
semantic-release-bot
8c31374c53 chore: Release v5.33.0-dev.8 [skip ci]
# [5.33.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.7...v5.33.0-dev.8) (2025-08-03)

### Bug Fixes

* **NFC Tools:** Remove broken patch that is no longer supported ([#5584](https://github.com/ReVanced/revanced-patches/issues/5584)) ([2e177a8](2e177a8839))
2025-08-03 23:44:42 +00:00
LisoUseInAIKyrios
2e177a8839 fix(NFC Tools): Remove broken patch that is no longer supported (#5584) 2025-08-03 19:41:54 -04:00
github-actions[bot]
cfffd422f8 chore: Sync translations (#5586) 2025-08-03 19:41:07 -04:00
github-actions[bot]
37aab8382e chore: Sync translations (#5585) 2025-08-03 19:38:17 -04:00
semantic-release-bot
f4950ec2ea chore: Release v5.33.0-dev.7 [skip ci]
# [5.33.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.6...v5.33.0-dev.7) (2025-08-03)

### Features

* **YouTube:** Add player button to change video quality ([#5435](https://github.com/ReVanced/revanced-patches/issues/5435)) ([7bdc328](7bdc32867a))
2025-08-03 15:26:39 +00:00
MarcaD
7bdc32867a feat(YouTube): Add player button to change video quality (#5435)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-08-03 11:23:46 -04:00
semantic-release-bot
6e60ac6963 chore: Release v5.33.0-dev.6 [skip ci]
# [5.33.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.5...v5.33.0-dev.6) (2025-07-31)

### Bug Fixes

* **YouTube - Video quality:** Use 1080p enhanced bitrate for Premium users ([#5565](https://github.com/ReVanced/revanced-patches/issues/5565)) ([1adbd56](1adbd563b2))
2025-07-31 18:30:03 +00:00
LisoUseInAIKyrios
1adbd563b2 fix(YouTube - Video quality): Use 1080p enhanced bitrate for Premium users (#5565) 2025-07-31 14:27:17 -04:00
github-actions[bot]
9ccf13b680 chore: Sync translations (#5567) 2025-07-31 14:27:05 -04:00
semantic-release-bot
7b8ca9c018 chore: Release v5.33.0-dev.5 [skip ci]
# [5.33.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.4...v5.33.0-dev.5) (2025-07-31)

### Bug Fixes

* **YouTube - Litho filter:** Correctly filter identifier of older YouTube targets ([b1d164b](b1d164b446))
2025-07-31 10:35:51 +00:00
github-actions[bot]
ae6dd23d08 chore: Sync translations (#5564) 2025-07-31 06:33:31 -04:00
LisoUseInAIKyrios
b1d164b446 fix(YouTube - Litho filter): Correctly filter identifier of older YouTube targets 2025-07-31 06:33:12 -04:00
dependabot[bot]
87c39dd485 chore(deps-dev): Bump semantic-release from 24.2.6 to 24.2.7 (#5545) 2025-07-30 10:31:08 -04:00
semantic-release-bot
1549ac12aa chore: Release v5.33.0-dev.4 [skip ci]
# [5.33.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.3...v5.33.0-dev.4) (2025-07-30)

### Performance Improvements

* **YouTube:** Filter identifier callback only on root component creation ([#5558](https://github.com/ReVanced/revanced-patches/issues/5558)) ([5d08fdd](5d08fdddb8))
2025-07-30 13:21:43 +00:00
LisoUseInAIKyrios
5d08fdddb8 perf(YouTube): Filter identifier callback only on root component creation (#5558) 2025-07-30 09:18:20 -04:00
semantic-release-bot
98114e5bde chore: Release v5.33.0-dev.3 [skip ci]
# [5.33.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.2...v5.33.0-dev.3) (2025-07-30)

### Bug Fixes

* **YouTube - Playback speed:** Use old speed menu for player button if enabled ([a4817df](a4817dfdd0))
2025-07-30 10:06:04 +00:00
LisoUseInAIKyrios
a4817dfdd0 fix(YouTube - Playback speed): Use old speed menu for player button if enabled 2025-07-30 06:03:21 -04:00
semantic-release-bot
d4f05351e1 chore: Release v5.33.0-dev.2 [skip ci]
# [5.33.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.1...v5.33.0-dev.2) (2025-07-29)

### Features

* **ORF ON:** Add `Remove root detection` patch ([#5551](https://github.com/ReVanced/revanced-patches/issues/5551)) ([d92362b](d92362b0d9))
2025-07-29 08:33:08 +00:00
abichinger
d92362b0d9 feat(ORF ON): Add Remove root detection patch (#5551) 2025-07-29 04:30:43 -04:00
github-actions[bot]
afc7c75df1 chore: Sync translations (#5555) 2025-07-29 04:29:42 -04:00
semantic-release-bot
f0d4e9bfb4 chore: Release v5.33.0-dev.1 [skip ci]
# [5.33.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.32.0...v5.33.0-dev.1) (2025-07-28)

### Features

* **YouTube - Playback speed:** Add "Restore old playback speed menu" option ([#5552](https://github.com/ReVanced/revanced-patches/issues/5552)) ([e9e4cf3](e9e4cf39b6))
2025-07-28 18:47:44 +00:00
LisoUseInAIKyrios
e9e4cf39b6 feat(YouTube - Playback speed): Add "Restore old playback speed menu" option (#5552) 2025-07-28 22:44:18 +04:00
semantic-release-bot
0579a9f760 chore: Release v5.32.0 [skip ci]
# [5.32.0](https://github.com/ReVanced/revanced-patches/compare/v5.31.2...v5.32.0) (2025-07-27)

### Bug Fixes

* **Messenger - Hide inbox ads:** Support the latest app version ([8ec857a](8ec857a175))
* **YouTube  - Hide layout components:** Fix "Hide ticket shelf" ([#5516](https://github.com/ReVanced/revanced-patches/issues/5516)) ([9ddb3ac](9ddb3ac39d))
* **YouTube - GmsCore support:** Fix search suggestions when logged out by using correct search provider ([#5483](https://github.com/ReVanced/revanced-patches/issues/5483)) ([e4e81b8](e4e81b89ea))

### Features

* **Prime Video:** Add `Playback speed` patch ([#5444](https://github.com/ReVanced/revanced-patches/issues/5444)) ([f46dbcd](f46dbcd084))
* **YouTube - External downloads:** Improve the selection of the external downloader package ([#5504](https://github.com/ReVanced/revanced-patches/issues/5504)) ([cfd7780](cfd77800d6))
* **YT Music:** Support latest versions ([#5524](https://github.com/ReVanced/revanced-patches/issues/5524)) ([1258555](125855540b))
2025-07-27 13:17:58 +00:00
LisoUseInAIKyrios
1c0acef3f3 chore: Merge branch dev to main (#5479) 2025-07-27 17:14:36 +04:00
github-actions[bot]
2419adb77b chore: Sync translations (#5544) 2025-07-27 17:14:11 +04:00
semantic-release-bot
9e4113555b chore: Release v5.32.0-dev.5 [skip ci]
# [5.32.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.32.0-dev.4...v5.32.0-dev.5) (2025-07-26)

### Features

* **YT Music:** Support latest versions ([#5524](https://github.com/ReVanced/revanced-patches/issues/5524)) ([1258555](125855540b))
2025-07-26 06:30:30 +00:00
netceil
125855540b feat(YT Music): Support latest versions (#5524) 2025-07-26 10:27:47 +04:00
github-actions[bot]
a8eee825e6 chore: Sync translations (#5538) 2025-07-26 10:27:17 +04:00
semantic-release-bot
63859f0ef9 chore: Release v5.32.0-dev.4 [skip ci]
# [5.32.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.32.0-dev.3...v5.32.0-dev.4) (2025-07-25)

### Bug Fixes

* **Messenger - Hide inbox ads:** Support the latest app version ([8ec857a](8ec857a175))
2025-07-25 06:53:39 +00:00
github-actions[bot]
1c9000dbda chore: Sync translations (#5531) 2025-07-25 10:51:05 +04:00
LisoUseInAIKyrios
8ec857a175 fix(Messenger - Hide inbox ads): Support the latest app version 2025-07-25 10:46:10 +04:00
semantic-release-bot
f56c7868f5 chore: Release v5.32.0-dev.3 [skip ci]
# [5.32.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.32.0-dev.2...v5.32.0-dev.3) (2025-07-24)

### Features

* **YouTube - External downloads:** Improve the selection of the external downloader package ([#5504](https://github.com/ReVanced/revanced-patches/issues/5504)) ([cfd7780](cfd77800d6))
2025-07-24 07:31:13 +00:00
MarcaD
cfd77800d6 feat(YouTube - External downloads): Improve the selection of the external downloader package (#5504)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
2025-07-24 11:28:16 +04:00
semantic-release-bot
707deaef0b chore: Release v5.32.0-dev.2 [skip ci]
# [5.32.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.32.0-dev.1...v5.32.0-dev.2) (2025-07-23)

### Bug Fixes

* **YouTube  - Hide layout components:** Fix "Hide ticket shelf" ([#5516](https://github.com/ReVanced/revanced-patches/issues/5516)) ([9ddb3ac](9ddb3ac39d))
2025-07-23 12:05:24 +00:00
ILoveOpenSourceApplications
9ddb3ac39d fix(YouTube - Hide layout components): Fix "Hide ticket shelf" (#5516) 2025-07-23 16:02:53 +04:00
github-actions[bot]
a7d3b7c287 chore: Sync translations (#5519) 2025-07-23 16:02:21 +04:00
LisoUseInAIKyrios
30bac0397e chore(YouTube): Fix string typo 2025-07-20 15:38:40 +04:00
semantic-release-bot
c5fc187a35 chore: Release v5.32.0-dev.1 [skip ci]
# [5.32.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.31.3-dev.1...v5.32.0-dev.1) (2025-07-16)

### Features

* **Prime Video:** Add `Playback speed` patch ([#5444](https://github.com/ReVanced/revanced-patches/issues/5444)) ([f46dbcd](f46dbcd084))
2025-07-16 19:30:50 +00:00
Sujitha Wijewantha
f46dbcd084 feat(Prime Video): Add Playback speed patch (#5444) 2025-07-16 23:27:55 +04:00
github-actions[bot]
2136573cb6 chore: Sync translations (#5484) 2025-07-16 23:27:18 +04:00
MarcaD
86ec08993c refactor(YouTube - Settings): Back button/gesture closes search instead of exiting (#5439) 2025-07-16 23:26:20 +04:00
semantic-release-bot
44da5a71c5 chore: Release v5.31.3-dev.1 [skip ci]
## [5.31.3-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.31.2...v5.31.3-dev.1) (2025-07-16)

### Bug Fixes

* **YouTube - GmsCore support:** Fix search suggestions when logged out by using correct search provider ([#5483](https://github.com/ReVanced/revanced-patches/issues/5483)) ([e4e81b8](e4e81b89ea))
2025-07-16 19:16:27 +00:00
sm455
e4e81b89ea fix(YouTube - GmsCore support): Fix search suggestions when logged out by using correct search provider (#5483) 2025-07-16 23:13:22 +04:00
LisoUseInAIKyrios
165df659a1 chore(YouTube): Add string contexts 2025-07-16 12:02:47 +04:00
LisoUseInAIKyrios
bb87afe0f6 ci: Revert "Group all Dependabot update into one PR (#5336)"
This reverts commit e019f83232.
2025-07-16 11:54:40 +04:00
semantic-release-bot
ac5fb17937 chore: Release v5.31.2 [skip ci]
## [5.31.2](https://github.com/ReVanced/revanced-patches/compare/v5.31.1...v5.31.2) (2025-07-14)

### Bug Fixes

* **Spotify - Spoof client:** Fix login failing by spoofing login request in addition ([#5448](https://github.com/ReVanced/revanced-patches/issues/5448)) ([c972267](c972267cd8))
* **YouTube - Disable double tap actions:** Remove old incompatible targets ([294b2dc](294b2dce2e))
* **YouTube - Hide layout components:** Hide quick actions does not work ([#5423](https://github.com/ReVanced/revanced-patches/issues/5423)) ([cc6984e](cc6984e919))
* **YouTube - Hide layout components:** Show correct custom header logo if 'Hide YouTube Doodles' is enabled ([#5431](https://github.com/ReVanced/revanced-patches/issues/5431)) ([19bc5b6](19bc5b63c5))
* **YouTube - Settings:** Back button/gesture closes search instead of exiting ([#5418](https://github.com/ReVanced/revanced-patches/issues/5418)) ([f994264](f994264d9c))
2025-07-14 12:02:46 +00:00
oSumAtrIX
e88356b3c5 chore: Merge branch dev to main (#5428) 2025-07-14 13:59:59 +02:00
github-actions[bot]
dead9c2d94 chore: Sync translations (#5449)
Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2025-07-14 13:59:35 +02:00
semantic-release-bot
ca640b2839 chore: Release v5.31.2-dev.5 [skip ci]
## [5.31.2-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.31.2-dev.4...v5.31.2-dev.5) (2025-07-14)

### Bug Fixes

* **Spotify - Spoof client:** Fix login failing by spoofing login request in addition ([#5448](https://github.com/ReVanced/revanced-patches/issues/5448)) ([c972267](c972267cd8))
2025-07-14 11:58:39 +00:00
oSumAtrIX
c972267cd8 fix(Spotify - Spoof client): Fix login failing by spoofing login request in addition (#5448) 2025-07-14 13:55:37 +02:00
ILoveOpenSourceApplications
d0d2c13d16 refactor(YouTube): Sort and standardize strings (#5442) 2025-07-14 15:01:10 +04:00
semantic-release-bot
e7b4ab53cf chore: Release v5.31.2-dev.4 [skip ci]
## [5.31.2-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.31.2-dev.3...v5.31.2-dev.4) (2025-07-13)

### Bug Fixes

* **YouTube - Settings:** Back button/gesture closes search instead of exiting ([#5418](https://github.com/ReVanced/revanced-patches/issues/5418)) ([f994264](f994264d9c))
2025-07-13 10:59:17 +00:00
MarcaD
f994264d9c fix(YouTube - Settings): Back button/gesture closes search instead of exiting (#5418) 2025-07-13 14:56:30 +04:00
github-actions[bot]
eb61c1f5d1 chore: Sync translations (#5437) 2025-07-13 14:55:36 +04:00
semantic-release-bot
e578347277 chore: Release v5.31.2-dev.3 [skip ci]
## [5.31.2-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.31.2-dev.2...v5.31.2-dev.3) (2025-07-13)

### Bug Fixes

* **YouTube - Disable double tap actions:** Remove old incompatible targets ([294b2dc](294b2dce2e))
2025-07-13 06:18:39 +00:00
LisoUseInAIKyrios
294b2dce2e fix(YouTube - Disable double tap actions): Remove old incompatible targets 2025-07-13 10:15:16 +04:00
github-actions[bot]
aa37105ea3 chore: Sync translations (#5436) 2025-07-13 10:03:04 +04:00
semantic-release-bot
eb57a2697b chore: Release v5.31.2-dev.2 [skip ci]
## [5.31.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.31.2-dev.1...v5.31.2-dev.2) (2025-07-12)

### Bug Fixes

* **YouTube - Hide layout components:** Show correct custom header logo if 'Hide YouTube Doodles' is enabled ([#5431](https://github.com/ReVanced/revanced-patches/issues/5431)) ([19bc5b6](19bc5b63c5))
2025-07-12 14:37:52 +00:00
LisoUseInAIKyrios
19bc5b63c5 fix(YouTube - Hide layout components): Show correct custom header logo if 'Hide YouTube Doodles' is enabled (#5431) 2025-07-12 18:34:29 +04:00
180 changed files with 5662 additions and 2875 deletions

View File

@@ -1,26 +1,22 @@
version: 2
multi-ecosystem-groups:
dependency:
schedule:
interval: "weekly"
target-branch: dev
labels: [ ]
updates:
- package-ecosystem: github-actions
multi-ecosystem-group: "dependency"
labels: []
directory: /
patterns:
- "*"
target-branch: dev
schedule:
interval: monthly
- package-ecosystem: npm
multi-ecosystem-group: "dependency"
labels: []
directory: /
patterns:
- "*"
target-branch: dev
schedule:
interval: monthly
- package-ecosystem: gradle
multi-ecosystem-group: "dependency"
labels: []
directory: /
patterns:
- "*"
target-branch: dev
schedule:
interval: monthly

View File

@@ -1,3 +1,170 @@
# [5.33.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.9...v5.33.0-dev.10) (2025-08-04)
### Bug Fixes
* **YouTube - Video quality:** Fix wrong qualities sometimes shown in player button dialog ([7378ae3](https://github.com/ReVanced/revanced-patches/commit/7378ae3c5fc88f91bf5cd6db47c6cd170a8c5a4f))
# [5.33.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.8...v5.33.0-dev.9) (2025-08-04)
### Bug Fixes
* **YouTube - Force original audio:** Disable a/b feature flag that forces localized audio ([#5582](https://github.com/ReVanced/revanced-patches/issues/5582)) ([9fe13ee](https://github.com/ReVanced/revanced-patches/commit/9fe13ee1af104c009efd19b826adef375e48e191))
# [5.33.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.7...v5.33.0-dev.8) (2025-08-03)
### Bug Fixes
* **NFC Tools:** Remove broken patch that is no longer supported ([#5584](https://github.com/ReVanced/revanced-patches/issues/5584)) ([cd3a6be](https://github.com/ReVanced/revanced-patches/commit/cd3a6be75c6bd3cc33c0b17a044bd6147f27b5ce))
# [5.33.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.6...v5.33.0-dev.7) (2025-08-03)
### Features
* **YouTube:** Add player button to change video quality ([#5435](https://github.com/ReVanced/revanced-patches/issues/5435)) ([d5f51bf](https://github.com/ReVanced/revanced-patches/commit/d5f51bf400dd22626ff65d7563b6fde70d53fb25))
# [5.33.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.5...v5.33.0-dev.6) (2025-07-31)
### Bug Fixes
* **YouTube - Video quality:** Use 1080p enhanced bitrate for Premium users ([#5565](https://github.com/ReVanced/revanced-patches/issues/5565)) ([bd3ace0](https://github.com/ReVanced/revanced-patches/commit/bd3ace0bd04ccd0369adb49d63aa0cf986402346))
# [5.33.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.4...v5.33.0-dev.5) (2025-07-31)
### Bug Fixes
* **YouTube - Litho filter:** Correctly filter identifier of older YouTube targets ([bf29d69](https://github.com/ReVanced/revanced-patches/commit/bf29d6909e389819bad878ad3b94bbc90d823cc9))
# [5.33.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.3...v5.33.0-dev.4) (2025-07-30)
### Performance Improvements
* **YouTube:** Filter identifier callback only on root component creation ([#5558](https://github.com/ReVanced/revanced-patches/issues/5558)) ([ccac46e](https://github.com/ReVanced/revanced-patches/commit/ccac46eebc2e14b094454e37ef4461d48a62c53f))
# [5.33.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.2...v5.33.0-dev.3) (2025-07-30)
### Bug Fixes
* **YouTube - Playback speed:** Use old speed menu for player button if enabled ([1e8f436](https://github.com/ReVanced/revanced-patches/commit/1e8f4368e117f4b278c24709231cb32546e46dc0))
# [5.33.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.33.0-dev.1...v5.33.0-dev.2) (2025-07-29)
### Features
* **ORF ON:** Add `Remove root detection` patch ([#5551](https://github.com/ReVanced/revanced-patches/issues/5551)) ([6c6aa35](https://github.com/ReVanced/revanced-patches/commit/6c6aa35411a139dddc3a15dd757fbeded5d1a0a3))
# [5.33.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.32.0...v5.33.0-dev.1) (2025-07-28)
### Features
* **YouTube - Playback speed:** Add "Restore old playback speed menu" option ([#5552](https://github.com/ReVanced/revanced-patches/issues/5552)) ([b01f15b](https://github.com/ReVanced/revanced-patches/commit/b01f15b9acb0427aed99b0141ae271831b7936bf))
# [5.32.0](https://github.com/ReVanced/revanced-patches/compare/v5.31.2...v5.32.0) (2025-07-27)
### Bug Fixes
* **Messenger - Hide inbox ads:** Support the latest app version ([2959c02](https://github.com/ReVanced/revanced-patches/commit/2959c0214dfa703ee623ef1f89bded7f78c9d252))
* **YouTube - Hide layout components:** Fix "Hide ticket shelf" ([#5516](https://github.com/ReVanced/revanced-patches/issues/5516)) ([3b85c71](https://github.com/ReVanced/revanced-patches/commit/3b85c71433325fff49e01c77c7b9ff8ddd0a7068))
* **YouTube - GmsCore support:** Fix search suggestions when logged out by using correct search provider ([#5483](https://github.com/ReVanced/revanced-patches/issues/5483)) ([e86fdc8](https://github.com/ReVanced/revanced-patches/commit/e86fdc86b161a6077960b85149e83bacbac664e7))
### Features
* **Prime Video:** Add `Playback speed` patch ([#5444](https://github.com/ReVanced/revanced-patches/issues/5444)) ([22cf313](https://github.com/ReVanced/revanced-patches/commit/22cf313a7b99b69e17b9d488c514802043a5dc10))
* **YouTube - External downloads:** Improve the selection of the external downloader package ([#5504](https://github.com/ReVanced/revanced-patches/issues/5504)) ([5de9aa9](https://github.com/ReVanced/revanced-patches/commit/5de9aa9fad4f24186da045fb188f8718d6f63d7a))
* **YT Music:** Support latest versions ([#5524](https://github.com/ReVanced/revanced-patches/issues/5524)) ([551dcf0](https://github.com/ReVanced/revanced-patches/commit/551dcf01ca9c489a779196b49c8744727d79d6bc))
# [5.32.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.32.0-dev.4...v5.32.0-dev.5) (2025-07-26)
### Features
* **YT Music:** Support latest versions ([#5524](https://github.com/ReVanced/revanced-patches/issues/5524)) ([551dcf0](https://github.com/ReVanced/revanced-patches/commit/551dcf01ca9c489a779196b49c8744727d79d6bc))
# [5.32.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.32.0-dev.3...v5.32.0-dev.4) (2025-07-25)
### Bug Fixes
* **Messenger - Hide inbox ads:** Support the latest app version ([2959c02](https://github.com/ReVanced/revanced-patches/commit/2959c0214dfa703ee623ef1f89bded7f78c9d252))
# [5.32.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.32.0-dev.2...v5.32.0-dev.3) (2025-07-24)
### Features
* **YouTube - External downloads:** Improve the selection of the external downloader package ([#5504](https://github.com/ReVanced/revanced-patches/issues/5504)) ([5de9aa9](https://github.com/ReVanced/revanced-patches/commit/5de9aa9fad4f24186da045fb188f8718d6f63d7a))
# [5.32.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.32.0-dev.1...v5.32.0-dev.2) (2025-07-23)
### Bug Fixes
* **YouTube - Hide layout components:** Fix "Hide ticket shelf" ([#5516](https://github.com/ReVanced/revanced-patches/issues/5516)) ([3b85c71](https://github.com/ReVanced/revanced-patches/commit/3b85c71433325fff49e01c77c7b9ff8ddd0a7068))
# [5.32.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.31.3-dev.1...v5.32.0-dev.1) (2025-07-16)
### Features
* **Prime Video:** Add `Playback speed` patch ([#5444](https://github.com/ReVanced/revanced-patches/issues/5444)) ([22cf313](https://github.com/ReVanced/revanced-patches/commit/22cf313a7b99b69e17b9d488c514802043a5dc10))
## [5.31.3-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.31.2...v5.31.3-dev.1) (2025-07-16)
### Bug Fixes
* **YouTube - GmsCore support:** Fix search suggestions when logged out by using correct search provider ([#5483](https://github.com/ReVanced/revanced-patches/issues/5483)) ([e86fdc8](https://github.com/ReVanced/revanced-patches/commit/e86fdc86b161a6077960b85149e83bacbac664e7))
## [5.31.2](https://github.com/ReVanced/revanced-patches/compare/v5.31.1...v5.31.2) (2025-07-14)
### Bug Fixes
* **Spotify - Spoof client:** Fix login failing by spoofing login request in addition ([#5448](https://github.com/ReVanced/revanced-patches/issues/5448)) ([4e59ddc](https://github.com/ReVanced/revanced-patches/commit/4e59ddc62388d09f71b89593fc8b76933d9facea))
* **YouTube - Disable double tap actions:** Remove old incompatible targets ([857053e](https://github.com/ReVanced/revanced-patches/commit/857053e29b72ded10a84b0ac693fa107705342d9))
* **YouTube - Hide layout components:** Hide quick actions does not work ([#5423](https://github.com/ReVanced/revanced-patches/issues/5423)) ([9c66729](https://github.com/ReVanced/revanced-patches/commit/9c6672946d44001e106bdac9041e2d79ef3f6ab2))
* **YouTube - Hide layout components:** Show correct custom header logo if 'Hide YouTube Doodles' is enabled ([#5431](https://github.com/ReVanced/revanced-patches/issues/5431)) ([20cc141](https://github.com/ReVanced/revanced-patches/commit/20cc141e61f75de1a1749247c4f4aed167dee8ea))
* **YouTube - Settings:** Back button/gesture closes search instead of exiting ([#5418](https://github.com/ReVanced/revanced-patches/issues/5418)) ([134b278](https://github.com/ReVanced/revanced-patches/commit/134b278baa7b90d2c4b06200cabacabf55ebc055))
## [5.31.2-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.31.2-dev.4...v5.31.2-dev.5) (2025-07-14)
### Bug Fixes
* **Spotify - Spoof client:** Fix login failing by spoofing login request in addition ([#5448](https://github.com/ReVanced/revanced-patches/issues/5448)) ([4e59ddc](https://github.com/ReVanced/revanced-patches/commit/4e59ddc62388d09f71b89593fc8b76933d9facea))
## [5.31.2-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.31.2-dev.3...v5.31.2-dev.4) (2025-07-13)
### Bug Fixes
* **YouTube - Settings:** Back button/gesture closes search instead of exiting ([#5418](https://github.com/ReVanced/revanced-patches/issues/5418)) ([134b278](https://github.com/ReVanced/revanced-patches/commit/134b278baa7b90d2c4b06200cabacabf55ebc055))
## [5.31.2-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.31.2-dev.2...v5.31.2-dev.3) (2025-07-13)
### Bug Fixes
* **YouTube - Disable double tap actions:** Remove old incompatible targets ([857053e](https://github.com/ReVanced/revanced-patches/commit/857053e29b72ded10a84b0ac693fa107705342d9))
## [5.31.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.31.2-dev.1...v5.31.2-dev.2) (2025-07-12)
### Bug Fixes
* **YouTube - Hide layout components:** Show correct custom header logo if 'Hide YouTube Doodles' is enabled ([#5431](https://github.com/ReVanced/revanced-patches/issues/5431)) ([20cc141](https://github.com/ReVanced/revanced-patches/commit/20cc141e61f75de1a1749247c4f4aed167dee8ea))
## [5.31.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.31.1...v5.31.2-dev.1) (2025-07-12)

View File

@@ -0,0 +1,207 @@
package app.revanced.extension.primevideo.videoplayer;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.RectF;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
import java.util.Arrays;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import com.amazon.video.sdk.player.Player;
public class PlaybackSpeedPatch {
private static Player player;
private static final float[] SPEED_VALUES = {0.5f, 0.7f, 0.8f, 0.9f, 0.95f, 1.0f, 1.05f, 1.1f, 1.2f, 1.3f, 1.5f, 2.0f};
private static final String SPEED_BUTTON_TAG = "speed_overlay";
public static void setPlayer(Player playerInstance) {
player = playerInstance;
if (player != null) {
// Reset playback rate when switching between episodes to ensure correct display.
player.setPlaybackRate(1.0f);
}
}
public static void initializeSpeedOverlay(View userControlsView) {
try {
LinearLayout buttonContainer = Utils.getChildViewByResourceName(userControlsView, "ButtonContainerPlayerTop");
// If the speed overlay exists we should return early.
if (Utils.getChildView(buttonContainer, false, child ->
child instanceof ImageView && SPEED_BUTTON_TAG.equals(child.getTag())) != null) {
return;
}
ImageView speedButton = createSpeedButton(userControlsView.getContext());
speedButton.setOnClickListener(v -> changePlaybackSpeed(speedButton));
buttonContainer.addView(speedButton, 0);
} catch (IllegalArgumentException e) {
Logger.printException(() -> "initializeSpeedOverlay, no button container found", e);
} catch (Exception e) {
Logger.printException(() -> "initializeSpeedOverlay failure", e);
}
}
private static ImageView createSpeedButton(Context context) {
ImageView speedButton = new ImageView(context);
speedButton.setContentDescription("Playback Speed");
speedButton.setTag(SPEED_BUTTON_TAG);
speedButton.setClickable(true);
speedButton.setFocusable(true);
speedButton.setScaleType(ImageView.ScaleType.CENTER);
SpeedIconDrawable speedIcon = new SpeedIconDrawable();
speedButton.setImageDrawable(speedIcon);
int buttonSize = Utils.dipToPixels(48);
speedButton.setMinimumWidth(buttonSize);
speedButton.setMinimumHeight(buttonSize);
return speedButton;
}
private static String[] getSpeedOptions() {
String[] options = new String[SPEED_VALUES.length];
for (int i = 0; i < SPEED_VALUES.length; i++) {
options[i] = SPEED_VALUES[i] + "x";
}
return options;
}
private static void changePlaybackSpeed(ImageView imageView) {
if (player == null) {
Logger.printException(() -> "Player not available");
return;
}
try {
player.pause();
AlertDialog dialog = createSpeedPlaybackDialog(imageView);
dialog.setOnDismissListener(dialogInterface -> player.play());
dialog.show();
} catch (Exception e) {
Logger.printException(() -> "changePlaybackSpeed", e);
}
}
private static AlertDialog createSpeedPlaybackDialog(ImageView imageView) {
Context context = imageView.getContext();
int currentSelection = getCurrentSpeedSelection();
return new AlertDialog.Builder(context)
.setTitle("Select Playback Speed")
.setSingleChoiceItems(getSpeedOptions(), currentSelection,
PlaybackSpeedPatch::handleSpeedSelection)
.create();
}
private static int getCurrentSpeedSelection() {
try {
float currentRate = player.getPlaybackRate();
int index = Arrays.binarySearch(SPEED_VALUES, currentRate);
return Math.max(index, 0); // Use slowest speed if not found.
} catch (Exception e) {
Logger.printException(() -> "getCurrentSpeedSelection error getting current playback speed", e);
return 0;
}
}
private static void handleSpeedSelection(android.content.DialogInterface dialog, int selectedIndex) {
try {
float selectedSpeed = SPEED_VALUES[selectedIndex];
player.setPlaybackRate(selectedSpeed);
player.play();
} catch (Exception e) {
Logger.printException(() -> "handleSpeedSelection error setting playback speed", e);
} finally {
dialog.dismiss();
}
}
}
class SpeedIconDrawable extends Drawable {
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
@Override
public void draw(Canvas canvas) {
int w = getBounds().width();
int h = getBounds().height();
float centerX = w / 2f;
// Position gauge in lower portion.
float centerY = h * 0.7f;
float radius = Math.min(w, h) / 2f * 0.8f;
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(radius * 0.1f);
// Draw semicircle.
RectF oval = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
canvas.drawArc(oval, 180, 180, false, paint);
// Draw three tick marks.
paint.setStrokeWidth(radius * 0.06f);
for (int i = 0; i < 3; i++) {
float angle = 180 + (i * 45); // 180°, 225°, 270°.
float angleRad = (float) Math.toRadians(angle);
float startX = centerX + (radius * 0.8f) * (float) Math.cos(angleRad);
float startY = centerY + (radius * 0.8f) * (float) Math.sin(angleRad);
float endX = centerX + radius * (float) Math.cos(angleRad);
float endY = centerY + radius * (float) Math.sin(angleRad);
canvas.drawLine(startX, startY, endX, endY, paint);
}
// Draw needle.
paint.setStrokeWidth(radius * 0.08f);
float needleAngle = 200; // Slightly right of center.
float needleAngleRad = (float) Math.toRadians(needleAngle);
float needleEndX = centerX + (radius * 0.6f) * (float) Math.cos(needleAngleRad);
float needleEndY = centerY + (radius * 0.6f) * (float) Math.sin(needleAngleRad);
canvas.drawLine(centerX, centerY, needleEndX, needleEndY, paint);
// Center dot.
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(centerX, centerY, radius * 0.06f, paint);
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
paint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public int getIntrinsicWidth() {
return Utils.dipToPixels(32);
}
@Override
public int getIntrinsicHeight() {
return Utils.dipToPixels(32);
}
}

View File

@@ -4,4 +4,10 @@ public interface VideoPlayer {
long getCurrentPosition();
void seekTo(long positionMs);
void pause();
void play();
boolean isPlaying();
}

View File

@@ -0,0 +1,11 @@
package com.amazon.video.sdk.player;
public interface Player {
float getPlaybackRate();
void setPlaybackRate(float rate);
void play();
void pause();
}

View File

@@ -1438,6 +1438,38 @@ public class Utils {
);
}
/**
* Converts a percentage of the screen height to actual device pixels.
*
* @param percentage The percentage of the screen height (e.g., 30 for 30%).
* @return The device pixel value corresponding to the percentage of screen height.
*/
public static int percentageHeightToPixels(int percentage) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int) (metrics.heightPixels * (percentage / 100.0f));
}
/**
* Converts a percentage of the screen width to actual device pixels.
*
* @param percentage The percentage of the screen width (e.g., 30 for 30%).
* @return The device pixel value corresponding to the percentage of screen width.
*/
public static int percentageWidthToPixels(int percentage) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return (int) (metrics.widthPixels * (percentage / 100.0f));
}
/**
* Uses {@link #adjustColorBrightness(int, float)} depending if light or dark mode is active.
*/
@ColorInt
public static int adjustColorBrightness(@ColorInt int baseColor, float lightThemeFactor, float darkThemeFactor) {
return isDarkModeEnabled()
? adjustColorBrightness(baseColor, darkThemeFactor)
: adjustColorBrightness(baseColor, lightThemeFactor);
}
/**
* Adjusts the brightness of a color by lightening or darkening it based on the given factor.
* <p>

View File

@@ -1,7 +1,5 @@
package app.revanced.extension.shared.settings.preference;
import static app.revanced.extension.shared.Utils.dipToPixels;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
@@ -26,7 +24,7 @@ public class CustomDialogListPreference extends ListPreference {
/**
* Custom ArrayAdapter to handle checkmark visibility.
*/
private static class ListPreferenceArrayAdapter extends ArrayAdapter<CharSequence> {
public static class ListPreferenceArrayAdapter extends ArrayAdapter<CharSequence> {
private static class SubViewDataContainer {
ImageView checkIcon;
View placeholder;

View File

@@ -1,28 +1,15 @@
package app.revanced.extension.shared.settings.preference;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.Paint.Style;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;

View File

@@ -1,9 +1,12 @@
package app.revanced.extension.youtube.patches;
import android.graphics.drawable.Drawable;
import androidx.annotation.Nullable;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@@ -11,18 +14,21 @@ import app.revanced.extension.youtube.settings.Settings;
public class ChangeHeaderPatch {
public enum HeaderLogo {
DEFAULT(null),
REGULAR("ytWordmarkHeader"),
PREMIUM("ytPremiumWordmarkHeader"),
REVANCED("revanced_header_logo"),
REVANCED_MINIMAL("revanced_header_logo_minimal"),
CUSTOM("custom_header");
DEFAULT(null, null),
REGULAR("ytWordmarkHeader", "yt_ringo2_wordmark_header"),
PREMIUM("ytPremiumWordmarkHeader", "yt_ringo2_premium_wordmark_header"),
REVANCED("revanced_header_logo", "revanced_header_logo"),
REVANCED_MINIMAL("revanced_header_logo_minimal", "revanced_header_logo_minimal"),
CUSTOM("custom_header", "custom_header");
@Nullable
private final String resourceName;
private final String attributeName;
@Nullable
private final String drawableName;
HeaderLogo(@Nullable String resourceName) {
this.resourceName = resourceName;
HeaderLogo(@Nullable String attributeName, @Nullable String drawableName) {
this.attributeName = attributeName;
this.drawableName = drawableName;
}
/**
@@ -30,24 +36,66 @@ public class ChangeHeaderPatch {
*/
@Nullable
private Integer getAttributeId() {
if (resourceName == null) {
if (attributeName == null) {
return null;
}
final int identifier = Utils.getResourceIdentifier(resourceName, "attr");
// Identifier is zero if custom header setting was included in imported settings
// and a custom image was not included during patching.
return identifier == 0 ? null : identifier;
final int identifier = Utils.getResourceIdentifier(attributeName, "attr");
if (identifier == 0) {
// Identifier is zero if custom header setting was included in imported settings
// and a custom image was not included during patching.
Logger.printDebug(() -> "Could not find attribute: " + drawableName);
Settings.HEADER_LOGO.resetToDefault();
return null;
}
return identifier;
}
@Nullable
public Drawable getDrawable() {
if (drawableName == null) {
return null;
}
String drawableFullName = drawableName + (Utils.isDarkModeEnabled()
? "_dark"
: "_light");
final int identifier = Utils.getResourceIdentifier(drawableFullName, "drawable");
if (identifier == 0) {
Logger.printDebug(() -> "Could not find drawable: " + drawableFullName);
Settings.HEADER_LOGO.resetToDefault();
return null;
}
return Utils.getContext().getDrawable(identifier);
}
}
@Nullable
private static final Integer headerLogoResource = Settings.HEADER_LOGO.get().getAttributeId();
/**
* Injection point.
*/
public static int getHeaderAttributeId(int original) {
return Objects.requireNonNullElse(headerLogoResource, original);
return Objects.requireNonNullElse(Settings.HEADER_LOGO.get().getAttributeId(), original);
}
public static Drawable getDrawable(Drawable original) {
Drawable logo = Settings.HEADER_LOGO.get().getDrawable();
if (logo != null) {
return logo;
}
// TODO: If 'Hide Doodles' is enabled, this will force the regular logo regardless
// what account the user has. This can be improved the next time a Doodle is
// active and the attribute id is passed to this method so the correct
// regular/premium logo is returned.
logo = HeaderLogo.REGULAR.getDrawable();
if (logo != null) {
return logo;
}
// Should never happen.
Logger.printException(() -> "Could not find regular header logo resource");
return original;
}
}

View File

@@ -3,7 +3,7 @@ package app.revanced.extension.youtube.patches;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class DisableChapterSkipDoubleTapPatch {
public final class DisableDoubleTapActionsPatch {
/**
* Injection point.

View File

@@ -1,17 +1,15 @@
package app.revanced.extension.youtube.patches;
import static app.revanced.extension.youtube.settings.preference.ExternalDownloaderPreference.showDialogIfAppIsNotInstalled;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import androidx.annotation.NonNull;
import java.lang.ref.WeakReference;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.StringRef;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@@ -36,7 +34,7 @@ public final class DownloadsPatch {
*
* Appears to always be called from the main thread.
*/
public static boolean inAppDownloadButtonOnClick(@NonNull String videoId) {
public static boolean inAppDownloadButtonOnClick(String videoId) {
try {
if (!Settings.EXTERNAL_DOWNLOADER_ACTION_BUTTON.get()) {
return false;
@@ -48,6 +46,9 @@ public final class DownloadsPatch {
boolean isActivityContext = true;
if (context == null) {
// Utils context is the application context, and not an activity context.
//
// Edit: This check may no longer be needed since YT can now
// only be launched from the main Activity (embedded usage in other apps no longer works).
context = Utils.getContext();
isActivityContext = false;
}
@@ -64,8 +65,7 @@ public final class DownloadsPatch {
* @param isActivityContext If the context parameter is for an Activity. If this is false, then
* the downloader is opened as a new task (which forces YT to minimize).
*/
public static void launchExternalDownloader(@NonNull String videoId,
@NonNull Context context, boolean isActivityContext) {
public static void launchExternalDownloader(String videoId, Context context, boolean isActivityContext) {
try {
Objects.requireNonNull(videoId);
Logger.printDebug(() -> "Launching external downloader with context: " + context);
@@ -73,16 +73,8 @@ public final class DownloadsPatch {
// Trim string to avoid any accidental whitespace.
var downloaderPackageName = Settings.EXTERNAL_DOWNLOADER_PACKAGE_NAME.get().trim();
boolean packageEnabled = false;
try {
packageEnabled = context.getPackageManager().getApplicationInfo(downloaderPackageName, 0).enabled;
} catch (PackageManager.NameNotFoundException error) {
Logger.printDebug(() -> "External downloader could not be found: " + error);
}
// If the package is not installed, show the toast
if (!packageEnabled) {
Utils.showToastLong(StringRef.str("revanced_external_downloader_not_installed_warning", downloaderPackageName));
// If the package is not installed, show a dialog.
if (showDialogIfAppIsNotInstalled(context, downloaderPackageName)) {
return;
}

View File

@@ -24,6 +24,16 @@ public class ForceOriginalAudioPatch {
}
}
/**
* Injection point.
*/
public static boolean ignoreDefaultAudioStream(boolean original) {
if (Settings.FORCE_ORIGINAL_AUDIO.get()) {
return false;
}
return original;
}
/**
* Injection point.
*/
@@ -50,7 +60,6 @@ public class ForceOriginalAudioPatch {
return isOriginal;
} catch (Exception ex) {
Logger.printException(() -> "isDefaultAudioStream failure", ex);
return isDefault;
}
}

View File

@@ -16,7 +16,7 @@ import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.components.ReturnYouTubeDislikeFilterPatch;
import app.revanced.extension.youtube.patches.components.ReturnYouTubeDislikeFilter;
import app.revanced.extension.youtube.returnyoutubedislike.ReturnYouTubeDislike;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
@@ -55,7 +55,7 @@ public class ReturnYouTubeDislikePatch {
private static volatile ReturnYouTubeDislike lastLithoShortsVideoData;
/**
* Because litho Shorts spans are created offscreen after {@link ReturnYouTubeDislikeFilterPatch}
* Because litho Shorts spans are created offscreen after {@link ReturnYouTubeDislikeFilter}
* detects the video ids, but the current Short can arbitrarily reload the same span,
* then use the {@link #lastLithoShortsVideoData} if this value is greater than zero.
*/

View File

@@ -6,8 +6,6 @@ import android.app.Instrumentation;
import android.view.KeyEvent;
import android.view.View;
import androidx.annotation.Nullable;
import java.util.List;
import app.revanced.extension.shared.Logger;
@@ -155,10 +153,10 @@ public final class AdsFilter extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (matchedGroup == playerShoppingShelf) {
return contentIndex == 0 && playerShoppingShelfBuffer.check(protobufBufferArray).isFiltered();
return contentIndex == 0 && playerShoppingShelfBuffer.check(buffer).isFiltered();
}
if (exceptions.matches(path)) {

View File

@@ -1,7 +1,5 @@
package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.patches.playback.quality.AdvancedVideoQualityMenuPatch;
import app.revanced.extension.youtube.settings.Settings;
@@ -21,7 +19,7 @@ public final class AdvancedVideoQualityMenuFilter extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
isVideoQualityMenuVisible = true;

View File

@@ -1,7 +1,5 @@
package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -100,7 +98,7 @@ final class ButtonsFilter extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (matchedGroup == likeSubscribeGlow) {
return (path.startsWith(VIDEO_ACTION_BAR_PATH_PREFIX) || path.startsWith(COMPACT_CHANNEL_BAR_PATH_PREFIX))
@@ -117,7 +115,7 @@ final class ButtonsFilter extends Filter {
// Make sure the current path is the right one
// to avoid false positives.
return path.startsWith(VIDEO_ACTION_BAR_PATH)
&& bufferButtonsGroupList.check(protobufBufferArray).isFiltered();
&& bufferButtonsGroupList.check(buffer).isFiltered();
}
return true;

View File

@@ -1,7 +1,5 @@
package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
@@ -87,12 +85,12 @@ final class CommentsFilter extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (matchedGroup == chipBar) {
// Playlist sort button uses same components and must only filter if the player is opened.
return PlayerType.getCurrent().isMaximizedOrFullscreen()
&& aiCommentsSummary.check(protobufBufferArray).isFiltered();
&& aiCommentsSummary.check(buffer).isFiltered();
}
return true;

View File

@@ -3,7 +3,6 @@ package app.revanced.extension.youtube.patches.components;
import static app.revanced.extension.shared.StringRef.str;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Arrays;
import java.util.Collection;
@@ -146,7 +145,7 @@ final class CustomFilter extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
// All callbacks are custom filter groups.
CustomFilterGroup custom = (CustomFilterGroup) matchedGroup;
@@ -158,6 +157,6 @@ final class CustomFilter extends Filter {
return true; // No buffer filter, only path filtering.
}
return custom.bufferSearch.matches(protobufBufferArray);
return custom.bufferSearch.matches(buffer);
}
}

View File

@@ -1,7 +1,5 @@
package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.StringTrieSearch;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
@@ -105,7 +103,7 @@ final class DescriptionComponentsFilter extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (matchedGroup == aiGeneratedVideoSummarySection) {
@@ -116,11 +114,11 @@ final class DescriptionComponentsFilter extends Filter {
if (exceptions.matches(path)) return false;
if (matchedGroup == macroMarkersCarousel) {
return contentIndex == 0 && macroMarkersCarouselGroupList.check(protobufBufferArray).isFiltered();
return contentIndex == 0 && macroMarkersCarouselGroupList.check(buffer).isFiltered();
}
if (matchedGroup == horizontalShelf) {
return cellVideoAttribute.check(protobufBufferArray).isFiltered();
return cellVideoAttribute.check(buffer).isFiltered();
}
return true;

View File

@@ -1,7 +1,5 @@
package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -42,6 +40,7 @@ abstract class Filter {
/**
* Adds callbacks to {@link #isFiltered(String, String, byte[], StringFilterGroup, FilterContentType, int)}
* if any of the groups are found.
* <p>
*/
protected final void addIdentifierCallbacks(StringFilterGroup... groups) {
identifierCallbacks.addAll(Arrays.asList(groups));
@@ -68,7 +67,7 @@ abstract class Filter {
* @param contentIndex Matched index of the identifier or path.
* @return True if the litho component should be filtered out.
*/
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
return true;
}

View File

@@ -3,9 +3,9 @@ package app.revanced.extension.youtube.patches.components;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public final class HideInfoCardsFilterPatch extends Filter {
public final class HideInfoCardsFilter extends Filter {
public HideInfoCardsFilterPatch() {
public HideInfoCardsFilter() {
addIdentifierCallbacks(
new StringFilterGroup(
Settings.HIDE_INFO_CARDS,

View File

@@ -554,7 +554,7 @@ final class KeywordContentFilter extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (contentIndex != 0 && matchedGroup == startsWithFilter) {
return false;
@@ -574,7 +574,7 @@ final class KeywordContentFilter extends Filter {
}
MutableReference<String> matchRef = new MutableReference<>();
if (bufferSearch.matches(protobufBufferArray, matchRef)) {
if (bufferSearch.matches(buffer, matchRef)) {
updateStats(true, matchRef.value);
return true;
}

View File

@@ -4,12 +4,14 @@ import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButt
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.StringTrieSearch;
import app.revanced.extension.youtube.patches.ChangeHeaderPatch;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.NavigationBar;
import app.revanced.extension.youtube.shared.PlayerType;
@@ -30,7 +32,7 @@ public final class LayoutComponentsFilter extends Filter {
);
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup inFeedSurvey;
private final StringFilterGroup surveys;
private final StringFilterGroup notifyMe;
private final StringFilterGroup singleItemInformationPanel;
private final StringFilterGroup expandableMetadata;
@@ -108,8 +110,8 @@ public final class LayoutComponentsFilter extends Filter {
"chip_bar"
);
inFeedSurvey = new StringFilterGroup(
Settings.HIDE_FEED_SURVEY,
surveys = new StringFilterGroup(
Settings.HIDE_SURVEYS,
"in_feed_survey",
"slimline_survey",
"feed_nudge"
@@ -264,7 +266,7 @@ public final class LayoutComponentsFilter extends Filter {
ticketShelf = new ByteArrayFilterGroup(
Settings.HIDE_TICKET_SHELF,
"ticket.eml"
"ticket_item.eml"
);
addPathCallbacks(
@@ -284,7 +286,6 @@ public final class LayoutComponentsFilter extends Filter {
forYouShelf,
horizontalShelves,
imageShelf,
inFeedSurvey,
infoPanel,
latestPosts,
medicalPanel,
@@ -296,13 +297,14 @@ public final class LayoutComponentsFilter extends Filter {
singleItemInformationPanel,
subscribersCommunityGuidelines,
subscriptionsChipBar,
surveys,
timedReactions,
videoRecommendationLabels
);
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
// This identifier is used not only in players but also in search results:
// https://github.com/ReVanced/revanced-patches/issues/3245
@@ -315,12 +317,12 @@ public final class LayoutComponentsFilter extends Filter {
// The groups are excluded from the filter due to the exceptions list below.
// Filter them separately here.
if (matchedGroup == notifyMe || matchedGroup == inFeedSurvey || matchedGroup == expandableMetadata) {
if (matchedGroup == notifyMe || matchedGroup == surveys || matchedGroup == expandableMetadata) {
return true;
}
if (matchedGroup == channelProfile) {
return channelProfileBuffer.check(protobufBufferArray).isFiltered();
return channelProfileBuffer.check(buffer).isFiltered();
}
if (exceptions.matches(path)) return false; // Exceptions are not filtered.
@@ -329,11 +331,11 @@ public final class LayoutComponentsFilter extends Filter {
return compactChannelBarInnerButton.check(path).isFiltered()
// The filter may be broad, but in the context of a compactChannelBarInnerButton,
// it's safe to assume that the button is the only thing that should be hidden.
&& joinMembershipButton.check(protobufBufferArray).isFiltered();
&& joinMembershipButton.check(buffer).isFiltered();
}
if (matchedGroup == horizontalShelves) {
return contentIndex == 0 && (hideShelves() || ticketShelf.check(protobufBufferArray).isFiltered());
return contentIndex == 0 && (hideShelves() || ticketShelf.check(buffer).isFiltered());
}
if (matchedGroup == chipBar) {
@@ -437,13 +439,11 @@ public final class LayoutComponentsFilter extends Filter {
/**
* Injection point.
*/
@Nullable
public static Drawable hideYoodles(Drawable animatedYoodle) {
if (HIDE_DOODLES_ENABLED) {
return null;
}
return animatedYoodle;
public static void setDoodleDrawable(ImageView imageView, Drawable original) {
Drawable replacement = HIDE_DOODLES_ENABLED
? ChangeHeaderPatch.getDrawable(original)
: original;
imageView.setImageDrawable(replacement);
}
private static final boolean HIDE_SHOW_MORE_BUTTON_ENABLED = Settings.HIDE_SHOW_MORE_BUTTON.get();

View File

@@ -17,29 +17,28 @@ public final class LithoFilterPatch {
* Simple wrapper to pass the litho parameters through the prefix search.
*/
private static final class LithoFilterParameters {
@Nullable
final String identifier;
final String path;
final byte[] protoBuffer;
final byte[] buffer;
LithoFilterParameters(@Nullable String lithoIdentifier, String lithoPath, byte[] protoBuffer) {
LithoFilterParameters(String lithoIdentifier, String lithoPath, byte[] buffer) {
this.identifier = lithoIdentifier;
this.path = lithoPath;
this.protoBuffer = protoBuffer;
this.buffer = buffer;
}
@NonNull
@Override
public String toString() {
// Estimate the percentage of the buffer that are Strings.
StringBuilder builder = new StringBuilder(Math.max(100, protoBuffer.length / 2));
StringBuilder builder = new StringBuilder(Math.max(100, buffer.length / 2));
builder.append( "ID: ");
builder.append(identifier);
builder.append(" Path: ");
builder.append(path);
if (Settings.DEBUG_PROTOBUFFER.get()) {
builder.append(" BufferStrings: ");
findAsciiStrings(builder, protoBuffer);
findAsciiStrings(builder, buffer);
}
return builder.toString();
@@ -128,21 +127,21 @@ public final class LithoFilterPatch {
private static void filterUsingCallbacks(StringTrieSearch pathSearchTree,
Filter filter, List<StringFilterGroup> groups,
Filter.FilterContentType type) {
String filterSimpleName = filter.getClass().getSimpleName();
for (StringFilterGroup group : groups) {
if (!group.includeInSearch()) {
continue;
}
for (String pattern : group.filters) {
String filterSimpleName = filter.getClass().getSimpleName();
pathSearchTree.addPattern(pattern, (textSearched, matchedStartIndex,
matchedLength, callbackParameter) -> {
if (!group.isEnabled()) return false;
LithoFilterParameters parameters = (LithoFilterParameters) callbackParameter;
final boolean isFiltered = filter.isFiltered(parameters.identifier,
parameters.path, parameters.protoBuffer, group, type, matchedStartIndex);
parameters.path, parameters.buffer, group, type, matchedStartIndex);
if (isFiltered && BaseSettings.DEBUG.get()) {
if (type == Filter.FilterContentType.IDENTIFIER) {
@@ -163,6 +162,7 @@ public final class LithoFilterPatch {
/**
* Injection point. Called off the main thread.
* Targets 20.22+
*/
public static void setProtoBuffer(byte[] buffer) {
// Set the buffer to a thread local. The buffer will remain in memory, even after the call to #filter completes.
@@ -193,9 +193,9 @@ public final class LithoFilterPatch {
/**
* Injection point.
*/
public static boolean shouldFilter(@Nullable String lithoIdentifier, StringBuilder pathBuilder) {
public static boolean isFiltered(String lithoIdentifier, StringBuilder pathBuilder) {
try {
if (pathBuilder.length() == 0) {
if (lithoIdentifier.isEmpty() && pathBuilder.length() == 0) {
return false;
}
@@ -210,7 +210,7 @@ public final class LithoFilterPatch {
lithoIdentifier, pathBuilder.toString(), buffer);
Logger.printDebug(() -> "Searching " + parameter);
if (parameter.identifier != null && identifierSearchTree.matches(parameter.identifier, parameter)) {
if (identifierSearchTree.matches(parameter.identifier, parameter)) {
return true;
}
@@ -218,7 +218,7 @@ public final class LithoFilterPatch {
return true;
}
} catch (Exception ex) {
Logger.printException(() -> "Litho filter failure", ex);
Logger.printException(() -> "isFiltered failure", ex);
}
return false;

View File

@@ -0,0 +1,49 @@
package app.revanced.extension.youtube.patches.components;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.Settings;
/**
* Abuse LithoFilter for {@link CustomPlaybackSpeedPatch}.
*/
public final class PlaybackSpeedMenuFilter extends Filter {
/**
* Old litho based speed selection menu.
*/
public static volatile boolean isOldPlaybackSpeedMenuVisible;
/**
* 0.05x speed selection menu.
*/
public static volatile boolean isPlaybackRateSelectorMenuVisible;
private final StringFilterGroup oldPlaybackMenuGroup;
public PlaybackSpeedMenuFilter() {
// 0.05x litho speed menu.
var playbackRateSelectorGroup = new StringFilterGroup(
Settings.CUSTOM_SPEED_MENU,
"playback_rate_selector_menu_sheet.eml-js"
);
// Old litho based speed menu.
oldPlaybackMenuGroup = new StringFilterGroup(
Settings.CUSTOM_SPEED_MENU,
"playback_speed_sheet_content.eml-js");
addPathCallbacks(playbackRateSelectorGroup, oldPlaybackMenuGroup);
}
@Override
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (matchedGroup == oldPlaybackMenuGroup) {
isOldPlaybackSpeedMenuVisible = true;
} else {
isPlaybackRateSelectorMenuVisible = true;
}
return false;
}
}

View File

@@ -1,35 +0,0 @@
package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.Settings;
/**
* Abuse LithoFilter for {@link CustomPlaybackSpeedPatch}.
*/
public final class PlaybackSpeedMenuFilterPatch extends Filter {
/**
* 0.05x speed selection menu.
*/
public static volatile boolean isPlaybackRateSelectorMenuVisible;
public PlaybackSpeedMenuFilterPatch() {
// 0.05x litho speed menu.
var playbackRateSelectorGroup = new StringFilterGroup(
Settings.CUSTOM_SPEED_MENU,
"playback_rate_selector_menu_sheet.eml-js"
);
addPathCallbacks(playbackRateSelectorGroup);
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
isPlaybackRateSelectorMenuVisible = true;
return false;
}
}

View File

@@ -1,7 +1,5 @@
package app.revanced.extension.youtube.patches.components;
import androidx.annotation.Nullable;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
import app.revanced.extension.youtube.settings.Settings;
@@ -96,7 +94,7 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (matchedGroup == videoQualityMenuFooter) {
return true;
@@ -107,10 +105,10 @@ public class PlayerFlyoutMenuItemsFilter extends Filter {
}
// Shorts also use this player flyout panel
if (PlayerType.getCurrent().isNoneOrHidden() || exception.check(protobufBufferArray).isFiltered()) {
if (PlayerType.getCurrent().isNoneOrHidden() || exception.check(buffer).isFiltered()) {
return false;
}
return flyoutFilterGroupList.check(protobufBufferArray).isFiltered();
return flyoutFilterGroupList.check(buffer).isFiltered();
}
}

View File

@@ -26,7 +26,7 @@ import app.revanced.extension.youtube.TrieSearch;
*
* Once a way to asynchronously update litho text is found, this strategy will no longer be needed.
*/
public final class ReturnYouTubeDislikeFilterPatch extends Filter {
public final class ReturnYouTubeDislikeFilter extends Filter {
/**
* Last unique video id's loaded. Value is ignored and Map is treated as a Set.
@@ -67,7 +67,7 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
private final ByteArrayFilterGroupList videoIdFilterGroup = new ByteArrayFilterGroupList();
public ReturnYouTubeDislikeFilterPatch() {
public ReturnYouTubeDislikeFilter() {
// When a new Short is opened, the like buttons always seem to load before the dislike.
// But if swiping back to a previous video and liking/disliking, then only that single button reloads.
// So must check for both buttons.
@@ -84,15 +84,15 @@ public final class ReturnYouTubeDislikeFilterPatch extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (!Settings.RYD_ENABLED.get() || !Settings.RYD_SHORTS.get()) {
return false;
}
FilterGroup.FilterGroupResult result = videoIdFilterGroup.check(protobufBufferArray);
FilterGroup.FilterGroupResult result = videoIdFilterGroup.check(buffer);
if (result.isFiltered()) {
String matchedVideoId = findVideoId(protobufBufferArray);
String matchedVideoId = findVideoId(buffer);
// Matched video will be null if in incognito mode.
// Must pass a null id to correctly clear out the current video data.
// Otherwise if a Short is opened in non-incognito, then incognito is enabled and another Short is opened,

View File

@@ -4,8 +4,6 @@ import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButt
import android.view.View;
import androidx.annotation.Nullable;
import com.google.android.libraries.youtube.rendering.ui.pivotbar.PivotBar;
import java.lang.ref.WeakReference;
@@ -13,7 +11,6 @@ import java.util.Arrays;
import java.util.List;
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.NavigationBar;
import app.revanced.extension.youtube.shared.PlayerType;
@@ -321,7 +318,7 @@ public final class ShortsFilter extends Filter {
}
@Override
boolean isFiltered(@Nullable String identifier, String path, byte[] protobufBufferArray,
boolean isFiltered(String identifier, String path, byte[] buffer,
StringFilterGroup matchedGroup, FilterContentType contentType, int contentIndex) {
if (contentType == FilterContentType.PATH) {
if (matchedGroup == subscribeButton || matchedGroup == joinButton || matchedGroup == paidPromotionButton) {
@@ -330,22 +327,22 @@ public final class ShortsFilter extends Filter {
}
if (matchedGroup == useSoundButton) {
return useSoundButtonBuffer.check(protobufBufferArray).isFiltered();
return useSoundButtonBuffer.check(buffer).isFiltered();
}
if (matchedGroup == useTemplateButton) {
return useTemplateButtonBuffer.check(protobufBufferArray).isFiltered();
return useTemplateButtonBuffer.check(buffer).isFiltered();
}
if (matchedGroup == shortsCompactFeedVideo) {
return shouldHideShortsFeedItems() && shortsCompactFeedVideoBuffer.check(protobufBufferArray).isFiltered();
return shouldHideShortsFeedItems() && shortsCompactFeedVideoBuffer.check(buffer).isFiltered();
}
// Video action buttons (comment, share, remix) have the same path.
// Like and dislike are separate path filters and don't require buffer searching.
if (matchedGroup == shortsActionBar) {
return videoActionButton.check(path).isFiltered()
&& videoActionButtonBuffer.check(protobufBufferArray).isFiltered();
&& videoActionButtonBuffer.check(buffer).isFiltered();
}
if (matchedGroup == suggestedAction) {
@@ -356,7 +353,7 @@ public final class ShortsFilter extends Filter {
return true;
}
return suggestedActionsBuffer.check(protobufBufferArray).isFiltered();
return suggestedActionsBuffer.check(buffer).isFiltered();
}
return true;

View File

@@ -18,7 +18,7 @@ import app.revanced.extension.youtube.settings.Settings;
public final class AdvancedVideoQualityMenuPatch {
/**
* Injection point.
* Injection point. Regular videos.
*/
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
@@ -61,22 +61,12 @@ public final class AdvancedVideoQualityMenuPatch {
});
}
/**
* Injection point.
*
* Used to force the creation of the advanced menu item for the Shorts quality flyout.
*/
public static boolean forceAdvancedVideoQualityMenuCreation(boolean original) {
return Settings.ADVANCED_VIDEO_QUALITY_MENU.get() || original;
}
/**
* Injection point.
*
* Shorts video quality flyout.
*/
public static void showAdvancedVideoQualityMenu(ListView listView) {
public static void addVideoQualityListMenuListener(ListView listView) {
if (!Settings.ADVANCED_VIDEO_QUALITY_MENU.get()) return;
listView.setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
@@ -91,7 +81,6 @@ public final class AdvancedVideoQualityMenuPatch {
listView.setSoundEffectsEnabled(false);
final var qualityItemMenuPosition = 4;
listView.performItemClick(null, qualityItemMenuPosition, 0);
} catch (Exception ex) {
Logger.printException(() -> "showAdvancedVideoQualityMenu failure", ex);
}
@@ -102,4 +91,13 @@ public final class AdvancedVideoQualityMenuPatch {
}
});
}
/**
* Injection point.
*
* Used to force the creation of the advanced menu item for the Shorts quality flyout.
*/
public static boolean forceAdvancedVideoQualityMenuCreation(boolean original) {
return Settings.ADVANCED_VIDEO_QUALITY_MENU.get() || original;
}
}

View File

@@ -5,10 +5,9 @@ import static app.revanced.extension.shared.Utils.NetworkType;
import androidx.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import com.google.android.libraries.youtube.innertube.model.media.VideoQuality;
import java.util.Arrays;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
@@ -17,58 +16,112 @@ import app.revanced.extension.shared.settings.IntegerSetting;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.ShortsPlayerState;
import app.revanced.extension.youtube.videoplayer.VideoQualityDialogButton;
@SuppressWarnings("unused")
public class RememberVideoQualityPatch {
private static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
/**
* Interface to use obfuscated methods.
*/
public interface VideoQualityMenuInterface {
void patch_setQuality(VideoQuality quality);
}
/**
* Video resolution of the automatic quality option..
*/
public static final int AUTOMATIC_VIDEO_QUALITY_VALUE = -2;
/**
* All quality names are the same for all languages.
* VideoQuality also has a resolution enum that can be used if needed.
*/
public static final String VIDEO_QUALITY_1080P_PREMIUM_NAME = "1080p Premium";
private static final IntegerSetting videoQualityWifi = Settings.VIDEO_QUALITY_DEFAULT_WIFI;
private static final IntegerSetting videoQualityMobile = Settings.VIDEO_QUALITY_DEFAULT_MOBILE;
private static final IntegerSetting shortsQualityWifi = Settings.SHORTS_QUALITY_DEFAULT_WIFI;
private static final IntegerSetting shortsQualityMobile = Settings.SHORTS_QUALITY_DEFAULT_MOBILE;
private static boolean qualityNeedsUpdating;
/**
* If the user selected a new quality from the flyout menu,
* and {@link Settings#REMEMBER_VIDEO_QUALITY_LAST_SELECTED} is enabled.
*/
private static boolean userChangedDefaultQuality;
/**
* Index of the video quality chosen by the user from the flyout menu.
*/
private static int userSelectedQualityIndex;
/**
* The available qualities of the current video in human readable form: [1080, 720, 480]
* The available qualities of the current video.
*/
@Nullable
private static List<Integer> videoQualities;
private static VideoQuality[] currentQualities;
private static boolean shouldRememberVideoQuality() {
BooleanSetting preference = ShortsPlayerState.isOpen() ?
Settings.REMEMBER_SHORTS_QUALITY_LAST_SELECTED
/**
* The current quality of the video playing.
* This is always the actual quality even if Automatic quality is active.
*/
@Nullable
private static VideoQuality currentQuality;
/**
* The current VideoQualityMenuInterface, set during setVideoQuality.
*/
@Nullable
private static VideoQualityMenuInterface currentMenuInterface;
@Nullable
public static VideoQuality[] getCurrentQualities() {
return currentQualities;
}
@Nullable
public static VideoQuality getCurrentQuality() {
return currentQuality;
}
@Nullable
public static VideoQualityMenuInterface getCurrentMenuInterface() {
return currentMenuInterface;
}
public static boolean shouldRememberVideoQuality() {
BooleanSetting preference = ShortsPlayerState.isOpen()
? Settings.REMEMBER_SHORTS_QUALITY_LAST_SELECTED
: Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED;
return preference.get();
}
private static void changeDefaultQuality(int defaultQuality) {
public static int getDefaultQualityResolution() {
final boolean isShorts = ShortsPlayerState.isOpen();
IntegerSetting preference = Utils.getNetworkType() == NetworkType.MOBILE
? (isShorts ? shortsQualityMobile : videoQualityMobile)
: (isShorts ? shortsQualityWifi : videoQualityWifi);
return preference.get();
}
public static void saveDefaultQuality(int qualityResolution) {
final boolean shortPlayerOpen = ShortsPlayerState.isOpen();
String networkTypeMessage;
boolean useShortsPreference = ShortsPlayerState.isOpen();
IntegerSetting qualitySetting;
if (Utils.getNetworkType() == NetworkType.MOBILE) {
if (useShortsPreference) shortsQualityMobile.save(defaultQuality);
else videoQualityMobile.save(defaultQuality);
networkTypeMessage = str("revanced_remember_video_quality_mobile");
qualitySetting = shortPlayerOpen ? shortsQualityMobile : videoQualityMobile;
} else {
if (useShortsPreference) shortsQualityWifi.save(defaultQuality);
else videoQualityWifi.save(defaultQuality);
networkTypeMessage = str("revanced_remember_video_quality_wifi");
qualitySetting = shortPlayerOpen ? shortsQualityWifi : videoQualityWifi;
}
if (Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED_TOAST.get())
if (qualitySetting.get() == qualityResolution) {
// User clicked the same video quality as the current video,
// or changed between 1080p Premium and non-Premium.
return;
}
qualitySetting.save(qualityResolution);
if (Settings.REMEMBER_VIDEO_QUALITY_LAST_SELECTED_TOAST.get()) {
String qualityLabel = qualityResolution + "p";
Utils.showToastShort(str(
useShortsPreference ? "revanced_remember_video_quality_toast_shorts" : "revanced_remember_video_quality_toast",
networkTypeMessage, (defaultQuality + "p")
));
shortPlayerOpen
? "revanced_remember_video_quality_toast_shorts"
: "revanced_remember_video_quality_toast",
networkTypeMessage,
qualityLabel)
);
}
}
/**
@@ -77,109 +130,125 @@ public class RememberVideoQualityPatch {
* @param qualities Video qualities available, ordered from largest to smallest, with index 0 being the 'automatic' value of -2
* @param originalQualityIndex quality index to use, as chosen by YouTube
*/
public static int setVideoQuality(Object[] qualities, final int originalQualityIndex, Object qInterface, String qIndexMethod) {
public static int setVideoQuality(VideoQuality[] qualities, VideoQualityMenuInterface menu, int originalQualityIndex) {
try {
boolean useShortsPreference = ShortsPlayerState.isOpen();
final int preferredQuality = Utils.getNetworkType() == NetworkType.MOBILE
? (useShortsPreference ? shortsQualityMobile : videoQualityMobile).get()
: (useShortsPreference ? shortsQualityWifi : videoQualityWifi).get();
Utils.verifyOnMainThread();
currentMenuInterface = menu;
if (!userChangedDefaultQuality && preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE) {
final boolean availableQualitiesChanged = (currentQualities == null)
|| !Arrays.equals(currentQualities, qualities);
if (availableQualitiesChanged) {
currentQualities = qualities;
Logger.printDebug(() -> "VideoQualities: " + Arrays.toString(currentQualities));
}
VideoQuality updatedCurrentQuality = qualities[originalQualityIndex];
if (updatedCurrentQuality.patch_getResolution() != AUTOMATIC_VIDEO_QUALITY_VALUE &&
(currentQuality == null
|| !currentQuality.patch_getQualityName().equals(updatedCurrentQuality.patch_getQualityName()))) {
currentQuality = updatedCurrentQuality;
Logger.printDebug(() -> "Current quality changed to: " + updatedCurrentQuality);
VideoQualityDialogButton.updateButtonIcon(updatedCurrentQuality);
}
// After changing videos the qualities can initially be for the prior video.
// If the qualities have changed and the default is not auto then an update is needed.
final int preferredQuality = getDefaultQualityResolution();
if (preferredQuality == AUTOMATIC_VIDEO_QUALITY_VALUE || !availableQualitiesChanged) {
return originalQualityIndex; // Nothing to do.
}
if (videoQualities == null || videoQualities.size() != qualities.length) {
videoQualities = new ArrayList<>(qualities.length);
for (Object streamQuality : qualities) {
for (Field field : streamQuality.getClass().getFields()) {
if (field.getType().isAssignableFrom(Integer.TYPE)
&& field.getName().length() <= 2) {
videoQualities.add(field.getInt(streamQuality));
}
}
}
// After changing videos the qualities can initially be for the prior video.
// So if the qualities have changed an update is needed.
qualityNeedsUpdating = true;
Logger.printDebug(() -> "VideoQualities: " + videoQualities);
}
if (userChangedDefaultQuality) {
userChangedDefaultQuality = false;
final int quality = videoQualities.get(userSelectedQualityIndex);
Logger.printDebug(() -> "User changed default quality to: " + quality);
changeDefaultQuality(quality);
return userSelectedQualityIndex;
}
if (!qualityNeedsUpdating) {
return originalQualityIndex;
}
qualityNeedsUpdating = false;
// Find the highest quality that is equal to or less than the preferred.
int qualityToUse = videoQualities.get(0); // first element is automatic mode
int qualityIndexToUse = 0;
int i = 0;
for (Integer quality : videoQualities) {
if (quality <= preferredQuality && qualityToUse < quality) {
qualityToUse = quality;
qualityIndexToUse = i;
for (VideoQuality quality : qualities) {
final int qualityResolution = quality.patch_getResolution();
if (qualityResolution != AUTOMATIC_VIDEO_QUALITY_VALUE && qualityResolution <= preferredQuality) {
final boolean qualityNeedsChange = (i != originalQualityIndex);
Logger.printDebug(() -> qualityNeedsChange
? "Changing video quality from: " + updatedCurrentQuality + " to: " + quality
: "Video is already the preferred quality: " + quality
);
// On first load of a new regular video, if the video is already the
// desired quality then the quality flyout will show 'Auto' (ie: Auto (720p)).
//
// To prevent user confusion, set the video index even if the
// quality is already correct so the UI picker will not display "Auto".
//
// Only change Shorts quality if the quality actually needs to change,
// because the "auto" option is not shown in the flyout
// and setting the same quality again can cause the Short to restart.
if (qualityNeedsChange || !ShortsPlayerState.isOpen()) {
menu.patch_setQuality(qualities[i]);
return i;
}
return originalQualityIndex;
}
i++;
}
// If the desired quality index is equal to the original index,
// then the video is already set to the desired default quality.
final int qualityToUseFinal = qualityToUse;
if (qualityIndexToUse == originalQualityIndex) {
// On first load of a new video, if the UI video quality flyout menu
// is not updated then it will still show 'Auto' (ie: Auto (480p)),
// even though it's already set to the desired resolution.
//
// To prevent confusion, set the video index anyways (even if it matches the existing index)
// as that will force the UI picker to not display "Auto".
Logger.printDebug(() -> "Video is already preferred quality: " + qualityToUseFinal);
} else {
Logger.printDebug(() -> "Changing video quality from: "
+ videoQualities.get(originalQualityIndex) + " to: " + qualityToUseFinal);
}
Method m = qInterface.getClass().getMethod(qIndexMethod, Integer.TYPE);
m.invoke(qInterface, qualityToUse);
return qualityIndexToUse;
} catch (Exception ex) {
Logger.printException(() -> "Failed to set quality", ex);
return originalQualityIndex;
Logger.printException(() -> "setVideoQuality failure", ex);
}
return originalQualityIndex;
}
/**
* Injection point.
* @param userSelectedQualityIndex Element index of {@link #currentQualities}.
*/
public static void userChangedShortsQuality(int userSelectedQualityIndex) {
try {
if (shouldRememberVideoQuality()) {
if (currentQualities == null) {
Logger.printDebug(() -> "Cannot save default quality, qualities is null");
return;
}
VideoQuality quality = currentQualities[userSelectedQualityIndex];
saveDefaultQuality(quality.patch_getResolution());
}
} catch (Exception ex) {
Logger.printException(() -> "userChangedShortsQuality failure", ex);
}
}
/**
* Injection point. Old quality menu.
* Injection point. Regular videos.
* @param videoResolution Human readable resolution: 480, 720, 1080.
*/
public static void userChangedQuality(int selectedQualityIndex) {
public static void userChangedQuality(int videoResolution) {
Utils.verifyOnMainThread();
if (shouldRememberVideoQuality()) {
userSelectedQualityIndex = selectedQualityIndex;
userChangedDefaultQuality = true;
saveDefaultQuality(videoResolution);
}
}
/**
* Injection point. New quality menu.
*/
public static void userChangedQualityInNewFlyout(int selectedQuality) {
if (!shouldRememberVideoQuality()) return;
changeDefaultQuality(selectedQuality); // Quality is human readable resolution (ie: 1080).
}
/**
* Injection point.
*/
public static void newVideoStarted(VideoInformation.PlaybackController ignoredPlayerController) {
Utils.verifyOnMainThread();
Logger.printDebug(() -> "newVideoStarted");
qualityNeedsUpdating = true;
videoQualities = null;
currentQualities = null;
currentQuality = null;
currentMenuInterface = null;
// Hide the quality button until playback starts and the qualities are available.
VideoQualityDialogButton.updateButtonIcon(null);
}
/**
* Injection point. Fixes bad data used by YouTube.
*/
public static int fixVideoQualityResolution(String name, int quality) {
final int correctQuality = 480;
if (name.equals("480p") && quality != correctQuality) {
return correctQuality;
}
return quality;
}
}

View File

@@ -23,7 +23,6 @@ import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -42,7 +41,7 @@ import java.util.function.Function;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.patches.components.PlaybackSpeedMenuFilterPatch;
import app.revanced.extension.youtube.patches.components.PlaybackSpeedMenuFilter;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.shared.PlayerType;
import kotlin.Unit;
@@ -80,6 +79,16 @@ public class CustomPlaybackSpeedPatch {
*/
public static final float[] customPlaybackSpeeds;
/**
* Minimum and maximum custom playback speeds of {@link #customPlaybackSpeeds}.
*/
private static final float customPlaybackSpeedsMin, customPlaybackSpeedsMax;
/**
* The last time the old playback menu was forcefully called.
*/
private static volatile long lastTimeOldPlaybackMenuInvoked;
/**
* Formats speeds to UI strings.
*/
@@ -90,11 +99,6 @@ public class CustomPlaybackSpeedPatch {
*/
private static WeakReference<Dialog> currentDialog = new WeakReference<>(null);
/**
* Minimum and maximum custom playback speeds of {@link #customPlaybackSpeeds}.
*/
private static final float customPlaybackSpeedsMin, customPlaybackSpeedsMax;
static {
// Cap at 2 decimals (rounds automatically).
speedFormatter.setMaximumFractionDigits(2);
@@ -174,25 +178,33 @@ public class CustomPlaybackSpeedPatch {
public static void onFlyoutMenuCreate(RecyclerView recyclerView) {
recyclerView.getViewTreeObserver().addOnDrawListener(() -> {
try {
if (PlaybackSpeedMenuFilterPatch.isPlaybackRateSelectorMenuVisible) {
if (hideLithoMenuAndShowCustomSpeedMenu(recyclerView, 5)) {
PlaybackSpeedMenuFilterPatch.isPlaybackRateSelectorMenuVisible = false;
if (PlaybackSpeedMenuFilter.isPlaybackRateSelectorMenuVisible) {
if (hideLithoMenuAndShowSpeedMenu(recyclerView, 5)) {
PlaybackSpeedMenuFilter.isPlaybackRateSelectorMenuVisible = false;
}
}
} catch (Exception ex) {
Logger.printException(() -> "onFlyoutMenuCreate failure", ex);
Logger.printException(() -> "isPlaybackRateSelectorMenuVisible failure", ex);
}
try {
if (PlaybackSpeedMenuFilter.isOldPlaybackSpeedMenuVisible) {
if (hideLithoMenuAndShowSpeedMenu(recyclerView, 8)) {
PlaybackSpeedMenuFilter.isOldPlaybackSpeedMenuVisible = false;
}
}
} catch (Exception ex) {
Logger.printException(() -> "isOldPlaybackSpeedMenuVisible failure", ex);
}
});
}
@SuppressWarnings("SameParameterValue")
private static boolean hideLithoMenuAndShowCustomSpeedMenu(RecyclerView recyclerView, int expectedChildCount) {
private static boolean hideLithoMenuAndShowSpeedMenu(RecyclerView recyclerView, int expectedChildCount) {
if (recyclerView.getChildCount() == 0) {
return false;
}
View firstChild = recyclerView.getChildAt(0);
if (!(firstChild instanceof ViewGroup playbackSpeedParentView)) {
if (!(recyclerView.getChildAt(0) instanceof ViewGroup playbackSpeedParentView)) {
return false;
}
@@ -200,33 +212,49 @@ public class CustomPlaybackSpeedPatch {
return false;
}
ViewParent parentView3rd = Utils.getParentView(recyclerView, 3);
if (!(parentView3rd instanceof ViewGroup)) {
return true;
if (!(Utils.getParentView(recyclerView, 3) instanceof ViewGroup parentView3rd)) {
return false;
}
ViewParent parentView4th = parentView3rd.getParent();
if (!(parentView4th instanceof ViewGroup)) {
return true;
if (!(parentView3rd.getParent() instanceof ViewGroup parentView4th)) {
return false;
}
// Dismiss View [R.id.touch_outside] is the 1st ChildView of the 4th ParentView.
// This only shows in phone layout.
final var touchInsidedView = ((ViewGroup) parentView4th).getChildAt(0);
var touchInsidedView = parentView4th.getChildAt(0);
touchInsidedView.setSoundEffectsEnabled(false);
touchInsidedView.performClick();
// In tablet layout there is no Dismiss View, instead we just hide all two parent views.
((ViewGroup) parentView3rd).setVisibility(View.GONE);
((ViewGroup) parentView4th).setVisibility(View.GONE);
parentView3rd.setVisibility(View.GONE);
parentView4th.setVisibility(View.GONE);
// Close the litho speed menu and show the modern custom speed dialog.
showModernCustomPlaybackSpeedDialog(recyclerView.getContext());
Logger.printDebug(() -> "Modern playback speed dialog shown");
// Close the litho speed menu and show the custom speeds.
if (Settings.RESTORE_OLD_SPEED_MENU.get()) {
showOldPlaybackSpeedMenu();
Logger.printDebug(() -> "Old playback speed dialog shown");
} else {
showModernCustomPlaybackSpeedDialog(recyclerView.getContext());
Logger.printDebug(() -> "Modern playback speed dialog shown");
}
return true;
}
public static void showOldPlaybackSpeedMenu() {
// This method is sometimes used multiple times.
// To prevent this, ignore method reuse within 1 second.
final long now = System.currentTimeMillis();
if (now - lastTimeOldPlaybackMenuInvoked < 1000) {
Logger.printDebug(() -> "Ignoring call to showOldPlaybackSpeedMenu");
return;
}
lastTimeOldPlaybackMenuInvoked = now;
// Rest of the implementation added by patch.
}
/**
* Displays a modern custom dialog for adjusting video playback speed.
* <p>
@@ -643,11 +671,9 @@ public class CustomPlaybackSpeedPatch {
*/
public static int getAdjustedBackgroundColor(boolean isHandleBar) {
final int baseColor = Utils.getDialogBackgroundColor();
float darkThemeFactor = isHandleBar ? 1.25f : 1.115f; // 1.25f for handleBar, 1.115f for others in dark theme.
float lightThemeFactor = isHandleBar ? 0.9f : 0.95f; // 0.9f for handleBar, 0.95f for others in light theme.
return Utils.isDarkModeEnabled()
? Utils.adjustColorBrightness(baseColor, darkThemeFactor) // Lighten for dark theme.
: Utils.adjustColorBrightness(baseColor, lightThemeFactor); // Darken for light theme.
final float darkThemeFactor = isHandleBar ? 1.25f : 1.115f; // 1.25f for handleBar, 1.115f for others in dark theme.
final float lightThemeFactor = isHandleBar ? 0.9f : 0.95f; // 0.9f for handleBar, 0.95f for others in light theme.
return Utils.adjustColorBrightness(baseColor, lightThemeFactor, darkThemeFactor);
}
}

View File

@@ -5,7 +5,9 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.preference.PreferenceFragment;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toolbar;
@@ -24,12 +26,15 @@ import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFrag
* This class is responsible for injecting our own fragment by replacing the LicenseActivity.
*/
@SuppressWarnings("unused")
public class LicenseActivityHook {
public class LicenseActivityHook extends Activity {
private static int currentThemeValueOrdinal = -1; // Must initially be a non-valid enum ordinal value.
private static ViewGroup.LayoutParams toolbarLayoutParams;
@SuppressLint("StaticFieldLeak")
public static SearchViewController searchViewController;
public static void setToolbarLayoutParams(Toolbar toolbar) {
if (toolbarLayoutParams != null) {
toolbar.setLayoutParams(toolbarLayoutParams);
@@ -126,12 +131,13 @@ public class LicenseActivityHook {
view -> view instanceof TextView);
if (toolbarTextView != null) {
toolbarTextView.setTextColor(Utils.getAppForegroundColor());
toolbarTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
}
setToolbarLayoutParams(toolbar);
// Add Search Icon and EditText for ReVancedPreferenceFragment only.
// Add Search bar only for ReVancedPreferenceFragment.
if (fragment instanceof ReVancedPreferenceFragment) {
SearchViewController.addSearchViewComponents(activity, toolbar, (ReVancedPreferenceFragment) fragment);
searchViewController = SearchViewController.addSearchViewComponents(activity, toolbar, (ReVancedPreferenceFragment) fragment);
}
toolBarParent.addView(toolbar, 0);
@@ -166,4 +172,10 @@ public class LicenseActivityHook {
Utils.setIsDarkModeEnabled(themeOrdinal == 1);
}
}
public static void handleConfigurationChanged(Activity activity, Configuration newConfig) {
if (searchViewController != null) {
searchViewController.handleOrientationChange(newConfig.orientation);
}
}
}

View File

@@ -10,6 +10,7 @@ import android.graphics.drawable.GradientDrawable;
import android.util.Pair;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
@@ -51,6 +52,7 @@ public class SearchViewController {
private final Deque<String> searchHistory;
private final AutoCompleteTextView autoCompleteTextView;
private final boolean showSettingsSearchHistory;
private int currentOrientation;
/**
* Creates a background drawable for the SearchView with rounded corners.
@@ -83,8 +85,8 @@ public class SearchViewController {
/**
* Adds search view components to the activity.
*/
public static void addSearchViewComponents(Activity activity, Toolbar toolbar, ReVancedPreferenceFragment fragment) {
new SearchViewController(activity, toolbar, fragment);
public static SearchViewController addSearchViewComponents(Activity activity, Toolbar toolbar, ReVancedPreferenceFragment fragment) {
return new SearchViewController(activity, toolbar, fragment);
}
private SearchViewController(Activity activity, Toolbar toolbar, ReVancedPreferenceFragment fragment) {
@@ -93,6 +95,7 @@ public class SearchViewController {
this.originalTitle = toolbar.getTitle();
this.showSettingsSearchHistory = Settings.SETTINGS_SEARCH_HISTORY.get();
this.searchHistory = new LinkedList<>();
this.currentOrientation = activity.getResources().getConfiguration().orientation;
StringSetting searchEntries = Settings.SETTINGS_SEARCH_ENTRIES;
if (showSettingsSearchHistory) {
String entries = searchEntries.get();
@@ -115,6 +118,9 @@ public class SearchViewController {
searchView.getContext().getResources().getIdentifier(
"android:id/search_src_text", null, null));
// Disable fullscreen keyboard mode.
autoCompleteTextView.setImeOptions(autoCompleteTextView.getImeOptions() | EditorInfo.IME_FLAG_NO_EXTRACT_UI);
// Set background and query hint.
searchView.setBackground(createBackgroundDrawable(toolbar.getContext()));
searchView.setQueryHint(str("revanced_settings_search_hint"));
@@ -197,7 +203,7 @@ public class SearchViewController {
if (isSearchActive) {
closeSearch();
} else {
activity.onBackPressed();
activity.finish();
}
} catch (Exception ex) {
Logger.printException(() -> "navigation click failure", ex);
@@ -285,6 +291,16 @@ public class SearchViewController {
}
}
public void handleOrientationChange(int newOrientation) {
if (newOrientation != currentOrientation) {
currentOrientation = newOrientation;
if (autoCompleteTextView != null) {
autoCompleteTextView.dismissDropDown();
Logger.printDebug(() -> "Orientation changed, search history dismissed");
}
}
}
/**
* Opens the search view and shows the keyboard.
*/
@@ -313,7 +329,7 @@ public class SearchViewController {
/**
* Closes the search view and hides the keyboard.
*/
private void closeSearch() {
public void closeSearch() {
isSearchActive = false;
toolbar.getMenu().findItem(getResourceIdentifier(
"action_search", "id")).setVisible(true);
@@ -326,6 +342,19 @@ public class SearchViewController {
imm.hideSoftInputFromWindow(searchView.getWindowToken(), 0);
}
public static boolean handleBackPress() {
if (LicenseActivityHook.searchViewController != null
&& LicenseActivityHook.searchViewController.isSearchActive()) {
LicenseActivityHook.searchViewController.closeSearch();
return true;
}
return false;
}
public boolean isSearchActive() {
return isSearchActive;
}
/**
* Custom ArrayAdapter for search history.
*/

View File

@@ -68,8 +68,9 @@ public class Settings extends BaseSettings {
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED = new BooleanSetting("revanced_remember_playback_speed_last_selected", FALSE);
public static final BooleanSetting REMEMBER_PLAYBACK_SPEED_LAST_SELECTED_TOAST = new BooleanSetting("revanced_remember_playback_speed_last_selected_toast", TRUE, false,
parent(REMEMBER_PLAYBACK_SPEED_LAST_SELECTED));
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 BooleanSetting CUSTOM_SPEED_MENU = new BooleanSetting("revanced_custom_speed_menu", TRUE);
public static final BooleanSetting RESTORE_OLD_SPEED_MENU = new BooleanSetting("revanced_restore_old_speed_menu", FALSE, parent(CUSTOM_SPEED_MENU));
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
"0.25\n0.5\n0.75\n1.0\n1.25\n1.5\n1.75\n2.0\n2.5\n3.0\n4.0\n5.0\n6.0\n7.0\n8.0", true);
@@ -100,7 +101,6 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_CROWDFUNDING_BOX = new BooleanSetting("revanced_hide_crowdfunding_box", FALSE, true);
public static final BooleanSetting HIDE_DOODLES = new BooleanSetting("revanced_hide_doodles", FALSE, true, "revanced_hide_doodles_user_dialog_message");
public static final BooleanSetting HIDE_EXPANDABLE_CARD = new BooleanSetting("revanced_hide_expandable_card", TRUE);
public static final BooleanSetting HIDE_FEED_SURVEY = new BooleanSetting("revanced_hide_feed_survey", TRUE);
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_FEED = new BooleanSetting("revanced_hide_filter_bar_feed_in_feed", FALSE, true);
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_HISTORY = new BooleanSetting("revanced_hide_filter_bar_feed_in_history", FALSE);
public static final BooleanSetting HIDE_FILTER_BAR_FEED_IN_RELATED_VIDEOS = new BooleanSetting("revanced_hide_filter_bar_feed_in_related_videos", FALSE, true);
@@ -113,6 +113,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
public static final BooleanSetting HIDE_SURVEYS = new BooleanSetting("revanced_hide_surveys", TRUE);
public static final BooleanSetting HIDE_TICKET_SHELF = new BooleanSetting("revanced_hide_ticket_shelf", FALSE);
public static final BooleanSetting HIDE_VIDEO_RECOMMENDATION_LABELS = new BooleanSetting("revanced_hide_video_recommendation_labels", TRUE);
@@ -171,6 +172,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_VIDEO_CHANNEL_WATERMARK = new BooleanSetting("revanced_hide_channel_watermark", 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 BooleanSetting VIDEO_QUALITY_DIALOG_BUTTON = new BooleanSetting("revanced_video_quality_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);
@@ -191,7 +193,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting EXTERNAL_DOWNLOADER = new BooleanSetting("revanced_external_downloader", FALSE);
public static final BooleanSetting EXTERNAL_DOWNLOADER_ACTION_BUTTON = new BooleanSetting("revanced_external_downloader_action_button", FALSE);
public static final StringSetting EXTERNAL_DOWNLOADER_PACKAGE_NAME = new StringSetting("revanced_external_downloader_name",
"org.schabi.newpipe" /* NewPipe */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
"com.deniscerri.ytdl" /* YTDLnis */, parentsAny(EXTERNAL_DOWNLOADER, EXTERNAL_DOWNLOADER_ACTION_BUTTON));
// Comments
public static final BooleanSetting HIDE_COMMENTS_AI_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_ai_chat_summary", FALSE);

View File

@@ -16,10 +16,8 @@ import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings({"unused", "deprecation"})
public final class CustomVideoSpeedListPreference extends CustomDialogListPreference {
/**
* Initialize a settings preference list with the available playback speeds.
*/
private void initializeEntryValues() {
{
// Initialize a settings preference list with the available playback speeds.
float[] customPlaybackSpeeds = CustomPlaybackSpeedPatch.customPlaybackSpeeds;
final int numberOfEntries = customPlaybackSpeeds.length + 1;
String[] preferenceListEntries = new String[numberOfEntries];
@@ -41,10 +39,6 @@ public final class CustomVideoSpeedListPreference extends CustomDialogListPrefer
setEntryValues(preferenceListEntryValues);
}
{
initializeEntryValues();
}
public CustomVideoSpeedListPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}

View File

@@ -0,0 +1,444 @@
package app.revanced.extension.youtube.settings.preference;
import static app.revanced.extension.shared.StringRef.sf;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.net.Uri;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Pair;
import android.util.TypedValue;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.CustomDialogListPreference;
import app.revanced.extension.youtube.settings.Settings;
/**
* A custom ListPreference for selecting an external downloader package with checkmarks and EditText for custom package names.
*/
@SuppressWarnings({"unused", "deprecation"})
public class ExternalDownloaderPreference extends CustomDialogListPreference {
/**
* Enum representing supported external downloaders with their display names, package names, and download URLs.
*/
private enum Downloader {
YTDLNIS("YTDLnis",
"com.deniscerri.ytdl",
"https://ytdlnis.org",
true),
SEAL("Seal",
"com.junkfood.seal",
"https://github.com/JunkFood02/Seal/releases/latest",
true),
GRAYJAY("Grayjay",
"com.futo.platformplayer",
"https://grayjay.app"),
LIBRETUBE("LibreTube",
"com.github.libretube",
"https://libretube.dev"),
NEWPIPE("NewPipe",
"org.schabi.newpipe",
"https://newpipe.net"),
PIPEPIPE("PipePipe",
"InfinityLoop1309.NewPipeEnhanced",
"https://pipepipe.dev"),
TUBULAR("Tubular",
"org.polymorphicshade.tubular",
"https://github.com/polymorphicshade/Tubular/releases/latest"),
OTHER(sf("revanced_external_downloader_other_item").toString(),
null,
null,
true);
private static final Map<String, Downloader> PACKAGE_TO_ENUM = new HashMap<>();
static {
for (Downloader downloader : values()) {
String packageName = downloader.packageName;
if (packageName != null) {
PACKAGE_TO_ENUM.put(packageName, downloader);
}
}
}
/**
* Finds a Downloader by its package name. This method can never return {@link #OTHER}.
* @return The Downloader enum or null if not found.
*/
@Nullable
public static Downloader findByPackageName(String packageName) {
return PACKAGE_TO_ENUM.get(Objects.requireNonNull(packageName));
}
public final String name;
@Nullable
public final String packageName;
@Nullable
public final String downloadUrl;
/**
* If a downloader app should be shown in the preference settings
* if the app is not currently installed.
*/
public final boolean isPreferred;
Downloader(String name, String packageName, String downloadUrl) {
this(name, packageName, downloadUrl, false);
}
Downloader(String name, @Nullable String packageName, @Nullable String downloadUrl, boolean isPreferred) {
this.name = name;
this.packageName = packageName;
this.downloadUrl = downloadUrl;
this.isPreferred = isPreferred;
}
public boolean isInstalled() {
return packageName != null && isAppInstalledAndEnabled(packageName);
}
}
private static boolean isAppInstalledAndEnabled(String packageName) {
try {
if (Utils.getContext().getPackageManager().getApplicationInfo(packageName, 0).enabled) {
Logger.printDebug(() -> "App installed: " + packageName);
return true;
}
} catch (PackageManager.NameNotFoundException error) {
Logger.printDebug(() -> "App not installed: " + packageName);
}
return false;
}
private EditText editText;
private CustomDialogListPreference.ListPreferenceArrayAdapter adapter;
public ExternalDownloaderPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public ExternalDownloaderPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ExternalDownloaderPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ExternalDownloaderPreference(Context context) {
super(context);
}
private void updateEntries() {
List<CharSequence> entries = new ArrayList<>();
List<CharSequence> entryValues = new ArrayList<>();
for (Downloader downloader : Downloader.values()) {
if (downloader.isPreferred || downloader.isInstalled()) {
String packageName = downloader.packageName;
entries.add(downloader.name);
entryValues.add(packageName != null
? packageName
: Downloader.OTHER.name);
}
}
setEntries(entries.toArray(new CharSequence[0]));
setEntryValues(entryValues.toArray(new CharSequence[0]));
}
/**
* Sets the summary for this ListPreference.
*/
@Override
public void setSummary(CharSequence summary) {
// Ignore calls to set the summary.
// Summary is always the description of the category.
//
// This is required otherwise the ReVanced preference fragment
// sets all ListPreference summaries to show the current selection.
}
/**
* Shows a custom dialog with a ListView for predefined downloader packages and EditText for custom package input.
*/
@Override
protected void showDialog(@Nullable Bundle state) {
// Must set entries before showing the dialog, to handle if
// an app is installed while the settings are open in the background.
updateEntries();
Context context = getContext();
String packageName = Settings.EXTERNAL_DOWNLOADER_PACKAGE_NAME.get();
// Create the main layout for the dialog content.
LinearLayout contentLayout = new LinearLayout(context);
contentLayout.setOrientation(LinearLayout.VERTICAL);
// Create ListView for predefined downloader apps.
ListView listView = new ListView(context);
listView.setId(android.R.id.list);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
// Create custom adapter for the ListView.
final boolean usingCustomDownloader = Downloader.findByPackageName(packageName) == null;
adapter = new CustomDialogListPreference.ListPreferenceArrayAdapter(
context,
Utils.getResourceIdentifier("revanced_custom_list_item_checked", "layout"),
getEntries(),
getEntryValues(),
usingCustomDownloader
? Downloader.OTHER.name
: packageName
);
listView.setAdapter(adapter);
Function<String, Void> updateListViewSelection = (updatedPackageName) -> {
String entryValueName = Downloader.findByPackageName(updatedPackageName) == null
? Downloader.OTHER.name
: updatedPackageName;
CharSequence[] entryValues = getEntryValues();
for (int i = 0, length = entryValues.length; i < length; i++) {
String entryString = entryValues[i].toString();
if (entryString.equals(entryValueName)) {
listView.setItemChecked(i, true);
listView.setSelection(i);
adapter.setSelectedValue(entryString);
adapter.notifyDataSetChanged();
break;
}
}
return null;
};
updateListViewSelection.apply(packageName);
// Handle item click to select value.
listView.setOnItemClickListener((parent, view, position, id) -> {
String selectedValue = getEntryValues()[position].toString();
Downloader selectedApp = Downloader.findByPackageName(selectedValue);
if (selectedApp != null) {
editText.setText(selectedApp.packageName);
editText.setEnabled(false); // Disable editing for predefined options.
} else {
String savedPackageName = Settings.EXTERNAL_DOWNLOADER_PACKAGE_NAME.get();
editText.setText(Downloader.findByPackageName(savedPackageName) == null
? savedPackageName // If the user is clicking thru options then retain existing other app.
: ""
);
editText.setEnabled(true); // Enable editing for Custom.
editText.requestFocus();
}
editText.setSelection(editText.getText().length());
adapter.setSelectedValue(selectedValue);
adapter.notifyDataSetChanged();
});
// Add ListView to content layout with initial height.
LinearLayout.LayoutParams listViewParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
0 // Initial height, will be updated.
);
listViewParams.bottomMargin = dipToPixels(16);
contentLayout.addView(listView, listViewParams);
// Add EditText for custom package name.
editText = new EditText(context);
editText.setText(packageName);
editText.setSelection(packageName.length());
editText.setHint(str("revanced_external_downloader_other_item_hint"));
editText.setSingleLine(true); // Restrict EditText to a single line.
editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
// Set initial EditText state based on selected downloader.
editText.setEnabled(usingCustomDownloader);
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {}
@Override
public void afterTextChanged(Editable edit) {
String updatedPackageName = edit.toString().trim();
updateListViewSelection.apply(updatedPackageName);
}
});
ShapeDrawable editTextBackground = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(10), null, null));
editTextBackground.getPaint().setColor(Utils.getEditTextBackground());
final int dip8 = dipToPixels(8);
editText.setPadding(dip8, dip8, dip8, dip8);
editText.setBackground(editTextBackground);
editText.setClipToOutline(true);
contentLayout.addView(editText);
// Create the custom dialog.
Pair<Dialog, LinearLayout> dialogPair = Utils.createCustomDialog(
context,
getTitle() != null ? getTitle().toString() : "",
null,
null,
null,
() -> {
String newValue = editText.getText().toString().trim();
if (newValue.isEmpty()) {
// Show dialog if EditText is empty.
Utils.createCustomDialog(
context,
str("revanced_external_downloader_name_title"),
str("revanced_external_downloader_empty_warning"),
null,
null,
() -> {}, // OK button does nothing (dismiss only).
null,
null,
null,
false
).first.show();
return;
}
if (showDialogIfAppIsNotInstalled(getContext(), newValue)) {
return; // Invalid package. Do not save.
}
// Save custom package name.
if (callChangeListener(newValue)) {
setValue(newValue);
}
},
() -> {}, // Cancel button action (dismiss only).
str("revanced_settings_reset"),
() -> { // Reset action.
String defaultValue = Settings.EXTERNAL_DOWNLOADER_PACKAGE_NAME.defaultValue;
editText.setText(defaultValue);
editText.setSelection(defaultValue.length());
editText.setEnabled(false); // Disable editing on reset.
updateListViewSelection.apply(defaultValue);
},
false
);
// Add the content layout directly to the dialog's main layout.
LinearLayout dialogMainLayout = dialogPair.second;
dialogMainLayout.addView(contentLayout, dialogMainLayout.getChildCount() - 1);
// Update ListView height dynamically based on orientation.
//noinspection ExtractMethodRecommender
Runnable updateListViewHeight = () -> {
int totalHeight = 0;
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter != null) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
final int listAdapterCount = listAdapter.getCount();
for (int i = 0; i < listAdapterCount; i++) {
View item = listAdapter.getView(i, null, listView);
item.measure(
View.MeasureSpec.makeMeasureSpec(metrics.widthPixels, View.MeasureSpec.AT_MOST),
View.MeasureSpec.UNSPECIFIED
);
totalHeight += item.getMeasuredHeight();
}
totalHeight += listView.getDividerHeight() * (listAdapterCount - 1);
}
final int orientation = context.getResources().getConfiguration().orientation;
if (orientation == android.content.res.Configuration.ORIENTATION_PORTRAIT) {
// In portrait orientation, use WRAP_CONTENT for ListView height.
listViewParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
} else {
// In landscape orientation, limit ListView height to 30% of screen height.
final int maxHeight = Utils.percentageHeightToPixels(30);
listViewParams.height = Math.min(totalHeight, maxHeight);
}
listView.setLayoutParams(listViewParams);
};
// Initial height calculation.
updateListViewHeight.run();
// Listen for configuration changes (e.g., orientation).
View dialogView = dialogPair.second;
// Recalculate height when layout changes (e.g., orientation change).
dialogView.getViewTreeObserver().addOnGlobalLayoutListener(updateListViewHeight::run);
// Show the dialog.
dialogPair.first.show();
}
/**
* @return If the app is not installed and a dialog was shown.
*/
public static boolean showDialogIfAppIsNotInstalled(Context context, String packageName) {
if (isAppInstalledAndEnabled(packageName)) {
return false;
}
Downloader downloader = Downloader.findByPackageName(packageName);
String downloadUrl = downloader != null
? downloader.downloadUrl
: null;
String okButtonText = downloadUrl != null
? str("gms_core_dialog_open_website_text") // Open website.
: null; // Ok.
// Show a dialog if the recommended app is not installed or if the custom package cannot be found.
String message = downloader != null
? str("revanced_external_downloader_not_installed_warning", downloader.name)
: str("revanced_external_downloader_package_not_found_warning", packageName);
Utils.createCustomDialog(
context,
str("revanced_external_downloader_not_found_title"),
message,
null,
okButtonText,
() -> {
try {
// OK button action: open the downloader's URL if available.
if (downloadUrl != null) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(downloadUrl));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
} catch (Exception ex) {
Logger.printException(() -> "Failed to open downloader URL: " + downloader, ex);
}
},
() -> {}, // Cancel button action (dismiss only).
null,
null,
false
).first.show();
return true;
}
}

View File

@@ -17,6 +17,7 @@ import android.preference.SwitchPreference;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.style.BackgroundColorSpan;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
@@ -248,7 +249,15 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
rootView.setOnApplyWindowInsetsListener((v, insets) -> {
Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars());
Insets navInsets = insets.getInsets(WindowInsets.Type.navigationBars());
v.setPadding(0, statusInsets.top, 0, navInsets.bottom);
Insets cutoutInsets = insets.getInsets(WindowInsets.Type.displayCutout());
// Apply padding for display cutout in landscape.
int leftPadding = cutoutInsets.left;
int rightPadding = cutoutInsets.right;
int topPadding = statusInsets.top;
int bottomPadding = navInsets.bottom;
v.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
return insets;
});
}
@@ -265,10 +274,16 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
true, TextView.class::isInstance);
if (toolbarTextView != null) {
toolbarTextView.setTextColor(Utils.getAppForegroundColor());
toolbarTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
}
LicenseActivityHook.setToolbarLayoutParams(toolbar);
if (LicenseActivityHook.searchViewController != null
&& LicenseActivityHook.searchViewController.isSearchActive()) {
toolbar.post(() -> LicenseActivityHook.searchViewController.closeSearch());
}
rootView.addView(toolbar, 0);
return false;
}
@@ -318,10 +333,8 @@ class AbstractPreferenceSearchData<T extends Preference> {
return text;
}
final int baseColor = Utils.getAppBackgroundColor();
final int adjustedColor = Utils.isDarkModeEnabled()
? Utils.adjustColorBrightness(baseColor, 1.20f) // Lighten for dark theme.
: Utils.adjustColorBrightness(baseColor, 0.95f); // Darken for light theme.
final int adjustedColor = Utils.adjustColorBrightness(Utils.getAppBackgroundColor(),
0.95f, 1.20f);
BackgroundColorSpan highlightSpan = new BackgroundColorSpan(adjustedColor);
SpannableStringBuilder spannable = new SpannableStringBuilder(text);

View File

@@ -830,11 +830,10 @@ public class SegmentPlaybackController {
WindowManager.LayoutParams params = window.getAttributes();
params.gravity = Gravity.BOTTOM;
params.y = dipToPixels(72);
DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics();
int portraitWidth = (int) (displayMetrics.widthPixels * 0.6);
int portraitWidth = Utils.percentageWidthToPixels(60); // 60% of the screen width.
if (Resources.getSystem().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
portraitWidth = (int) Math.min(portraitWidth, displayMetrics.heightPixels * 0.6);
portraitWidth = Math.min(portraitWidth, Utils.percentageHeightToPixels(60)); // 60% of the screen height.
}
params.width = portraitWidth;
params.dimAmount = 0.0f;

View File

@@ -9,9 +9,6 @@ import app.revanced.extension.youtube.patches.VideoInformation;
import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch;
import app.revanced.extension.youtube.settings.Settings;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.showToastShort;
@SuppressWarnings("unused")
public class PlaybackSpeedDialogButton {
@Nullable
@@ -29,7 +26,11 @@ public class PlaybackSpeedDialogButton {
Settings.PLAYBACK_SPEED_DIALOG_BUTTON::get,
view -> {
try {
CustomPlaybackSpeedPatch.showModernCustomPlaybackSpeedDialog(view.getContext());
if (Settings.RESTORE_OLD_SPEED_MENU.get()) {
CustomPlaybackSpeedPatch.showOldPlaybackSpeedMenu();
} else {
CustomPlaybackSpeedPatch.showModernCustomPlaybackSpeedDialog(view.getContext());
}
} catch (Exception ex) {
Logger.printException(() -> "speed button onClick failure", ex);
}

View File

@@ -187,4 +187,56 @@ public class PlayerControlButton {
if (view != null) view.setVisibility(View.GONE);
isVisible = false;
}
}
/**
* Sets the icon of the button.
* @param resourceId Drawable identifier, or zero to hide the icon.
*/
public void setIcon(int resourceId) {
try {
View button = buttonRef.get();
if (button instanceof ImageView imageButton) {
imageButton.setImageResource(resourceId);
}
} catch (Exception ex) {
Logger.printException(() -> "setIcon failure", ex);
}
}
/**
* Starts an animation on the button.
* @param animation The animation to apply.
*/
public void startAnimation(Animation animation) {
try {
View button = buttonRef.get();
if (button != null) {
button.startAnimation(animation);
}
} catch (Exception ex) {
Logger.printException(() -> "startAnimation failure", ex);
}
}
/**
* Clears any animation on the button.
*/
public void clearAnimation() {
try {
View button = buttonRef.get();
if (button != null) {
button.clearAnimation();
}
} catch (Exception ex) {
Logger.printException(() -> "clearAnimation failure", ex);
}
}
/**
* Returns the View associated with this button.
* @return The button View.
*/
public View getView() {
return buttonRef.get();
}
}

View File

@@ -0,0 +1,484 @@
package app.revanced.extension.youtube.videoplayer;
import static app.revanced.extension.shared.StringRef.str;
import static app.revanced.extension.shared.Utils.dipToPixels;
import static app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch.AUTOMATIC_VIDEO_QUALITY_VALUE;
import static app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch.VIDEO_QUALITY_1080P_PREMIUM_NAME;
import static app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch.VideoQualityMenuInterface;
import android.app.Dialog;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.libraries.youtube.innertube.model.media.VideoQuality;
import java.util.ArrayList;
import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.patches.playback.quality.RememberVideoQualityPatch;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class VideoQualityDialogButton {
private static final int DRAWABLE_LD = getDrawableIdentifier("revanced_video_quality_dialog_button_ld");
private static final int DRAWABLE_SD = getDrawableIdentifier("revanced_video_quality_dialog_button_sd");
private static final int DRAWABLE_HD = getDrawableIdentifier("revanced_video_quality_dialog_button_hd");
private static final int DRAWABLE_FHD = getDrawableIdentifier("revanced_video_quality_dialog_button_fhd");
private static final int DRAWABLE_FHD_PLUS = getDrawableIdentifier("revanced_video_quality_dialog_button_fhd_plus");
private static final int DRAWABLE_QHD = getDrawableIdentifier("revanced_video_quality_dialog_button_qhd");
private static final int DRAWABLE_4K = getDrawableIdentifier("revanced_video_quality_dialog_button_4k");
private static final int DRAWABLE_UNKNOWN = getDrawableIdentifier("revanced_video_quality_dialog_button_unknown");
@Nullable
private static PlayerControlButton instance;
/**
* The current resource name of the button icon.
*/
private static int currentIconResource;
private static int getDrawableIdentifier(String resourceName) {
final int resourceId = Utils.getResourceIdentifier(resourceName, "drawable");
if (resourceId == 0) Logger.printException(() -> "Could not find resource: " + resourceName);
return resourceId;
}
/**
* Updates the button icon based on the current video quality.
*/
public static void updateButtonIcon(@Nullable VideoQuality quality) {
try {
Utils.verifyOnMainThread();
if (instance == null) return;
final int resolution = quality == null
? AUTOMATIC_VIDEO_QUALITY_VALUE // Video is still loading.
: quality.patch_getResolution();
final int iconResource = switch (resolution) {
case 144, 240, 360 -> DRAWABLE_LD;
case 480 -> DRAWABLE_SD;
case 720 -> DRAWABLE_HD;
case 1080 -> VIDEO_QUALITY_1080P_PREMIUM_NAME.equals(quality.patch_getQualityName())
? DRAWABLE_FHD_PLUS
: DRAWABLE_FHD;
case 1440 -> DRAWABLE_QHD;
case 2160 -> DRAWABLE_4K;
default -> DRAWABLE_UNKNOWN;
};
if (iconResource != currentIconResource) {
currentIconResource = iconResource;
Utils.runOnMainThreadDelayed(() -> {
if (iconResource != currentIconResource) {
Logger.printDebug(() -> "Ignoring stale button update to: " + quality);
return;
}
instance.setIcon(iconResource);
}, 100);
}
} catch (Exception ex) {
Logger.printException(() -> "updateButtonIcon failure", ex);
}
}
/**
* Injection point.
*/
public static void initializeButton(View controlsView) {
try {
instance = new PlayerControlButton(
controlsView,
"revanced_video_quality_dialog_button",
"revanced_video_quality_dialog_button_placeholder",
Settings.VIDEO_QUALITY_DIALOG_BUTTON::get,
view -> {
try {
showVideoQualityDialog(view.getContext());
} catch (Exception ex) {
Logger.printException(() -> "Video quality button onClick failure", ex);
}
},
view -> {
try {
VideoQuality[] qualities = RememberVideoQualityPatch.getCurrentQualities();
VideoQualityMenuInterface menu = RememberVideoQualityPatch.getCurrentMenuInterface();
if (qualities == null || menu == null) {
Logger.printDebug(() -> "Cannot reset quality, videoQualities is null");
return true;
}
// Reset to default quality.
final int defaultResolution = RememberVideoQualityPatch.getDefaultQualityResolution();
for (VideoQuality quality : qualities) {
final int resolution = quality.patch_getResolution();
if (resolution != AUTOMATIC_VIDEO_QUALITY_VALUE && resolution <= defaultResolution) {
Logger.printDebug(() -> "Resetting quality to: " + quality);
menu.patch_setQuality(quality);
return true;
}
}
// Existing hook cannot set default quality to auto.
// Instead show the quality dialog.
showVideoQualityDialog(view.getContext());
return true;
} catch (Exception ex) {
Logger.printException(() -> "Video quality button reset failure", ex);
}
return false;
}
);
// Set initial icon.
updateButtonIcon(RememberVideoQualityPatch.getCurrentQuality());
} catch (Exception ex) {
Logger.printException(() -> "initializeButton failure", ex);
}
}
/**
* Injection point.
*/
public static void setVisibilityImmediate(boolean visible) {
if (instance != null) {
instance.setVisibilityImmediate(visible);
}
}
/**
* Injection point.
*/
public static void setVisibility(boolean visible, boolean animated) {
if (instance != null) {
instance.setVisibility(visible, animated);
}
}
/**
* Shows a dialog with available video qualities, excluding Auto, with a title showing the current quality.
*/
private static void showVideoQualityDialog(Context context) {
try {
VideoQuality[] currentQualities = RememberVideoQualityPatch.getCurrentQualities();
VideoQuality currentQuality = RememberVideoQualityPatch.getCurrentQuality();
if (currentQualities == null || currentQuality == null) {
Logger.printDebug(() -> "Cannot show qualities dialog, videoQualities is null");
return;
}
if (currentQualities.length < 2) {
// Should never happen.
Logger.printException(() -> "Cannot show qualities dialog, no qualities available");
return;
}
VideoQualityMenuInterface menu = RememberVideoQualityPatch.getCurrentMenuInterface();
if (menu == null) {
Logger.printDebug(() -> "Cannot show qualities dialog, menu is null");
return;
}
// -1 adjustment for automatic quality at first index.
int listViewSelectedIndex = 0;
for (VideoQuality quality : currentQualities) {
if (quality == currentQuality) {
// -1 adjustment for the missing automatic quality in the dialog list.
listViewSelectedIndex--;
break;
}
listViewSelectedIndex++;
}
List<String> qualityLabels = new ArrayList<>(currentQualities.length - 1);
for (VideoQuality availableQuality : currentQualities) {
if (availableQuality.patch_getResolution() != AUTOMATIC_VIDEO_QUALITY_VALUE) {
qualityLabels.add(availableQuality.patch_getQualityName());
}
}
Dialog dialog = new Dialog(context);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setCanceledOnTouchOutside(true);
dialog.setCancelable(true);
final int dip4 = dipToPixels(4); // Height for handle bar.
final int dip5 = dipToPixels(5); // Padding for mainLayout.
final int dip6 = dipToPixels(6); // Bottom margin.
final int dip8 = dipToPixels(8); // Side padding.
final int dip16 = dipToPixels(16); // Left padding for ListView.
final int dip20 = dipToPixels(20); // Margin below handle.
final int dip40 = dipToPixels(40); // Width for handle bar.
LinearLayout mainLayout = new LinearLayout(context);
mainLayout.setOrientation(LinearLayout.VERTICAL);
mainLayout.setPadding(dip5, dip8, dip5, dip8);
ShapeDrawable background = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(12), null, null));
background.getPaint().setColor(Utils.getDialogBackgroundColor());
mainLayout.setBackground(background);
View handleBar = new View(context);
ShapeDrawable handleBackground = new ShapeDrawable(new RoundRectShape(
Utils.createCornerRadii(4), null, null));
final int baseColor = Utils.getDialogBackgroundColor();
final int adjustedHandleBarBackgroundColor = Utils.adjustColorBrightness(
baseColor, 0.9f, 1.25f);
handleBackground.getPaint().setColor(adjustedHandleBarBackgroundColor);
handleBar.setBackground(handleBackground);
LinearLayout.LayoutParams handleParams = new LinearLayout.LayoutParams(dip40, dip4);
handleParams.gravity = Gravity.CENTER_HORIZONTAL;
handleParams.setMargins(0, 0, 0, dip20);
handleBar.setLayoutParams(handleParams);
mainLayout.addView(handleBar);
// Create SpannableStringBuilder for formatted text.
SpannableStringBuilder spannableTitle = new SpannableStringBuilder();
String titlePart = str("video_quality_quick_menu_title");
String separatorPart = str("video_quality_title_seperator");
// Append title part with default foreground color.
spannableTitle.append(titlePart);
spannableTitle.setSpan(
new ForegroundColorSpan(Utils.getAppForegroundColor()),
0,
titlePart.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
spannableTitle.append(" "); // Space after title.
// Append separator part with adjusted title color.
int separatorStart = spannableTitle.length();
spannableTitle.append(separatorPart);
final int adjustedTitleForegroundColor = Utils.adjustColorBrightness(
Utils.getAppForegroundColor(), 1.6f, 0.6f);
spannableTitle.setSpan(
new ForegroundColorSpan(adjustedTitleForegroundColor),
separatorStart,
separatorStart + separatorPart.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
spannableTitle.append(" "); // Space after separator.
// Append quality label with adjusted title color.
final int qualityStart = spannableTitle.length();
spannableTitle.append(currentQuality.patch_getQualityName());
spannableTitle.setSpan(
new ForegroundColorSpan(adjustedTitleForegroundColor),
qualityStart,
qualityStart + currentQuality.patch_getQualityName().length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
);
// Add title with current quality.
TextView titleView = new TextView(context);
titleView.setText(spannableTitle);
titleView.setTextSize(16);
// Remove setTextColor since color is handled by SpannableStringBuilder.
LinearLayout.LayoutParams titleParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
titleParams.setMargins(dip8, 0, 0, dip20);
titleView.setLayoutParams(titleParams);
mainLayout.addView(titleView);
ListView listView = new ListView(context);
CustomQualityAdapter adapter = new CustomQualityAdapter(context, qualityLabels);
adapter.setSelectedPosition(listViewSelectedIndex);
listView.setAdapter(adapter);
listView.setDivider(null);
listView.setPadding(dip16, 0, 0, 0);
listView.setOnItemClickListener((parent, view, which, id) -> {
try {
final int originalIndex = which + 1; // Adjust for automatic.
VideoQuality selectedQuality = currentQualities[originalIndex];
Logger.printDebug(() -> "User clicked on quality: " + selectedQuality);
if (RememberVideoQualityPatch.shouldRememberVideoQuality()) {
RememberVideoQualityPatch.saveDefaultQuality(selectedQuality.patch_getResolution());
}
// Don't update button icon now. Icon will update when the actual
// quality is changed by YT. This is needed to ensure the icon is correct
// if YT ignores changing from 1080p Premium to regular 1080p.
menu.patch_setQuality(selectedQuality);
dialog.dismiss();
} catch (Exception ex) {
Logger.printException(() -> "Video quality selection failure", ex);
}
});
LinearLayout.LayoutParams listViewParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
listViewParams.setMargins(0, 0, 0, dip5);
listView.setLayoutParams(listViewParams);
mainLayout.addView(listView);
LinearLayout wrapperLayout = new LinearLayout(context);
wrapperLayout.setOrientation(LinearLayout.VERTICAL);
wrapperLayout.setPadding(dip8, 0, dip8, 0);
wrapperLayout.addView(mainLayout);
dialog.setContentView(wrapperLayout);
Window window = dialog.getWindow();
if (window != null) {
WindowManager.LayoutParams params = window.getAttributes();
params.gravity = Gravity.BOTTOM;
params.y = dip6;
int portraitWidth = context.getResources().getDisplayMetrics().widthPixels;
if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
portraitWidth = Math.min(
portraitWidth,
context.getResources().getDisplayMetrics().heightPixels);
}
params.width = portraitWidth;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(params);
window.setBackgroundDrawable(null);
}
final int fadeDurationFast = Utils.getResourceInteger("fade_duration_fast");
Animation slideInABottomAnimation = Utils.getResourceAnimation("slide_in_bottom");
slideInABottomAnimation.setDuration(fadeDurationFast);
mainLayout.startAnimation(slideInABottomAnimation);
// noinspection ClickableViewAccessibility
mainLayout.setOnTouchListener(new View.OnTouchListener() {
final float dismissThreshold = dipToPixels(100);
float touchY;
float translationY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchY = event.getRawY();
translationY = mainLayout.getTranslationY();
return true;
case MotionEvent.ACTION_MOVE:
final float deltaY = event.getRawY() - touchY;
if (deltaY >= 0) {
mainLayout.setTranslationY(translationY + deltaY);
}
return true;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mainLayout.getTranslationY() > dismissThreshold) {
//noinspection ExtractMethodRecommender
final float remainingDistance = context.getResources().getDisplayMetrics().heightPixels
- mainLayout.getTop();
TranslateAnimation slideOut = new TranslateAnimation(
0, 0, mainLayout.getTranslationY(), remainingDistance);
slideOut.setDuration(fadeDurationFast);
slideOut.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {}
@Override
public void onAnimationEnd(Animation animation) {
dialog.dismiss();
}
@Override
public void onAnimationRepeat(Animation animation) {}
});
mainLayout.startAnimation(slideOut);
} else {
TranslateAnimation slideBack = new TranslateAnimation(
0, 0, mainLayout.getTranslationY(), 0);
slideBack.setDuration(fadeDurationFast);
mainLayout.startAnimation(slideBack);
mainLayout.setTranslationY(0);
}
return true;
default:
return false;
}
}
});
dialog.show();
} catch (Exception ex) {
Logger.printException(() -> "showVideoQualityDialog failure", ex);
}
}
private static class CustomQualityAdapter extends ArrayAdapter<String> {
private int selectedPosition = -1;
public CustomQualityAdapter(@NonNull Context context, @NonNull List<String> objects) {
super(context, 0, objects);
}
private void setSelectedPosition(int position) {
this.selectedPosition = position;
notifyDataSetChanged();
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(
Utils.getResourceIdentifier("revanced_custom_list_item_checked", "layout"),
parent,
false
);
viewHolder = new ViewHolder();
viewHolder.checkIcon = convertView.findViewById(
Utils.getResourceIdentifier("revanced_check_icon", "id")
);
viewHolder.placeholder = convertView.findViewById(
Utils.getResourceIdentifier("revanced_check_icon_placeholder", "id")
);
viewHolder.textView = convertView.findViewById(
Utils.getResourceIdentifier("revanced_item_text", "id")
);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.textView.setText(getItem(position));
final boolean isSelected = position == selectedPosition;
viewHolder.checkIcon.setVisibility(isSelected ? View.VISIBLE : View.GONE);
viewHolder.placeholder.setVisibility(isSelected ? View.GONE : View.INVISIBLE);
return convertView;
}
private static class ViewHolder {
ImageView checkIcon;
View placeholder;
TextView textView;
}
}
}

View File

@@ -0,0 +1,8 @@
package com.google.android.libraries.youtube.innertube.model.media;
public abstract class VideoQuality implements Comparable<VideoQuality> {
public abstract String patch_getQualityName();
public abstract int patch_getResolution();
}

View File

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

9
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^24.2.6"
"semantic-release": "^24.2.7"
}
},
"node_modules/@babel/code-frame": {
@@ -6889,10 +6889,11 @@
"license": "MIT"
},
"node_modules/semantic-release": {
"version": "24.2.6",
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.6.tgz",
"integrity": "sha512-D0cwjlO5RZzHHxAcsoF1HxiRLfC3ehw+ay+zntzFs6PNX6aV0JzKNG15mpxPipBYa/l4fHly88dHvgDyqwb1Ww==",
"version": "24.2.7",
"resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.7.tgz",
"integrity": "sha512-g7RssbTAbir1k/S7uSwSVZFfFXwpomUB9Oas0+xi9KStSCmeDXcA7rNhiskjLqvUe/Evhx8fVCT16OSa34eM5g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@semantic-release/commit-analyzer": "^13.0.0-beta.1",
"@semantic-release/error": "^4.0.0",

View File

@@ -4,6 +4,6 @@
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"gradle-semantic-release-plugin": "^1.10.1",
"semantic-release": "^24.2.6"
"semantic-release": "^24.2.7"
}
}

View File

@@ -424,6 +424,10 @@ public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatc
public static final fun getFixCrashPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/orfon/detection/root/RemoveRootDetectionPatchKt {
public static final fun getRemoveRootDetectionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/pandora/ads/DisableAudioAdsPatchKt {
public static final fun getDisableAudioAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -476,6 +480,10 @@ public final class app/revanced/patches/primevideo/misc/permissions/RenamePermis
public static final fun getRenamePermissionsPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
public final class app/revanced/patches/primevideo/video/speed/PlaybackSpeedPatchKt {
public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/protonmail/account/RemoveFreeAccountsLimitPatchKt {
public static final fun getRemoveFreeAccountsLimitPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
@@ -1238,6 +1246,7 @@ public final class app/revanced/patches/youtube/interaction/dialog/RemoveViewerD
public final class app/revanced/patches/youtube/interaction/doubletap/DisableChapterSkipDoubleTapPatchKt {
public static final fun getDisableChapterSkipDoubleTapPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun getDisableDoubleTapActionsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/interaction/downloads/DownloadsPatchKt {
@@ -1655,6 +1664,10 @@ public final class app/revanced/patches/youtube/video/quality/VideoQualityPatchK
public static final fun getVideoQualityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/quality/button/VideoQualityDialogButtonPatchKt {
public static final fun getVideoQualityButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/speed/PlaybackSpeedPatchKt {
public static final fun getPlaybackSpeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

View File

@@ -23,7 +23,6 @@ internal val createInboxSubTabsFingerprint = fingerprint {
}
internal val loadInboxAdsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("V")
strings(
"ads_load_begin",

View File

@@ -8,12 +8,7 @@ val hideVideoAdsPatch = bytecodePatch(
name = "Hide music video ads",
description = "Hides ads that appear while listening to or streaming music videos, podcasts, or songs.",
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
compatibleWith("com.google.android.apps.youtube.music")
execute {
navigate(showVideoAdsParentFingerprint.originalMethod)

View File

@@ -8,12 +8,7 @@ val enableExclusiveAudioPlaybackPatch = bytecodePatch(
name = "Enable exclusive audio playback",
description = "Enables the option to play audio without video.",
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
compatibleWith("com.google.android.apps.youtube.music")
execute {
allowExclusiveAudioPlaybackFingerprint.method.returnEarly(true)

View File

@@ -11,12 +11,7 @@ val permanentRepeatPatch = bytecodePatch(
description = "Permanently remember your repeating preference even if the playlist ends or another track is played.",
use = false,
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
compatibleWith("com.google.android.apps.youtube.music")
execute {
val startIndex = repeatTrackFingerprint.patternMatch!!.endIndex

View File

@@ -7,18 +7,9 @@ import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
val permanentShufflePatch = bytecodePatch(
description = "Permanently remember your shuffle preference " +
"even if the playlist ends or another track is played.",
use = false,
"even if the playlist ends or another track is played."
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"6.45.54",
"6.51.53",
"7.01.53",
"7.02.52",
"7.03.52",
),
)
compatibleWith("com.google.android.apps.youtube.music")
execute {
disableShuffleFingerprint.method.addInstruction(0, "return-void")

View File

@@ -11,12 +11,7 @@ val hideCategoryBar = bytecodePatch(
description = "Hides the category bar at the top of the homepage.",
use = false,
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
compatibleWith("com.google.android.apps.youtube.music")
execute {
constructCategoryBarFingerprint.method.apply {

View File

@@ -11,12 +11,7 @@ val hideGetPremiumPatch = bytecodePatch(
name = "Hide 'Get Music Premium' label",
description = "Hides the \"Get Music Premium\" label from the account menu and settings.",
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
compatibleWith("com.google.android.apps.youtube.music")
execute {
hideGetPremiumFingerprint.method.apply {

View File

@@ -18,12 +18,7 @@ val removeUpgradeButtonPatch = bytecodePatch(
name = "Remove upgrade button",
description = "Removes the upgrade tab from the pivot bar.",
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
compatibleWith("com.google.android.apps.youtube.music")
execute {
pivotBarConstructorFingerprint.method.apply {

View File

@@ -8,12 +8,7 @@ val bypassCertificateChecksPatch = bytecodePatch(
name = "Bypass certificate checks",
description = "Bypasses certificate checks which prevent YouTube Music from working on Android Auto.",
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
compatibleWith("com.google.android.apps.youtube.music")
execute {
checkCertificateFingerprint.method.returnEarly(true)

View File

@@ -8,12 +8,7 @@ val backgroundPlaybackPatch = bytecodePatch(
name = "Remove background playback restrictions",
description = "Removes restrictions on background playback, including playing kids videos in the background.",
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
compatibleWith("com.google.android.apps.youtube.music")
execute {
kidsBackgroundPlaybackPolicyControllerFingerprint.method.addInstruction(

View File

@@ -25,12 +25,7 @@ val spoofClientPatch = bytecodePatch(
name = "Spoof client",
description = "Spoofs the client to fix playback.",
) {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.16.53",
"8.05.51"
)
)
compatibleWith("com.google.android.apps.youtube.music")
dependsOn(
sharedExtensionPatch,

View File

@@ -3,6 +3,7 @@ package app.revanced.patches.nfctoolsse.misc.pro
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
@Deprecated("This patch no longer works and will soon be deleted.")
internal val isLicenseRegisteredFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC)
returns("Z")

View File

@@ -4,9 +4,8 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
val unlockProPatch = bytecodePatch(
name = "Unlock pro",
) {
@Deprecated("This patch no longer works and will soon be deleted.")
val unlockProPatch = bytecodePatch{
compatibleWith("com.wakdev.apps.nfctools.se")
execute {

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.orfon.detection.root
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val isDeviceRootedFingeprint = fingerprint {
accessFlags(AccessFlags.PUBLIC)
returns("Z")
custom { method, classDef ->
method.name == "isDeviceRooted" &&
classDef.endsWith("/RootChecker;")
}
}

View File

@@ -0,0 +1,16 @@
package app.revanced.patches.orfon.detection.root
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val removeRootDetectionPatch = bytecodePatch(
name = "Remove root detection",
description = "Removes the check for root permissions.",
) {
compatibleWith("com.nousguide.android.orftvthek")
execute {
isDeviceRootedFingeprint.method.returnEarly(false)
}
}

View File

@@ -12,7 +12,7 @@ val skipAdsPatch = bytecodePatch(
name = "Skip ads",
description = "Automatically skips video stream ads.",
) {
compatibleWith("com.amazon.avod.thirdpartyclient"("3.0.403.257"))
compatibleWith("com.amazon.avod.thirdpartyclient"("3.0.412.2947"))
dependsOn(sharedExtensionPatch)

View File

@@ -0,0 +1,23 @@
package app.revanced.patches.primevideo.video.speed
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val playbackUserControlsInitializeFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC)
parameters("Lcom/amazon/avod/playbackclient/PlaybackInitializationContext;")
returns("V")
custom { method, classDef ->
method.name == "initialize" && classDef.type == "Lcom/amazon/avod/playbackclient/activity/feature/PlaybackUserControlsFeature;"
}
}
internal val playbackUserControlsPrepareForPlaybackFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC)
parameters("Lcom/amazon/avod/playbackclient/PlaybackContext;")
returns("V")
custom { method, classDef ->
method.name == "prepareForPlayback" &&
classDef.type == "Lcom/amazon/avod/playbackclient/activity/feature/PlaybackUserControlsFeature;"
}
}

View File

@@ -0,0 +1,56 @@
package app.revanced.patches.primevideo.video.speed
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.primevideo.misc.extension.sharedExtensionPatch
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.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch;"
val playbackSpeedPatch = bytecodePatch(
name = "Playback speed",
description = "Adds playback speed controls to the video player.",
) {
dependsOn(
sharedExtensionPatch,
)
compatibleWith(
"com.amazon.avod.thirdpartyclient"("3.0.412.2947")
)
execute {
playbackUserControlsInitializeFingerprint.method.apply {
val getIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT &&
getReference<FieldReference>()?.name == "mUserControls"
}
val getRegister = getInstruction<OneRegisterInstruction>(getIndex).registerA
addInstructions(
getIndex + 1,
"""
invoke-static { v$getRegister }, $EXTENSION_CLASS_DESCRIPTOR->initializeSpeedOverlay(Landroid/view/View;)V
"""
)
}
playbackUserControlsPrepareForPlaybackFingerprint.method.apply {
addInstructions(
0,
"""
invoke-virtual { p1 }, Lcom/amazon/avod/playbackclient/PlaybackContext;->getPlayer()Lcom/amazon/video/sdk/player/Player;
move-result-object v0
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setPlayer(Lcom/amazon/video/sdk/player/Player;)V
"""
)
}
}
}

View File

@@ -148,7 +148,7 @@ fun gmsCoreSupportPatch(
fun packageNameTransform(fromPackageName: String, toPackageName: String): (String) -> String? = { string ->
when (string) {
"$fromPackageName.SuggestionsProvider",
"$fromPackageName.SuggestionProvider",
"$fromPackageName.fileprovider",
-> string.replace(fromPackageName, toPackageName)

View File

@@ -11,6 +11,22 @@ internal val loadOrbitLibraryFingerprint = fingerprint {
strings("/liborbit-jni-spotify.so")
}
internal val setClientIdFingerprint = fingerprint {
parameters("Ljava/lang/String;")
custom { method, classDef ->
classDef.type == "Lcom/spotify/connectivity/ApplicationScopeConfiguration;"
&& method.name == "setClientId"
}
}
internal val setUserAgentFingerprint = fingerprint {
parameters("Ljava/lang/String;")
custom { method, classDef ->
classDef.type == "Lcom/spotify/connectivity/ApplicationScopeConfiguration;"
&& method.name == "setDefaultHTTPUserAgent"
}
}
internal val extensionFixConstantsFingerprint = fingerprint {
custom { _, classDef -> classDef.type == "Lapp/revanced/extension/spotify/misc/fix/Constants;" }
}

View File

@@ -1,5 +1,6 @@
package app.revanced.patches.spotify.misc.fix
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.intOption
@@ -72,7 +73,29 @@ val spoofClientPatch = bytecodePatch(
compatibleWith("com.spotify.music")
execute {
// region Spoof client.
val clientVersion = clientVersion!!
val hardwareMachine = hardwareMachine!!
val systemVersion = systemVersion!!
// region Spoof login request.
val version = clientVersion
.substringAfter('-')
.substringBeforeLast('.')
.substringBeforeLast('.')
setUserAgentFingerprint.method.addInstruction(
0,
"const-string p1, \"Spotify/$version iOS/$systemVersion ($hardwareMachine)\""
)
setClientIdFingerprint.method.addInstruction(
0, "const-string p1, \"58bd3c95768941ea9eb4350aaa033eb3\""
)
// endregion
// region Spoof client-token request.
loadOrbitLibraryFingerprint.method.addInstructions(
0,
@@ -83,9 +106,9 @@ val spoofClientPatch = bytecodePatch(
)
mapOf(
"getClientVersion" to clientVersion!!,
"getSystemVersion" to systemVersion!!,
"getHardwareMachine" to hardwareMachine!!
"getClientVersion" to clientVersion,
"getSystemVersion" to systemVersion,
"getHardwareMachine" to hardwareMachine
).forEach { (methodName, value) ->
extensionFixConstantsFingerprint.classDef.methods.single { it.name == methodName }.returnEarly(value)
}

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.interaction.doubletap
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.fingerprint
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -8,12 +9,14 @@ 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 com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/DisableChapterSkipDoubleTapPatch;"
"Lapp/revanced/extension/youtube/patches/DisableDoubleTapActionsPatch;"
@Suppress("unused")
val disableChapterSkipDoubleTapPatch = bytecodePatch(
val disableDoubleTapActionsPatch = bytecodePatch(
name = "Disable double tap actions",
description = "Adds an option to disable player double tap gestures.",
) {
@@ -25,9 +28,6 @@ val disableChapterSkipDoubleTapPatch = bytecodePatch(
compatibleWith(
"com.google.android.youtube"(
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
"20.13.41",
@@ -35,12 +35,28 @@ val disableChapterSkipDoubleTapPatch = bytecodePatch(
)
execute {
addResources("youtube", "interaction.doubletap.disableChapterSkipDoubleTapPatch")
addResources("youtube", "interaction.doubletap.disableDoubleTapActionsPatch")
PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_disable_chapter_skip_double_tap"),
)
val doubleTapInfoGetSeekSourceFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("Z")
returns(seekTypeEnumFingerprint.originalClassDef.type)
opcodes(
Opcode.IF_EQZ,
Opcode.SGET_OBJECT,
Opcode.RETURN_OBJECT,
Opcode.SGET_OBJECT,
Opcode.RETURN_OBJECT,
)
custom { _, classDef ->
classDef.fields.count() == 4
}
}
// Force isChapterSeek flag to false.
doubleTapInfoGetSeekSourceFingerprint.method.addInstructions(
0,
@@ -60,4 +76,9 @@ val disableChapterSkipDoubleTapPatch = bytecodePatch(
"""
)
}
}
}
@Deprecated("Patch was renamed", ReplaceWith("disableDoubleTapActionsPatch"))
val disableChapterSkipDoubleTapPatch = bytecodePatch {
dependsOn(disableDoubleTapActionsPatch)
}

View File

@@ -2,22 +2,13 @@ package app.revanced.patches.youtube.interaction.doubletap
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val doubleTapInfoGetSeekSourceFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("Z")
returns("L") // Enum SeekSource, but name obfuscated.
opcodes(
Opcode.IF_EQZ,
Opcode.SGET_OBJECT,
Opcode.RETURN_OBJECT,
Opcode.SGET_OBJECT,
Opcode.RETURN_OBJECT,
internal val seekTypeEnumFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR)
strings(
"SEEK_SOURCE_SEEK_TO_NEXT_CHAPTER",
"SEEK_SOURCE_SEEK_TO_PREVIOUS_CHAPTER"
)
custom { _, classDef ->
classDef.fields.count() == 4
}
}
internal val doubleTapInfoCtorFingerprint = fingerprint {

View File

@@ -6,7 +6,6 @@ import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
@@ -40,7 +39,10 @@ private val downloadsResourcePatch = resourcePatch {
preferences = setOf(
SwitchPreference("revanced_external_downloader"),
SwitchPreference("revanced_external_downloader_action_button"),
TextPreference("revanced_external_downloader_name", inputType = InputType.TEXT),
TextPreference(
"revanced_external_downloader_name",
tag = "app.revanced.extension.youtube.settings.preference.ExternalDownloaderPreference",
),
),
),
)

View File

@@ -22,6 +22,8 @@ import app.revanced.util.forEachLiteralValueInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import java.io.File
private val variants = arrayOf("light", "dark")
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/ChangeHeaderPatch;"
@@ -29,6 +31,17 @@ private val changeHeaderBytecodePatch = bytecodePatch {
dependsOn(resourceMappingPatch)
execute {
// Resources are not used during patching, but extension code uses these
// images so verify they exist.
arrayOf(
"yt_ringo2_wordmark_header",
"yt_ringo2_premium_wordmark_header"
).forEach { resource ->
variants.forEach { theme ->
resourceMappings["drawable", resource + "_" + theme]
}
}
arrayOf(
"ytWordmarkHeader",
"ytPremiumWordmarkHeader"
@@ -57,7 +70,6 @@ private val targetResourceDirectoryNames = mapOf(
"mdpi" to "129px x 48px"
).mapKeys { (dpi, _) -> "drawable-$dpi" }
private val variants = arrayOf("light", "dark")
/**
* Header logos built into this patch.

View File

@@ -8,6 +8,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.smali.ExternalLabel
@@ -200,9 +201,9 @@ val hideLayoutComponentsPatch = bytecodePatch(
key = "revanced_hide_filter_bar_screen",
preferences = setOf(
SwitchPreference("revanced_hide_filter_bar_feed_in_feed"),
SwitchPreference("revanced_hide_filter_bar_feed_in_history"),
SwitchPreference("revanced_hide_filter_bar_feed_in_search"),
SwitchPreference("revanced_hide_filter_bar_feed_in_related_videos"),
SwitchPreference("revanced_hide_filter_bar_feed_in_search"),
SwitchPreference("revanced_hide_filter_bar_feed_in_history"),
),
),
PreferenceScreenPreference(
@@ -222,7 +223,6 @@ val hideLayoutComponentsPatch = bytecodePatch(
SwitchPreference("revanced_hide_crowdfunding_box"),
SwitchPreference("revanced_hide_chips_shelf"),
SwitchPreference("revanced_hide_expandable_card"),
SwitchPreference("revanced_hide_feed_survey"),
SwitchPreference("revanced_hide_floating_microphone_button"),
SwitchPreference("revanced_hide_horizontal_shelves"),
SwitchPreference("revanced_hide_image_shelf"),
@@ -232,6 +232,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
SwitchPreference("revanced_hide_notify_me_button"),
SwitchPreference("revanced_hide_playables"),
SwitchPreference("revanced_hide_show_more_button"),
SwitchPreference("revanced_hide_surveys"),
SwitchPreference("revanced_hide_ticket_shelf"),
SwitchPreference("revanced_hide_video_recommendation_labels"),
SwitchPreference("revanced_hide_doodles"),
@@ -379,16 +380,13 @@ val hideLayoutComponentsPatch = bytecodePatch(
findInstructionIndicesReversedOrThrow {
getReference<MethodReference>()?.name == "setImageDrawable"
}.forEach { insertIndex ->
val register = getInstruction<FiveRegisterInstruction>(insertIndex).registerD
val drawableRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerD
val imageViewRegister = getInstruction<FiveRegisterInstruction>(insertIndex).registerC
addInstructionsWithLabels(
replaceInstruction(
insertIndex,
"""
invoke-static { v$register }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->hideYoodles(Landroid/graphics/drawable/Drawable;)Landroid/graphics/drawable/Drawable;
move-result-object v$register
if-eqz v$register, :hide
""",
ExternalLabel("hide", getInstruction(insertIndex + 1)),
"invoke-static { v$imageViewRegister, v$drawableRegister }, $LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR->" +
"setDoodleDrawable(Landroid/widget/ImageView;Landroid/graphics/drawable/Drawable;)V"
)
}
}

View File

@@ -99,7 +99,7 @@ val hideInfoCardsPatch = bytecodePatch(
)
// Info cards can also appear as Litho components.
val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/components/HideInfoCardsFilterPatch;"
val filterClassDescriptor = "Lapp/revanced/extension/youtube/patches/components/HideInfoCardsFilter;"
addLithoFilter(filterClassDescriptor)
}
}

View File

@@ -66,8 +66,8 @@ private val hideShortsComponentsResourcePatch = resourcePatch {
PreferenceScreen.SHORTS.addPreferences(
SwitchPreference("revanced_hide_shorts_home"),
SwitchPreference("revanced_hide_shorts_subscriptions"),
SwitchPreference("revanced_hide_shorts_search"),
SwitchPreference("revanced_hide_shorts_subscriptions"),
SwitchPreference("revanced_hide_shorts_history"),
PreferenceScreenPreference(

View File

@@ -43,7 +43,7 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/ReturnYouTubeDislikePatch;"
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilterPatch;"
"Lapp/revanced/extension/youtube/patches/components/ReturnYouTubeDislikeFilter;"
val returnYouTubeDislikePatch = bytecodePatch(
name = "Return YouTube Dislike",

View File

@@ -156,7 +156,7 @@ val lithoFilterPatch = bytecodePatch(
move-object/from16 v$freeRegister, p2
iget-object v$identifierRegister, v$freeRegister, $conversionContextIdentifierField
iget-object v$pathRegister, v$freeRegister, $conversionContextPathBuilderField
invoke-static { v$identifierRegister, v$pathRegister }, $EXTENSION_CLASS_DESCRIPTOR->shouldFilter(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
invoke-static { v$identifierRegister, v$pathRegister }, $EXTENSION_CLASS_DESCRIPTOR->isFiltered(Ljava/lang/String;Ljava/lang/StringBuilder;)Z
move-result v$freeRegister
if-eqz v$freeRegister, :unfiltered

View File

@@ -11,7 +11,6 @@ val recyclerViewTreeHookPatch = bytecodePatch {
dependsOn(sharedExtensionPatch)
execute {
recyclerViewTreeObserverFingerprint.method.apply {
val insertIndex = recyclerViewTreeObserverFingerprint.patternMatch!!.startIndex + 1
val recyclerViewParameter = 2

View File

@@ -12,31 +12,15 @@ import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
import app.revanced.patches.shared.misc.mapping.resourceMappings
import app.revanced.patches.shared.misc.settings.overrideThemeColors
import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
import app.revanced.patches.shared.misc.settings.preference.InputType
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
import app.revanced.patches.shared.misc.settings.preference.*
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.shared.misc.settings.preference.TextPreference
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.playbackspeed.fixPlaybackSpeedWhilePlayingPatch
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.addInstructionsAtControlFlowLabel
import app.revanced.util.copyResources
import app.revanced.util.copyXmlNode
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.inputStreamFromBundledResource
import app.revanced.util.insertLiteralOverride
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
@@ -152,15 +136,24 @@ private val settingsResourcePatch = resourcePatch {
}
}
// Modify the manifest and add a data intent filter to the LicenseActivity.
// Some devices freak out if undeclared data is passed to an intent,
// and this change appears to fix the issue.
// Modify the manifest to enhance LicenseActivity behavior:
// 1. Add a data intent filter with MIME type "text/plain".
// Some devices crash if undeclared data is passed to an intent,
// and this change appears to fix the issue.
// 2. Add android:configChanges="orientation|screenSize|keyboardHidden".
// This prevents the activity from being recreated on configuration changes
// (e.g., screen rotation), preserving its current state and fragment.
document("AndroidManifest.xml").use { document ->
val licenseElement = document.childNodes.findElementByAttributeValueOrThrow(
"android:name",
"com.google.android.libraries.social.licenses.LicenseActivity",
)
licenseElement.setAttribute(
"android:configChanges",
"orientation|screenSize|keyboardHidden"
)
val mimeType = document.createElement("data")
mimeType.setAttribute("android:mimeType", "text/plain")
@@ -242,9 +235,9 @@ val settingsPatch = bytecodePatch(
methods.removeIf { it.name != "onCreate" && !MethodUtil.isConstructor(it) }
}
// Add context override to force a specific settings language.
licenseActivityOnCreateFingerprint.classDef.apply {
val attachBaseContext = ImmutableMethod(
// Add attachBaseContext method to override the context for setting a specific language.
ImmutableMethod(
type,
"attachBaseContext",
listOf(ImmutableMethodParameter("Landroid/content/Context;", null, null)),
@@ -262,9 +255,50 @@ val settingsPatch = bytecodePatch(
return-void
"""
)
}
}.let(methods::add)
methods.add(attachBaseContext)
// Add onBackPressed method to handle back button presses, delegating to SearchViewController.
ImmutableMethod(
type,
"onBackPressed",
emptyList(),
"V",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(3),
).toMutable().apply {
addInstructions(
"""
invoke-static {}, Lapp/revanced/extension/youtube/settings/SearchViewController;->handleBackPress()Z
move-result v0
if-nez v0, :search_handled
invoke-virtual { p0 }, Landroid/app/Activity;->finish()V
:search_handled
return-void
"""
)
}.let(methods::add)
// Add onConfigurationChanged method to handle configuration changes (e.g., screen orientation).
ImmutableMethod(
type,
"onConfigurationChanged",
listOf(ImmutableMethodParameter("Landroid/content/res/Configuration;", null, null)),
"V",
AccessFlags.PUBLIC.value,
null,
null,
MutableMethodImplementation(3)
).toMutable().apply {
addInstructions(
"""
invoke-super { p0, p1 }, Landroid/app/Activity;->onConfigurationChanged(Landroid/content/res/Configuration;)V
invoke-static { p0, p1 }, $EXTENSION_CLASS_DESCRIPTOR->handleConfigurationChanged(Landroid/app/Activity;Landroid/content/res/Configuration;)V
return-void
"""
)
}.let(methods::add)
}
// Update shared dark mode status based on YT theme.
@@ -338,20 +372,18 @@ object PreferenceScreen : BasePreferenceScreen() {
icon = "@drawable/revanced_settings_screen_05_player",
layout = "@layout/preference_with_icon",
)
val SHORTS = Screen(
key = "revanced_settings_screen_06_shorts",
summaryKey = null,
icon = "@drawable/revanced_settings_screen_06_shorts",
layout = "@layout/preference_with_icon",
)
val SEEKBAR = Screen(
key = "revanced_settings_screen_07_seekbar",
summaryKey = null,
icon = "@drawable/revanced_settings_screen_07_seekbar",
layout = "@layout/preference_with_icon",
)
)
val SWIPE_CONTROLS = Screen(
key = "revanced_settings_screen_08_swipe_controls",
summaryKey = null,

View File

@@ -121,7 +121,7 @@ internal val subtitleButtonControllerFingerprint = fingerprint {
)
}
internal val newVideoQualityChangedFingerprint = fingerprint {
internal val videoQualityChangedFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
parameters("L")

View File

@@ -1,23 +1,27 @@
package app.revanced.patches.youtube.video.audio
import app.revanced.patcher.fingerprint
import app.revanced.util.containsLiteralInstruction
import com.android.tools.smali.dexlib2.AccessFlags
internal val streamingModelBuilderFingerprint = fingerprint {
internal val formatStreamModelToStringFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
strings("vprng")
returns("Ljava/lang/String;")
custom { method, classDef ->
method.name == "toString" && classDef.type ==
"Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;"
}
}
internal val menuItemAudioTrackFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("L")
returns("V")
strings("menu_item_audio_track")
internal const val AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG = 45666189L
internal val selectAudioStreamFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("L")
custom { method, _ ->
method.parameters.size > 2 // Method has a large number of parameters and may change.
&& method.parameters[1].type == "Lcom/google/android/libraries/youtube/innertube/model/media/PlayerConfigModel;"
&& method.containsLiteralInstruction(AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG)
}
}
internal val audioStreamingTypeSelector = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returns("L")
strings("raw") // String is not unique
}

View File

@@ -5,22 +5,22 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
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.playservice.is_20_07_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.getReference
import app.revanced.util.findMethodFromToString
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertLiteralOverride
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.Method
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
@@ -37,6 +37,7 @@ val forceOriginalAudioPatch = bytecodePatch(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
versionCheckPatch
)
compatibleWith(
@@ -60,29 +61,25 @@ val forceOriginalAudioPatch = bytecodePatch(
)
)
fun Method.firstFormatStreamingModelCall(
returnType: String = "Ljava/lang/String;"
): MutableMethod {
val audioTrackIdIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/google/android/libraries/youtube/innertube/model/media/FormatStreamModel;"
&& reference.returnType == returnType
}
return navigate(this).to(audioTrackIdIndex).stop()
// Disable feature flag that ignores the default track flag
// and instead overrides to the user region language.
if (is_20_07_or_greater) {
selectAudioStreamFingerprint.method.insertLiteralOverride(
AUDIO_STREAM_IGNORE_DEFAULT_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->ignoreDefaultAudioStream(Z)Z"
)
}
// Accessor methods of FormatStreamModel have no string constants and
// opcodes are identical to other methods in the same class,
// so must walk from another class that use the methods.
val isDefaultMethod = streamingModelBuilderFingerprint.originalMethod.firstFormatStreamingModelCall("Z")
val audioTrackIdMethod = menuItemAudioTrackFingerprint.originalMethod.firstFormatStreamingModelCall()
val audioTrackDisplayNameMethod = audioStreamingTypeSelector.originalMethod.firstFormatStreamingModelCall()
val formatStreamModelClass = proxy(classes.first {
it.type == audioTrackIdMethod.definingClass
}).mutableClass
val isDefaultAudioTrackMethod = formatStreamModelToStringFingerprint.originalMethod
.findMethodFromToString("isDefaultAudioTrack=")
val audioTrackDisplayNameMethod = formatStreamModelToStringFingerprint.originalMethod
.findMethodFromToString("audioTrackDisplayName=")
val audioTrackIdMethod = formatStreamModelToStringFingerprint.originalMethod
.findMethodFromToString("audioTrackId=")
formatStreamModelClass.apply {
proxy(classes.first {
it.type == audioTrackIdMethod.definingClass
}).mutableClass.apply {
// Add a new field to store the override.
val helperFieldName = "isDefaultAudioTrackOverride"
fields.add(
@@ -103,7 +100,7 @@ val forceOriginalAudioPatch = bytecodePatch(
// Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed.
val helperMethodClass = type
val helperMethodName = "extension_isDefaultAudioTrack"
val helperMethodName = "patch_isDefaultAudioTrack"
val helperMethod = ImmutableMethod(
helperMethodClass,
helperMethodName,
@@ -143,7 +140,7 @@ val forceOriginalAudioPatch = bytecodePatch(
methods.add(helperMethod)
// Modify isDefaultAudioTrack() to call extension helper method.
isDefaultMethod.apply {
isDefaultAudioTrackMethod.apply {
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN)
val register = getInstruction<OneRegisterInstruction>(index).registerA

View File

@@ -1,7 +1,7 @@
package app.revanced.patches.youtube.video.information
import app.revanced.patcher.fingerprint
import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint
import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -110,7 +110,7 @@ internal val seekRelativeFingerprint = fingerprint {
}
/**
* Resolves with the class found in [newVideoQualityChangedFingerprint].
* Resolves with the class found in [videoQualityChangedFingerprint].
*/
internal val playbackSpeedMenuSpeedChangedFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)

View File

@@ -9,7 +9,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patcher.util.smali.toInstructions
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint
import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint
import app.revanced.patches.youtube.video.playerresponse.Hook
import app.revanced.patches.youtube.video.playerresponse.addPlayerResponseMethodHook
import app.revanced.patches.youtube.video.playerresponse.playerResponseMethodHookPatch
@@ -263,7 +263,7 @@ val videoInformationPatch = bytecodePatch(
// Handle new playback speed menu.
playbackSpeedMenuSpeedChangedFingerprint.match(
newVideoQualityChangedFingerprint.originalClassDef,
videoQualityChangedFingerprint.originalClassDef,
).method.apply {
val index = indexOfFirstInstructionOrThrow(Opcode.IGET)

View File

@@ -68,7 +68,6 @@ internal val advancedVideoQualityMenuPatch = bytecodePatch {
// region Patch for the old type of the video quality menu.
// Used for regular videos when spoofing to old app version,
// and for the Shorts quality flyout on newer app versions.
videoQualityMenuViewInflateFingerprint.let {
it.method.apply {
val checkCastIndex = it.patternMatch!!.endIndex
@@ -77,7 +76,7 @@ internal val advancedVideoQualityMenuPatch = bytecodePatch {
addInstruction(
checkCastIndex + 1,
"invoke-static { v$listViewRegister }, $EXTENSION_CLASS_DESCRIPTOR->" +
"showAdvancedVideoQualityMenu(Landroid/widget/ListView;)V",
"addVideoQualityListMenuListener(Landroid/widget/ListView;)V",
)
}
}

View File

@@ -5,10 +5,25 @@ import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal const val YOUTUBE_VIDEO_QUALITY_CLASS_TYPE = "Lcom/google/android/libraries/youtube/innertube/model/media/VideoQuality;"
internal val videoQualityFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters(
"I", // Resolution.
"Ljava/lang/String;", // Human readable resolution: "480p", "1080p Premium", etc
"Z",
"L"
)
custom { _, classDef ->
classDef.type == YOUTUBE_VIDEO_QUALITY_CLASS_TYPE
}
}
/**
* Matches with the class found in [videoQualitySetterFingerprint].
*/
internal val setQualityByIndexMethodClassFieldReferenceFingerprint = fingerprint {
internal val setVideoQualityFingerprint = fingerprint {
returns("V")
parameters("L")
opcodes(
@@ -23,6 +38,22 @@ internal val videoQualityItemOnClickParentFingerprint = fingerprint {
strings("VIDEO_QUALITIES_MENU_BOTTOM_SHEET_FRAGMENT")
}
/**
* Resolves to class found in [videoQualityItemOnClickFingerprint].
*/
internal val videoQualityItemOnClickFingerprint = fingerprint {
returns("V")
parameters(
"Landroid/widget/AdapterView;",
"Landroid/view/View;",
"I",
"J"
)
custom { method, _ ->
method.name == "onItemClick"
}
}
internal val videoQualitySetterFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
@@ -37,7 +68,6 @@ internal val videoQualitySetterFingerprint = fingerprint {
strings("menu_item_video_quality")
}
internal val videoQualityMenuOptionsFingerprint = fingerprint {
accessFlags(AccessFlags.STATIC)
returns("[L")

View File

@@ -3,8 +3,8 @@ package app.revanced.patches.youtube.video.quality
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.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
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
@@ -12,15 +12,21 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.newVideoQualityChangedFingerprint
import app.revanced.patches.youtube.shared.videoQualityChangedFingerprint
import app.revanced.patches.youtube.video.information.onCreateHook
import app.revanced.patches.youtube.video.information.videoInformationPatch
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch;"
private const val EXTENSION_VIDEO_QUALITY_MENU_INTERFACE =
"Lapp/revanced/extension/youtube/patches/playback/quality/RememberVideoQualityPatch\$VideoQualityMenuInterface;"
val rememberVideoQualityPatch = bytecodePatch {
dependsOn(
@@ -61,81 +67,152 @@ val rememberVideoQualityPatch = bytecodePatch {
SwitchPreference("revanced_remember_video_quality_last_selected_toast")
))
/*
* The following code works by hooking the method which is called when the user selects a video quality
* to remember the last selected video quality.
*
* It also hooks the method which is called when the video quality to set is determined.
* Conveniently, at this point the video quality is overridden to the remembered playback speed.
*/
onCreateHook(EXTENSION_CLASS_DESCRIPTOR, "newVideoStarted")
videoQualityFingerprint.let {
// Fix bad data used by YouTube.
it.method.addInstructions(
0,
"""
invoke-static { p2, p1 }, $EXTENSION_CLASS_DESCRIPTOR->fixVideoQualityResolution(Ljava/lang/String;I)I
move-result p1
"""
)
// Add methods to access obfuscated quality fields.
it.classDef.apply {
methods.add(
ImmutableMethod(
type,
"patch_getQualityName",
listOf(),
"Ljava/lang/String;",
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
null,
null,
MutableMethodImplementation(2),
).toMutable().apply {
// Only one string field.
val qualityNameField = fields.single { field ->
field.type == "Ljava/lang/String;"
}
addInstructions(
0,
"""
iget-object v0, p0, $qualityNameField
return-object v0
"""
)
}
)
methods.add(
ImmutableMethod(
type,
"patch_getResolution",
listOf(),
"I",
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
null,
null,
MutableMethodImplementation(2),
).toMutable().apply {
val resolutionField = fields.single { field ->
field.type == "I"
}
addInstructions(
0,
"""
iget v0, p0, $resolutionField
return v0
"""
)
}
)
}
}
// Inject a call to set the remembered quality once a video loads.
setQualityByIndexMethodClassFieldReferenceFingerprint.match(
videoQualitySetterFingerprint.originalClassDef,
setVideoQualityFingerprint.match(
videoQualitySetterFingerprint.originalClassDef
).let { match ->
// This instruction refers to the field with the type that contains the setQualityByIndex method.
// This instruction refers to the field with the type that contains the setQuality method.
val instructions = match.method.implementation!!.instructions
val getOnItemClickListenerClassReference =
val onItemClickListenerClassReference =
(instructions.elementAt(0) as ReferenceInstruction).reference
val getSetQualityByIndexMethodClassFieldReference =
(instructions.elementAt(1) as ReferenceInstruction).reference
val setQualityFieldReference =
((instructions.elementAt(1) as ReferenceInstruction).reference) as FieldReference
val setQualityByIndexMethodClassFieldReference =
getSetQualityByIndexMethodClassFieldReference as FieldReference
proxy(
classes.find { classDef ->
classDef.type == setQualityFieldReference.type
}!!
).mutableClass.apply {
// Add interface and helper methods to allow extension code to call obfuscated methods.
interfaces.add(EXTENSION_VIDEO_QUALITY_MENU_INTERFACE)
val setQualityByIndexMethodClass = classes
.find { classDef -> classDef.type == setQualityByIndexMethodClassFieldReference.type }!!
methods.add(
ImmutableMethod(
type,
"patch_setQuality",
listOf(
ImmutableMethodParameter(YOUTUBE_VIDEO_QUALITY_CLASS_TYPE, null, null)
),
"V",
AccessFlags.PUBLIC.value or AccessFlags.FINAL.value,
null,
null,
MutableMethodImplementation(2),
).toMutable().apply {
val setQualityMenuIndexMethod = methods.single { method ->
method.parameterTypes.firstOrNull() == YOUTUBE_VIDEO_QUALITY_CLASS_TYPE
}
// Get the name of the setQualityByIndex method.
val setQualityByIndexMethod = setQualityByIndexMethodClass.methods
.find { method -> method.parameterTypes.first() == "I" }
?: throw PatchException("Could not find setQualityByIndex method")
addInstructions(
0,
"""
invoke-virtual { p0, p1 }, $setQualityMenuIndexMethod
return-void
"""
)
}
)
}
videoQualitySetterFingerprint.method.addInstructions(
0,
"""
# Get the object instance to invoke the setQualityByIndex method on.
iget-object v0, p0, $getOnItemClickListenerClassReference
iget-object v0, v0, $getSetQualityByIndexMethodClassFieldReference
# Get object instance to invoke setQuality method.
iget-object v0, p0, $onItemClickListenerClassReference
iget-object v0, v0, $setQualityFieldReference
# Get the method name.
const-string v1, "${setQualityByIndexMethod.name}"
# Set the quality.
# The first parameter is the array list of video qualities.
# The second parameter is the index of the selected quality.
# The register v0 stores the object instance to invoke the setQualityByIndex method on.
# The register v1 stores the name of the setQualityByIndex method.
invoke-static { p1, p2, v0, v1 }, $EXTENSION_CLASS_DESCRIPTOR->setVideoQuality([Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/String;)I
invoke-static { p1, v0, p2 }, $EXTENSION_CLASS_DESCRIPTOR->setVideoQuality([$YOUTUBE_VIDEO_QUALITY_CLASS_TYPE${EXTENSION_VIDEO_QUALITY_MENU_INTERFACE}I)I
move-result p2
""",
"""
)
}
// Inject a call to remember the selected quality.
videoQualityItemOnClickParentFingerprint.classDef.methods.find { it.name == "onItemClick" }
?.apply {
val listItemIndexParameter = 3
// Inject a call to remember the selected quality for Shorts.
videoQualityItemOnClickFingerprint.match(
videoQualityItemOnClickParentFingerprint.classDef
).method.addInstruction(
0,
"invoke-static { p3 }, $EXTENSION_CLASS_DESCRIPTOR->userChangedShortsQuality(I)V"
)
// Inject a call to remember the user selected quality for regular videos.
videoQualityChangedFingerprint.let {
it.method.apply {
val index = it.patternMatch!!.startIndex
val register = getInstruction<TwoRegisterInstruction>(index).registerA
addInstruction(
0,
"invoke-static { p$listItemIndexParameter }, " +
"$EXTENSION_CLASS_DESCRIPTOR->userChangedQuality(I)V",
index + 1,
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->userChangedQuality(I)V",
)
} ?: throw PatchException("Failed to find onItemClick method")
// Remember video quality if not using old layout menu.
newVideoQualityChangedFingerprint.method.apply {
val index = newVideoQualityChangedFingerprint.patternMatch!!.startIndex
val qualityRegister = getInstruction<TwoRegisterInstruction>(index).registerA
addInstruction(
index + 1,
"invoke-static { v$qualityRegister }, " +
"$EXTENSION_CLASS_DESCRIPTOR->userChangedQualityInNewFlyout(I)V",
)
}
}
}
}

View File

@@ -5,6 +5,7 @@ import app.revanced.patches.shared.misc.settings.preference.BasePreference
import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.video.quality.button.videoQualityButtonPatch
/**
* Video quality settings. Used to organize all speed related settings together.
@@ -19,6 +20,7 @@ val videoQualityPatch = bytecodePatch(
dependsOn(
rememberVideoQualityPatch,
advancedVideoQualityMenuPatch,
videoQualityButtonPatch,
)
compatibleWith(

View File

@@ -0,0 +1,64 @@
package app.revanced.patches.youtube.video.quality.button
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
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.playercontrols.*
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.video.quality.rememberVideoQualityPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
private val videoQualityButtonResourcePatch = resourcePatch {
dependsOn(playerControlsResourcePatch)
execute {
copyResources(
"qualitybutton",
ResourceGroup(
"drawable",
"revanced_video_quality_dialog_button_ld.xml",
"revanced_video_quality_dialog_button_sd.xml",
"revanced_video_quality_dialog_button_hd.xml",
"revanced_video_quality_dialog_button_fhd.xml",
"revanced_video_quality_dialog_button_fhd_plus.xml",
"revanced_video_quality_dialog_button_qhd.xml",
"revanced_video_quality_dialog_button_4k.xml",
"revanced_video_quality_dialog_button_unknown.xml",
),
)
addBottomControl("qualitybutton")
}
}
private const val QUALITY_BUTTON_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/videoplayer/VideoQualityDialogButton;"
val videoQualityButtonPatch = bytecodePatch(
description = "Adds the option to display video quality dialog button in the video player.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
rememberVideoQualityPatch,
videoQualityButtonResourcePatch,
playerControlsPatch,
)
execute {
addResources("youtube", "video.quality.button.videoQualityButtonPatch")
PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_video_quality_dialog_button"),
)
initializeBottomControl(QUALITY_BUTTON_CLASS_DESCRIPTOR)
injectVisibilityCheckCall(QUALITY_BUTTON_CLASS_DESCRIPTOR)
}
}

View File

@@ -1,11 +1,19 @@
package app.revanced.patches.youtube.video.speed.custom
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
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.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
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
@@ -18,18 +26,34 @@ import app.revanced.patches.youtube.misc.recyclerviewtree.hook.addRecyclerViewTr
import app.revanced.patches.youtube.misc.recyclerviewtree.hook.recyclerViewTreeHookPatch
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.video.speed.settingsMenuVideoSpeedGroup
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstruction
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
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
private const val FILTER_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilterPatch;"
"Lapp/revanced/extension/youtube/patches/components/PlaybackSpeedMenuFilter;"
private const val EXTENSION_CLASS_DESCRIPTOR =
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/playback/speed/CustomPlaybackSpeedPatch;"
internal var speedUnavailableId = -1L
private set
private val customPlaybackSpeedResourcePatch = resourcePatch {
dependsOn(resourceMappingPatch)
execute {
speedUnavailableId = resourceMappings["string", "varispeed_unavailable_message"]
}
}
internal val customPlaybackSpeedPatch = bytecodePatch(
description = "Adds custom playback speed options.",
) {
@@ -39,7 +63,8 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
addResourcesPatch,
lithoFilterPatch,
versionCheckPatch,
recyclerViewTreeHookPatch
recyclerViewTreeHookPatch,
customPlaybackSpeedResourcePatch
)
execute {
@@ -48,6 +73,7 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
settingsMenuVideoSpeedGroup.addAll(
listOf(
SwitchPreference("revanced_custom_speed_menu"),
SwitchPreference("revanced_restore_old_speed_menu"),
TextPreference(
"revanced_custom_playback_speeds",
inputType = InputType.TEXT_MULTI_LINE
@@ -77,15 +103,88 @@ internal val customPlaybackSpeedPatch = bytecodePatch(
replaceInstruction(limitMaxIndex, "const/high16 v$limitMaxRegister, 8.0f")
}
// Replace the speeds float array with custom speeds.
// These speeds are used if the speed menu is immediately opened after a video is opened.
speedArrayGeneratorFingerprint.method.apply {
val sizeCallIndex = indexOfFirstInstructionOrThrow { getReference<MethodReference>()?.name == "size" }
val sizeCallResultRegister = getInstruction<OneRegisterInstruction>(sizeCallIndex + 1).registerA
replaceInstruction(sizeCallIndex + 1, "const/4 v$sizeCallResultRegister, 0x0")
val arrayLengthConstIndex = indexOfFirstLiteralInstructionOrThrow(7)
val arrayLengthConstDestination = getInstruction<OneRegisterInstruction>(arrayLengthConstIndex).registerA
val playbackSpeedsArrayType = "$EXTENSION_CLASS_DESCRIPTOR->customPlaybackSpeeds:[F"
addInstructions(
arrayLengthConstIndex + 1,
"""
sget-object v$arrayLengthConstDestination, $playbackSpeedsArrayType
array-length v$arrayLengthConstDestination, v$arrayLengthConstDestination
""",
)
val originalArrayFetchIndex = indexOfFirstInstructionOrThrow {
val reference = getReference<FieldReference>()
reference?.type == "[F" && reference.definingClass.endsWith("/PlayerConfigModel;")
}
val originalArrayFetchDestination =
getInstruction<OneRegisterInstruction>(originalArrayFetchIndex).registerA
replaceInstruction(
originalArrayFetchIndex,
"sget-object v$originalArrayFetchDestination, $playbackSpeedsArrayType",
)
}
// region Force old video quality menu.
// Add a static INSTANCE field to the class.
// This is later used to call "showOldPlaybackSpeedMenu" on the instance.
val instanceField = ImmutableField(
getOldPlaybackSpeedsFingerprint.originalClassDef.type,
"INSTANCE",
getOldPlaybackSpeedsFingerprint.originalClassDef.type,
AccessFlags.PUBLIC.value or AccessFlags.STATIC.value,
null,
null,
null,
).toMutable()
getOldPlaybackSpeedsFingerprint.classDef.staticFields.add(instanceField)
// Set the INSTANCE field to the instance of the class.
// In order to prevent a conflict with another patch, add the instruction at index 1.
getOldPlaybackSpeedsFingerprint.method.addInstruction(1, "sput-object p0, $instanceField")
// Get the "showOldPlaybackSpeedMenu" method.
// This is later called on the field INSTANCE.
val showOldPlaybackSpeedMenuMethod = showOldPlaybackSpeedMenuFingerprint.match(
getOldPlaybackSpeedsFingerprint.classDef,
).method
// Insert the call to the "showOldPlaybackSpeedMenu" method on the field INSTANCE.
showOldPlaybackSpeedMenuExtensionFingerprint.method.apply {
addInstructionsWithLabels(
instructions.lastIndex,
"""
sget-object v0, $instanceField
if-nez v0, :not_null
return-void
:not_null
invoke-virtual { v0 }, $showOldPlaybackSpeedMenuMethod
"""
)
}
// endregion
// Close the unpatched playback dialog and show the modern custom dialog.
addRecyclerViewTreeHook(EXTENSION_CLASS_DESCRIPTOR)
// Required to check if the playback speed menu is currently shown.
addLithoFilter(FILTER_CLASS_DESCRIPTOR)
// endregion
// region Custom tap and hold 2x speed.
if (is_19_25_or_greater) {

View File

@@ -3,10 +3,33 @@ package app.revanced.patches.youtube.video.speed.custom
import app.revanced.patcher.fingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.StringReference
internal val getOldPlaybackSpeedsFingerprint = fingerprint {
parameters("[L", "I")
strings("menu_item_playback_speed")
}
internal val showOldPlaybackSpeedMenuFingerprint = fingerprint {
literal { speedUnavailableId }
}
internal val showOldPlaybackSpeedMenuExtensionFingerprint = fingerprint {
custom { method, classDef ->
method.name == "showOldPlaybackSpeedMenu" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}
internal val speedArrayGeneratorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("[L")
parameters("Lcom/google/android/libraries/youtube/innertube/model/player/PlayerResponseModel;")
strings("0.0#")
}
internal val speedLimiterFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")

View File

@@ -32,7 +32,10 @@ import com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstructio
import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.Reference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
import com.android.tools.smali.dexlib2.immutable.ImmutableField
import com.android.tools.smali.dexlib2.util.MethodUtil
import java.util.EnumSet
@@ -171,6 +174,79 @@ internal val Instruction.isBranchInstruction: Boolean
internal val Instruction.isReturnInstruction: Boolean
get() = this.opcode in returnOpcodes
/**
* Find the instruction index used for a toString() StringBuilder write of a given String name.
*
* @param fieldName The name of the field to find. Partial matches are allowed.
*/
private fun Method.findInstructionIndexFromToString(fieldName: String) : Int {
val stringIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
reference?.string?.contains(fieldName) == true
}
if (stringIndex < 0) {
throw IllegalArgumentException("Could not find usage of string: '$fieldName'")
}
val stringRegister = getInstruction<OneRegisterInstruction>(stringIndex).registerA
// Find use of the string with a StringBuilder.
val stringUsageIndex = indexOfFirstInstruction(stringIndex) {
val reference = getReference<MethodReference>()
reference?.definingClass == "Ljava/lang/StringBuilder;" &&
(this as? FiveRegisterInstruction)?.registerD == stringRegister
}
if (stringUsageIndex < 0) {
throw IllegalArgumentException("Could not find StringBuilder usage in: $this")
}
// Find the next usage of StringBuilder, which should be the desired field.
val fieldUsageIndex = indexOfFirstInstruction(stringUsageIndex + 1) {
val reference = getReference<MethodReference>()
reference?.definingClass == "Ljava/lang/StringBuilder;" && reference.name == "append"
}
if (fieldUsageIndex < 0) {
// Should never happen.
throw IllegalArgumentException("Could not find StringBuilder append usage in: $this")
}
val fieldUsageRegister = getInstruction<FiveRegisterInstruction>(fieldUsageIndex).registerD
// Look backwards up the method to find the instruction that sets the register.
var fieldSetIndex = indexOfFirstInstructionReversedOrThrow(fieldUsageIndex - 1) {
fieldUsageRegister == writeRegister
}
// If the field is a method call, then adjust from MOVE_RESULT to the method call.
val fieldSetOpcode = getInstruction(fieldSetIndex).opcode
if (fieldSetOpcode == MOVE_RESULT ||
fieldSetOpcode == MOVE_RESULT_WIDE ||
fieldSetOpcode == MOVE_RESULT_OBJECT) {
fieldSetIndex--
}
return fieldSetIndex
}
/**
* Find the method used for a toString() StringBuilder write of a given String name.
*
* @param fieldName The name of the field to find. Partial matches are allowed.
*/
context(BytecodePatchContext)
internal fun Method.findMethodFromToString(fieldName: String) : MutableMethod {
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
return navigate(this).to(methodUsageIndex).stop()
}
/**
* Find the field used for a toString() StringBuilder write of a given String name.
*
* @param fieldName The name of the field to find. Partial matches are allowed.
*/
internal fun Method.findFieldFromToString(fieldName: String) : FieldReference {
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
return getInstruction<ReferenceInstruction>(methodUsageIndex).getReference<FieldReference>()!!
}
/**
* Adds public [AccessFlags] and removes private and protected flags (if present).
*/
@@ -594,7 +670,7 @@ fun Method.indexOfFirstInstructionReversed(targetOpcode: Opcode): Int = indexOfF
/**
* Get the index of matching instruction,
* starting from and [startIndex] and searching down.
* starting from [startIndex] and searching down.
*
* @param startIndex Optional starting index to search down from. Searching includes the start index.
* @return The index of the instruction.
@@ -617,7 +693,7 @@ fun Method.indexOfFirstInstructionReversedOrThrow(targetOpcode: Opcode): Int = i
/**
* Get the index of matching instruction,
* starting from and [startIndex] and searching down.
* starting from [startIndex] and searching down.
*
* @param startIndex Optional starting index to search down from. Searching includes the start index.
* @return The index of the instruction.

View File

@@ -40,7 +40,7 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<!-- '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. -->
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<!-- 'People also watched' and 'You might also like' should be translated using the same localized wording YouTube displays. -->
@@ -58,7 +58,6 @@ Second \"item\" text"</string>
</patch>
<patch id="ad.general.hideAdsResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<!-- 'View products' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="ad.getpremium.hideGetPremiumPatch">
</patch>
@@ -68,7 +67,7 @@ Second \"item\" text"</string>
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
</patch>
<patch id="interaction.doubletap.disableChapterSkipDoubleTapPatch">
<patch id="interaction.doubletap.disableDoubleTapActionsPatch">
</patch>
<patch id="interaction.downloads.downloadsResourcePatch">
<!-- 'Download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title'. -->
@@ -128,6 +127,7 @@ Second \"item\" text"</string>
<patch id="layout.hide.rollingnumber.disableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.hideSeekbarPatch">
<!-- Seekbar shown inside video thumbnails found the home/feed/search/history. The seekbar shows the prior watch progress when the video was last open. -->
</patch>
<patch id="layout.hide.shorts.hideShortsComponentsResourcePatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the Home tab. -->
@@ -170,8 +170,6 @@ Second \"item\" text"</string>
<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. -->
</patch>
<patch id="layout.startpage.changeStartPagePatch">
</patch>
@@ -224,6 +222,8 @@ Second \"item\" text"</string>
</patch>
<patch id="video.speed.button.playbackSpeedButtonPatch">
</patch>
<patch id="video.quality.button.videoQualityButtonPatch">
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">

View File

@@ -40,7 +40,7 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<!-- '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. -->
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<!-- 'People also watched' and 'You might also like' should be translated using the same localized wording YouTube displays. -->
@@ -58,7 +58,6 @@ Second \"item\" text"</string>
</patch>
<patch id="ad.general.hideAdsResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<!-- 'View products' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="ad.getpremium.hideGetPremiumPatch">
</patch>
@@ -68,7 +67,7 @@ Second \"item\" text"</string>
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
</patch>
<patch id="interaction.doubletap.disableChapterSkipDoubleTapPatch">
<patch id="interaction.doubletap.disableDoubleTapActionsPatch">
</patch>
<patch id="interaction.downloads.downloadsResourcePatch">
<!-- 'Download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title'. -->
@@ -128,6 +127,7 @@ Second \"item\" text"</string>
<patch id="layout.hide.rollingnumber.disableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.hideSeekbarPatch">
<!-- Seekbar shown inside video thumbnails found the home/feed/search/history. The seekbar shows the prior watch progress when the video was last open. -->
</patch>
<patch id="layout.hide.shorts.hideShortsComponentsResourcePatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the Home tab. -->
@@ -170,8 +170,6 @@ Second \"item\" text"</string>
<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. -->
</patch>
<patch id="layout.startpage.changeStartPagePatch">
</patch>
@@ -224,6 +222,8 @@ Second \"item\" text"</string>
</patch>
<patch id="video.speed.button.playbackSpeedButtonPatch">
</patch>
<patch id="video.quality.button.videoQualityButtonPatch">
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">

View File

@@ -137,7 +137,7 @@ Second \"item\" text"</string>
<string name="revanced_debug_logs_disabled">ØĒŲ… ØĒØšØˇŲŠŲ„ ØĒØŗØŦŲŠŲ„Ø§ØĒ ØĒØĩØ­ŲŠØ­ Ø§Ų„ØŖØŽØˇØ§ØĄ</string>
<string name="revanced_debug_logs_none_found">Ų„Ų… ؊ØĒŲ… Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ ØŗØŦŲ„Ø§ØĒ</string>
<string name="revanced_debug_logs_copied_to_clipboard">ØĒŲ… Ų†ØŗØŽ Ø§Ų„ØŗØŦŲ„Ø§ØĒ</string>
<string name="revanced_debug_logs_failed_to_export">ŲØ´Ų„ ØĒØĩØ¯ŲŠØą Ø§Ų„ØŗØŦŲ„Ø§ØĒ: $s</string>
<string name="revanced_debug_logs_failed_to_export">ŲØ´Ų„ ØĒØĩØ¯ŲŠØą Ø§Ų„ØŗØŦŲ„Ø§ØĒ: %s</string>
<string name="revanced_debug_logs_clear_buffer_title">Ų…ØŗØ­ ØŗØŦŲ„Ø§ØĒ ØĒØĩØ­ŲŠØ­ Ø§Ų„ØŖØŽØˇØ§ØĄ</string>
<string name="revanced_debug_logs_clear_buffer_summary">ŲŠŲ…ØŗØ­ ØŦŲ…ŲŠØš ØŗØŦŲ„Ø§ØĒ ØĒØĩØ­ŲŠØ­ ØŖØŽØˇØ§ØĄ ReVanced Ø§Ų„Ų…ØŽØ˛Ų†ØŠ</string>
<string name="revanced_debug_logs_clear_toast">ØĒŲ… Ų…ØŗØ­ Ø§Ų„ØŗØŦŲ„Ø§ØĒ</string>
@@ -164,9 +164,6 @@ Second \"item\" text"</string>
<string name="revanced_hide_expandable_card_title">ØĨØŽŲØ§ØĄ Ø§Ų„Ø¨ØˇØ§Ų‚ØŠ Ø§Ų„Ų‚Ø§Ø¨Ų„ØŠ Ų„Ų„ØĒŲˆØŗŲŠØš</string>
<string name="revanced_hide_expandable_card_summary_on">Ø§Ų„Ø¨ØˇØ§Ų‚ØŠ Ø§Ų„Ų‚Ø§Ø¨Ų„ØŠ Ų„Ų„ØĒŲˆØŗŲŠØš ØŖØŗŲŲ„ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų…ØŽŲŲŠØŠ</string>
<string name="revanced_hide_expandable_card_summary_off">Ø§Ų„Ø¨ØˇØ§Ų‚ØŠ Ø§Ų„Ų‚Ø§Ø¨Ų„ØŠ Ų„Ų„ØĒŲˆØŗŲŠØš ØŖØŗŲŲ„ Ų…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų…ØšØąŲˆØļØŠ</string>
<string name="revanced_hide_feed_survey_title">ØĨØŽŲØ§ØĄ Ø§Ų„Ø§ØŗØĒØ¨ŲŠØ§Ų†Ø§ØĒ</string>
<string name="revanced_hide_feed_survey_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø§Ų„Ø§ØŗØĒØ¨ŲŠØ§Ų†Ø§ØĒ</string>
<string name="revanced_hide_feed_survey_summary_off">؊ØĒŲ… ØšØąØļ Ø§Ų„Ø§ØŗØĒØ¨ŲŠØ§Ų†Ø§ØĒ</string>
<string name="revanced_hide_floating_microphone_button_title">ØĨØŽŲØ§ØĄ Ø˛Øą Ø§Ų„Ų…ŲŠŲƒØąŲˆŲŲˆŲ† Ø§Ų„ØšØ§ØĻŲ…</string>
<string name="revanced_hide_floating_microphone_button_summary_on">Ø˛Øą Ø§Ų„Ų…ŲŠŲƒØąŲˆŲŲˆŲ† Ø§Ų„ØšØ§ØĻŲ… ؁؊ Ø§Ų„Ø¨Ø­ØĢ Ų…ØŽŲŲŠ</string>
<string name="revanced_hide_floating_microphone_button_summary_off">ŲŠØ¸Ų‡Øą Ø˛Øą Ø§Ų„Ų…ŲŠŲƒØąŲˆŲŲˆŲ† Ø§Ų„ØšØ§ØĻŲ… ؁؊ Ø§Ų„Ø¨Ø­ØĢ</string>
@@ -192,7 +189,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_movies_section_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ų‚ØŗŲ… Ø§Ų„ØŖŲŲ„Ø§Ų…</string>
<string name="revanced_hide_movies_section_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. -->
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
<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>
@@ -204,6 +201,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_show_more_button_title">ØĨØŽŲØ§ØĄ Ø˛Øą \'ØšØąØļ Ø§Ų„Ų…Ø˛ŲŠØ¯\'</string>
<string name="revanced_hide_show_more_button_summary_on">Ø˛Øą ØĨØ¸Ų‡Ø§Øą Ø§Ų„Ų…Ø˛ŲŠØ¯ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ Ų…ØŽŲŲŠ</string>
<string name="revanced_hide_show_more_button_summary_off">Ø˛Øą ØĨØ¸Ų‡Ø§Øą Ø§Ų„Ų…Ø˛ŲŠØ¯ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ Ų…ØšØąŲˆØļ</string>
<string name="revanced_hide_surveys_title">ØĨØŽŲØ§ØĄ Ø§Ų„Ø§ØŗØĒØ¨ŲŠØ§Ų†Ø§ØĒ</string>
<string name="revanced_hide_surveys_summary_on">Ø§Ų„Ø§ØŗØĒØˇŲ„Ø§ØšØ§ØĒ Ų…ØŽŲŲŠØŠ</string>
<string name="revanced_hide_surveys_summary_off">Ø§Ų„Ø§ØŗØĒØˇŲ„Ø§ØšØ§ØĒ Ų…ØšØąŲˆØļØŠ</string>
<string name="revanced_hide_ticket_shelf_title">ØĨØŽŲØ§ØĄ ØąŲ Ø§Ų„ØĒØ°Ø§ŲƒØą</string>
<string name="revanced_hide_ticket_shelf_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ ØąŲ Ø§Ų„ØĒØ°Ø§ŲƒØą</string>
<string name="revanced_hide_ticket_shelf_summary_off">؊ØĒŲ… ØšØąØļ ØąŲ Ø§Ų„ØĒØ°Ø§ŲƒØą</string>
@@ -251,8 +251,8 @@ Second \"item\" text"</string>
<string name="revanced_hide_timed_reactions_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ ØąØ¯ŲˆØ¯ Ø§Ų„ŲØšŲ„ Ø§Ų„Ų…Ø¤Ų‚ØĒØŠ</string>
<string name="revanced_hide_timed_reactions_summary_off">؊ØĒŲ… ØšØąØļ ØąØ¯ŲˆØ¯ Ø§Ų„ŲØšŲ„ Ø§Ų„Ų…Ø¤Ų‚ØĒØŠ</string>
<string name="revanced_hide_ai_generated_video_summary_section_title">ØĨØŽŲØ§ØĄ \'Ų…Ų„ØŽØĩ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ø°ŲŠ ØĒŲ… ØĨŲ†Ø´Ø§Ø¤Ų‡ Ø¨ŲˆØ§ØŗØˇØŠ Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ\'</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ų‚ØŗŲ… Ų…Ų„ØŽØĩ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">؊ØĒŲ… ØšØąØļ Ų‚ØŗŲ… Ų…Ų„ØŽØĩ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">Ų‚ØŗŲ… Ų…Ų„ØŽØĩ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ø°ŲŠ ØĒŲ… ØĨŲ†Ø´Ø§Ø¤Ų‡ Ø¨ŲˆØ§ØŗØˇØŠ Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ Ų…ØŽŲŲŠ</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">Ų‚ØŗŲ… Ų…Ų„ØŽØĩ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ø§Ų„Ø°ŲŠ ØĒŲ… ØĨŲ†Ø´Ø§Ø¤Ų‡ Ø¨ŲˆØ§ØŗØˇØŠ Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ Ų…ØšØąŲˆØļ</string>
<string name="revanced_hide_ask_section_title">ØĨØŽŲØ§ØĄ \"Ask\"</string>
<string name="revanced_hide_ask_section_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ų‚ØŗŲ… \"Ask\"</string>
<string name="revanced_hide_ask_section_summary_off">؊ØĒŲ… ØšØąØļ Ų‚ØŗŲ… \"Ask\"</string>
@@ -280,19 +280,19 @@ 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>
<string name="revanced_hide_filter_bar_feed_in_history_title">ØĨØŽŲØ§ØĄ ؁؊ Ø§Ų„ØŗØŦŲ„</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">Ų…ØŽŲŲŠ ؁؊ Ø§Ų„ØŗØŦŲ„</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">Ų…ØšØąŲˆØļ ؁؊ Ø§Ų„ØŗØŦŲ„</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">ØĨØŽŲØ§ØĄ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">Ų…ØŽŲŲŠ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">ŲŠŲØšØąØļ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_title">ØĨØŽŲØ§ØĄ ؁؊ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">Ų…ØŽŲŲŠ ؁؊ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">ŲŠŲØšØąØļ ؁؊ Ø§Ų„ŲŲŠØ¯ŲŠŲˆŲ‡Ø§ØĒ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">ØĨØŽŲØ§ØĄ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">Ų…ØŽŲŲŠ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">ŲŠŲØšØąØļ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_filter_bar_feed_in_history_title">ØĨØŽŲØ§ØĄ ؁؊ ØŗØŦŲ„ Ø§Ų„Ų…Ø´Ø§Ų‡Ø¯ØŠ</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">Ų…ØŽŲŲŠ ؁؊ ØŗØŦŲ„ Ø§Ų„Ų…Ø´Ø§Ų‡Ø¯ØŠ</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">Ų…ØšØąŲˆØļ ؁؊ ØŗØŦŲ„ Ø§Ų„Ų…Ø´Ø§Ų‡Ø¯ØŠ</string>
<string name="revanced_channel_screen_title">ØĩŲØ­ØŠ Ø§Ų„Ų‚Ų†Ø§ØŠ</string>
<string name="revanced_channel_screen_summary">ØĨØŽŲØ§ØĄ ØŖŲˆ ØĨØ¸Ų‡Ø§Øą Ų…ŲƒŲˆŲ†Ø§ØĒ ØĩŲØ­ØŠ Ø§Ų„Ų‚Ų†Ø§ØŠ</string>
<!-- 'For You' should be translated using the same localized wording YouTube displays. -->
@@ -315,12 +315,12 @@ Second \"item\" text"</string>
<string name="revanced_hide_visit_store_button_summary_off">Ø˛Øą Ø˛ŲŠØ§ØąØŠ Ø§Ų„Ų…ØĒØŦØą Ų…ØšØąŲˆØļ</string>
<string name="revanced_comments_screen_title">Ø§Ų„ØĒØšŲ„ŲŠŲ‚Ø§ØĒ</string>
<string name="revanced_comments_screen_summary">ØĨØŽŲØ§ØĄ ØŖŲˆ ØšØąØļ Ų…ŲƒŲˆŲ†Ø§ØĒ Ų‚ØŗŲ… Ø§Ų„ØĒØšŲ„ŲŠŲ‚Ø§ØĒ</string>
<string name="revanced_hide_comments_ai_chat_summary_title">ØĨØŽŲØ§ØĄ Ų…Ų„ØŽØĩ Ų…Ø­Ø§Ø¯ØĢاØĒ Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ų…Ų„ØŽØĩ Ø§Ų„Ų…Ø­Ø§Ø¯ØĢاØĒ</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">؊ØĒŲ… ØšØąØļ Ų…Ų„ØŽØĩ Ø§Ų„Ų…Ø­Ø§Ø¯ØĢاØĒ</string>
<string name="revanced_hide_comments_ai_chat_summary_title">ØĨØŽŲØ§ØĄ Ų…Ų„ØŽØĩ Ø§Ų„Ø¯ØąØ¯Ø´ØŠ Ø¨Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">Ų…Ų„ØŽØĩ Ø§Ų„Ø¯ØąØ¯Ø´ØŠ Ø¨Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ Ų…ØŽŲŲŠ</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">Ų…Ų„ØŽØĩ Ø§Ų„Ø¯ØąØ¯Ø´ØŠ Ø¨Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ Ų…ØšØąŲˆØļ</string>
<string name="revanced_hide_comments_ai_summary_title">ØĨØŽŲØ§ØĄ Ų…Ų„ØŽØĩ ØĒØšŲ„ŲŠŲ‚Ø§ØĒ Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ</string>
<string name="revanced_hide_comments_ai_summary_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ų…Ų„ØŽØĩ Ø§Ų„ØĒØšŲ„ŲŠŲ‚Ø§ØĒ</string>
<string name="revanced_hide_comments_ai_summary_summary_off">؊ØĒŲ… ØšØąØļ Ų…Ų„ØŽØĩ Ø§Ų„ØĒØšŲ„ŲŠŲ‚Ø§ØĒ</string>
<string name="revanced_hide_comments_ai_summary_summary_on">Ų…Ų„ØŽØĩ ØĒØšŲ„ŲŠŲ‚Ø§ØĒ Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ Ų…ØŽŲŲŠ</string>
<string name="revanced_hide_comments_ai_summary_summary_off">Ų…Ų„ØŽØĩ ØĒØšŲ„ŲŠŲ‚Ø§ØĒ Ø§Ų„Ø°ŲƒØ§ØĄ Ø§Ų„Ø§ØĩØˇŲ†Ø§ØšŲŠ Ų…ØšØąŲˆØļ</string>
<string name="revanced_hide_comments_channel_guidelines_title">ØĨØŽŲØ§ØĄ ØĨØąØ´Ø§Ø¯Ø§ØĒ Ø§Ų„Ų‚Ų†Ø§ØŠ</string>
<string name="revanced_hide_comments_channel_guidelines_summary_on">ØĨØąØ´Ø§Ø¯Ø§ØĒ Ø§Ų„Ų‚Ų†Ø§ØŠ Ų…ØŽŲŲŠØŠ</string>
<string name="revanced_hide_comments_channel_guidelines_summary_off">ØĨØąØ´Ø§Ø¯Ø§ØĒ Ø§Ų„Ų‚Ų†Ø§ØŠ Ų…ØšØąŲˆØļØŠ</string>
@@ -419,7 +419,6 @@ Second \"item\" text"</string>
<string name="revanced_hide_shopping_links_title">ØĨØŽŲØ§ØĄ ØąŲˆØ§Ø¨Øˇ Ø§Ų„ØĒØŗŲˆŲ‚</string>
<string name="revanced_hide_shopping_links_summary_on">ØąŲˆØ§Ø¨Øˇ Ø§Ų„ØĒØŗŲˆŲ‚ ؁؊ ؈Øĩ؁ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų…ØŽŲŲŠØŠ</string>
<string name="revanced_hide_shopping_links_summary_off">؊ØĒŲ… ØšØąØļ ØąŲˆØ§Ø¨Øˇ Ø§Ų„ØĒØŗŲˆŲ‚ ؁؊ ؈Øĩ؁ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<!-- 'View products' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_view_products_banner_title">ØĨØŽŲØ§ØĄ Ų„Ø§ŲØĒØŠ \'ØšØąØļ Ø§Ų„Ų…Ų†ØĒØŦاØĒ\'</string>
<string name="revanced_hide_view_products_banner_summary_on">Ų„Ø§ŲØĒØŠ ØšØąØļ Ø§Ų„Ų…Ų†ØĒØŦاØĒ ؁؊ ØĒØąØ§ŲƒØ¨ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų…ØŽŲŲŠØŠ</string>
<string name="revanced_hide_view_products_banner_summary_off">Ų„Ø§ŲØĒØŠ ØšØąØļ Ø§Ų„Ų…Ų†ØĒØŦاØĒ ؁؊ ØĒØąØ§ŲƒØ¨ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ Ų…ØšØąŲˆØļØŠ</string>
@@ -453,7 +452,7 @@ Second \"item\" text"</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_off">ØŗŲŠØĒŲ… ØšØąØļ Ų…ØąØ¨Øš Ø§Ų„Ø­ŲˆØ§Øą</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">ŲˆŲ‡Ø°Ø§ Ų„Ø§ ؊ØĒØŦØ§ŲˆØ˛ Ų‚ŲŠŲˆØ¯ Ø§Ų„ØŗŲ†. Ø¨Ų„ ŲŠŲ‚Ø¨Ų„Ų‡Ø§ ØĒŲ„Ų‚Ø§ØĻŲŠŲ‹Ø§.</string>
</patch>
<patch id="interaction.doubletap.disableChapterSkipDoubleTapPatch">
<patch id="interaction.doubletap.disableDoubleTapActionsPatch">
<string name="revanced_disable_chapter_skip_double_tap_title">ØĒØšØˇŲŠŲ„ ØĒØŽØˇŲŠ Ø§Ų„ŲØĩŲ„ Ø¨Ø§Ų„Ų†Ų‚Øą Ø§Ų„Ų…Ø˛Ø¯ŲˆØŦ</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_on">Ų„Ø§ ŲŠŲ…ŲƒŲ† Ų„Ų„Ų†Ų‚Øą Ø§Ų„Ų…Ø˛Ø¯ŲˆØŦ Ų…ØˇŲ„Ų‚Ų‹Ø§ ØŖŲ† ŲŠØ¤Ø¯ŲŠ ØĨŲ„Ų‰ ØĒØŽØˇŲŠ Ø§Ų„ŲØĩŲ„ Ø§Ų„ØĒØ§Ų„ŲŠ/Ø§Ų„ØŗØ§Ø¨Ų‚</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_off">ŲŠŲ…ŲƒŲ† Ų„Ų„Ų†Ų‚Øą Ø§Ų„Ų…Ø˛Ø¯ŲˆØŦ ØŖŲ† ŲŠØ¤Ø¯ŲŠ ØŖØ­ŲŠØ§Ų†Ų‹Ø§ ØĨŲ„Ų‰ ØĒØŽØˇŲŠ Ø§Ų„ŲØĩŲ„ Ø§Ų„ØĒØ§Ų„ŲŠ/Ø§Ų„ØŗØ§Ø¨Ų‚</string>
@@ -469,8 +468,15 @@ Second \"item\" text"</string>
<string name="revanced_external_downloader_action_button_summary_on">؊؁ØĒØ­ Ø˛Øą Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„ ØŖØ¯Ø§ØŠ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„ Ø§Ų„ØŽØ§ØąØŦŲŠØŠ</string>
<string name="revanced_external_downloader_action_button_summary_off">؊؁ØĒØ­ Ø˛Øą Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„ ØŖØ¯Ø§ØŠ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„ Ø§Ų„ØŖØĩŲ„ŲŠØŠ Ø¯Ø§ØŽŲ„ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚</string>
<string name="revanced_external_downloader_name_title">Ø§ØŗŲ… Ø­Ø˛Ų…ØŠ ØŖØ¯Ø§ØŠ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„</string>
<string name="revanced_external_downloader_name_summary">Ø§ØŗŲ… Ø§Ų„Ø­Ø˛Ų…ØŠ Ų„ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„ Ø§Ų„ØŽØ§ØąØŦ؊ Ø§Ų„Ų…ØĢبØĒ Ų„Ø¯ŲŠŲƒØŒ Ų…ØĢŲ„ NewPipe ØŖŲˆ Seal</string>
<string name="revanced_external_downloader_name_summary">Ø§ØŗŲ… Ø­Ø˛Ų…ØŠ ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„ØĒŲ†Ø˛ŲŠŲ„ Ø§Ų„ØŽØ§ØąØŦ؊ Ø§Ų„Ų…ØĢبØĒ Ų„Ø¯ŲŠŲƒ</string>
<string name="revanced_external_downloader_other_item_hint">ØŖØ¯ØŽŲ„ Ø§ØŗŲ… Ø§Ų„Ø­Ø˛Ų…ØŠ</string>
<string name="revanced_external_downloader_other_item">ØŖØŽØąŲ‰</string>
<string name="revanced_external_downloader_not_found_title">Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ ØēŲŠØą Ų…ØĢبØĒ</string>
<string name="revanced_external_downloader_not_installed_warning">Ų„Ų… ؊ØĒŲ… ØĒØĢØ¨ŲŠØĒ %s . Ø§Ų„ØąØŦØ§ØĄ ØĒØĢØ¨ŲŠØĒŲ‡.</string>
<string name="revanced_external_downloader_package_not_found_warning">"ØĒØšØ°Øą Ø§Ų„ØšØĢŲˆØą ØšŲ„Ų‰ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ø§Ų„Ų…ØĢبØĒ Ø¨Ø§ØŗŲ… Ø§Ų„Ø­Ø˛Ų…ØŠ: %s
ØĒØŖŲƒØ¯ Ų…Ų† ØŖŲ† Ø§ØŗŲ… Ø§Ų„Ø­Ø˛Ų…ØŠ ØĩØ­ŲŠØ­ ŲˆØŖŲ† Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ų…ØĢبØĒ"</string>
<string name="revanced_external_downloader_empty_warning">Ų„Ø§ ŲŠŲ…ŲƒŲ† ØŖŲ† ŲŠŲƒŲˆŲ† Ø§ØŗŲ… Ø§Ų„Ø­Ø˛Ų…ØŠ ŲØ§ØąØēŲ‹Ø§</string>
</patch>
<patch id="interaction.seekbar.disablePreciseSeekingGesturePatch">
<string name="revanced_disable_precise_seeking_gesture_title">ØĒØšØˇŲŠŲ„ ØĨŲŠŲ…Ø§ØĄØŠ Ø§Ų„ØĒŲ…ØąŲŠØą Ø§Ų„Ø¯Ų‚ŲŠŲ‚ØŠ</string>
@@ -730,27 +736,28 @@ Second \"item\" text"</string>
<string name="revanced_disable_rolling_number_animations_summary_off">ؚدد Ų…ØąØ§ØĒ Ø§Ų„Ų…Ø´Ø§Ų‡Ø¯ØŠ ŲˆØ§Ų„ØĨØšØŦاباØĒ Ų…ØĒØ­ØąŲƒØŠ</string>
</patch>
<patch id="layout.hide.seekbar.hideSeekbarPatch">
<string name="revanced_hide_seekbar_title">ØĨØŽŲØ§ØĄ Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ‚Ø¯Ų… ؁؊ Ų…Ø´ØēŲ„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_seekbar_title">ØĨØŽŲØ§ØĄ Ø´ØąŲŠØˇ ØĒŲ‚Ø¯Ų… Ų…Ø´ØēŲ„ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_seekbar_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ø´ØąŲŠØˇ ØĒŲ‚Ø¯Ų… Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_seekbar_summary_off">؊ØĒŲ… ØšØąØļ Ø´ØąŲŠØˇ ØĒŲ‚Ø¯Ų… Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_seekbar_thumbnail_title">ØĨØŽŲØ§ØĄ Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ‚Ø¯Ų… ؁؊ Ų…ŲØĩŲ‘ŲŽØēŲŽØąØ§ØĒ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">ØĒŲ… ØĨØŽŲØ§ØĄ Ų…ØĩØēØąØŠ Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ‚Ø¯Ų…</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">؊ØĒŲ… ØšØąØļ Ų…ØĩØēØąØŠ Ø´ØąŲŠØˇ Ø§Ų„ØĒŲ‚Ø¯Ų…</string>
<!-- Seekbar shown inside video thumbnails found the home/feed/search/history. The seekbar shows the prior watch progress when the video was last open. -->
<string name="revanced_hide_seekbar_thumbnail_title">ØĨØŽŲØ§ØĄ Ø´ØąŲŠØˇ ØĒŲ‚Ø¯Ų… ØĩŲˆØą Ų…ØĩØēØąØŠ Ų„Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">Ø´ØąŲŠØˇ ØĒŲ‚Ø¯Ų… ØĩŲˆØą Ų…ØĩØēØąØŠ Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ų…ØŽŲŲŠ</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">Ø´ØąŲŠØˇ ØĒŲ‚Ø¯Ų… ØĩŲˆØą Ų…ØĩØēØąØŠ Ų„Ų„ŲŲŠØ¯ŲŠŲˆ Ų…ØšØąŲˆØļ</string>
</patch>
<patch id="layout.hide.shorts.hideShortsComponentsResourcePatch">
<string name="revanced_shorts_player_screen_title">Ų…Ø´ØēŲ„ Shorts</string>
<string name="revanced_shorts_player_screen_summary">ØĨØŽŲØ§ØĄ ØŖŲˆ ØšØąØļ Ø§Ų„Ų…ŲƒŲˆŲ†Ø§ØĒ ؁؊ Ų…Ø´ØēŲ„ Shorts</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">Ų…ØŽŲŲŠØŠ ؁؊ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_shorts_home_summary_off">Ų…ØšØąŲˆØļØŠ ؁؊ Ø§Ų„ØĩŲØ­ØŠ Ø§Ų„ØąØĻŲŠØŗŲŠØŠ ŲˆŲ…Ų‚Ø§ØˇØš Ø§Ų„ŲŲŠØ¯ŲŠŲˆ ذاØĒ Ø§Ų„ØĩŲ„ØŠ</string>
<string name="revanced_hide_shorts_search_title">ØĨØŽŲØ§ØĄ Shorts ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_shorts_search_summary_on">Ų…ØŽŲŲŠØŠ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<string name="revanced_hide_shorts_search_summary_off">ØĒŲØšØąØļ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</string>
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the Subscriptions tab. -->
<string name="revanced_hide_shorts_subscriptions_title">ØĨØŽŲØ§ØĄ 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">Ų…ØŽŲŲŠØŠ ؁؊ Ų†ØĒاØĻØŦ Ø§Ų„Ø¨Ø­ØĢ</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>
@@ -1190,8 +1197,6 @@ Second \"item\" text"</string>
ØŗŲŠØ¤Ø¯ŲŠ Ų‡Ø°Ø§ ØĨŲ„Ų‰ ØĒØēŲŠŲŠØą Ų…Ø¸Ų‡Øą ŲˆŲ…Ų…ŲŠØ˛Ø§ØĒ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ØŒ ŲˆŲ„ŲƒŲ† Ų‚Ø¯ ØĒحدØĢ ØĒØŖØĢŲŠØąØ§ØĒ ØŦØ§Ų†Ø¨ŲŠØŠ ØēŲŠØą Ų…ØšØąŲˆŲØŠ.
ØĨذا ØĒŲ… ØĨŲŠŲ‚Ø§Ų ØĒØ´ØēŲŠŲ„Ų‡ Ų„Ø§Ø­Ų‚Ų‹Ø§ØŒ Ų…Ų† Ø§Ų„Ų…ØŗØĒØ­ØŗŲ† Ų…ØŗØ­ Ø¨ŲŠØ§Ų†Ø§ØĒ Ø§Ų„ØĒØˇØ¨ŲŠŲ‚ Ų„Ų…Ų†Øš Ø­Ø¯ŲˆØĢ ØŖØŽØˇØ§ØĄ ؁؊ ŲˆØ§ØŦŲ‡ØŠ Ø§Ų„Ų…ØŗØĒØŽØ¯Ų…."</string>
<!-- 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. -->
<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.01.34 - Ø§ØŗØĒؚاد؊ ØŖŲŠŲ‚ŲˆŲ†Ø§ØĒ Ø§Ų„ØĒŲ†Ų‚Ų„ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ</string>
@@ -1460,10 +1465,18 @@ Second \"item\" text"</string>
<string name="revanced_playback_speed_dialog_button_summary_on">Ø§Ų„Ø˛Øą Ų…ØšØąŲˆØļ. Ø§Ų†Ų‚Øą Ų…Øš Ø§Ų„Ø§ØŗØĒŲ…ØąØ§Øą Ų„ØĨؚاد؊ ØļØ¨Øˇ ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„ ØĨŲ„Ų‰ Ø§Ų„ŲˆØļØš Ø§Ų„Ø§ŲØĒØąØ§Øļ؊</string>
<string name="revanced_playback_speed_dialog_button_summary_off">Ų„Ø§ ؊ØĒŲ… ØšØąØļ Ø§Ų„Ø˛Øą</string>
</patch>
<patch id="video.quality.button.videoQualityButtonPatch">
<string name="revanced_video_quality_dialog_button_title">ØšØąØļ Ø˛Øą ØŦŲˆØ¯ØŠ Ø§Ų„ŲŲŠØ¯ŲŠŲˆ</string>
<string name="revanced_video_quality_dialog_button_summary_on">Ø§Ų„Ø˛Øą Ų…ØšØąŲˆØļ. Ø§Ų†Ų‚Øą Ų…Øš Ø§Ų„Ø§ØŗØĒŲ…ØąØ§Øą Ų„ØĨؚاد؊ ØĒØšŲŠŲŠŲ† Ø§Ų„ØŦŲˆØ¯ØŠ ØĨŲ„Ų‰ Ø§Ų„Ø§ŲØĒØąØ§Øļ؊</string>
<string name="revanced_video_quality_dialog_button_summary_off">Ø§Ų„Ø˛Øą ØēŲŠØą Ų…ØšØąŲˆØļ</string>
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
<string name="revanced_custom_speed_menu_title">Ų‚Ø§ØĻŲ…ØŠ ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…ØŽØĩØĩØŠ</string>
<string name="revanced_custom_speed_menu_summary_on">؊ØĒŲ… ØšØąØļ Ų‚Ø§ØĻŲ…ØŠ ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…ØŽØĩØĩØŠ</string>
<string name="revanced_custom_speed_menu_summary_off">Ų„Ø§ ؊ØĒŲ… ØšØąØļ Ų‚Ø§ØĻŲ…ØŠ ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…ØŽØĩØĩØŠ</string>
<string name="revanced_restore_old_speed_menu_title">Ø§ØŗØĒؚاد؊ Ų‚Ø§ØĻŲ…ØŠ ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ</string>
<string name="revanced_restore_old_speed_menu_summary_on">Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„ØŗØąØšØŠ Ø§Ų„Ų‚Ø¯ŲŠŲ…ØŠ Ų…ØšØąŲˆØļØŠ</string>
<string name="revanced_restore_old_speed_menu_summary_off">Ų‚Ø§ØĻŲ…ØŠ Ø§Ų„ØŗØąØšØŠ Ø§Ų„Ø­Ø¯ŲŠØĢØŠ Ų…ØšØąŲˆØļØŠ</string>
<string name="revanced_custom_playback_speeds_title">ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…ØŽØĩØĩØŠ</string>
<string name="revanced_custom_playback_speeds_summary">ØĨØļØ§ŲØŠ ØŖŲˆ ØĒØēŲŠŲŠØą ØŗØąØšØŠ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…ØŽØĩØĩØŠ</string>
<string name="revanced_custom_playback_speeds_invalid">؊ØŦب ØŖŲ† ØĒŲƒŲˆŲ† ØŗØąØšØ§ØĒ Ø§Ų„ØĒØ´ØēŲŠŲ„ Ø§Ų„Ų…ØŽØĩØĩØŠ ØŖŲ‚Ų„ Ų…Ų† %s</string>

View File

@@ -40,7 +40,7 @@ Second \"item\" text"</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<!-- '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. -->
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
<!-- 'Show more' should be translated with the same localized wording that YouTube displays.
This button usually appears when searching for a YT creator. -->
<!-- 'People also watched' and 'You might also like' should be translated using the same localized wording YouTube displays. -->
@@ -58,7 +58,6 @@ Second \"item\" text"</string>
</patch>
<patch id="ad.general.hideAdsResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<!-- 'View products' should be translated with the same localized wording that YouTube displays. -->
</patch>
<patch id="ad.getpremium.hideGetPremiumPatch">
</patch>
@@ -68,7 +67,7 @@ Second \"item\" text"</string>
</patch>
<patch id="interaction.dialog.removeViewerDiscretionDialogPatch">
</patch>
<patch id="interaction.doubletap.disableChapterSkipDoubleTapPatch">
<patch id="interaction.doubletap.disableDoubleTapActionsPatch">
</patch>
<patch id="interaction.downloads.downloadsResourcePatch">
<!-- 'Download action button' should be translated using the same wording as the translation of 'revanced_hide_download_button_title'. -->
@@ -128,6 +127,7 @@ Second \"item\" text"</string>
<patch id="layout.hide.rollingnumber.disableRollingNumberAnimationPatch">
</patch>
<patch id="layout.hide.seekbar.hideSeekbarPatch">
<!-- Seekbar shown inside video thumbnails found the home/feed/search/history. The seekbar shows the prior watch progress when the video was last open. -->
</patch>
<patch id="layout.hide.shorts.hideShortsComponentsResourcePatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the Home tab. -->
@@ -170,8 +170,6 @@ Second \"item\" text"</string>
<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. -->
</patch>
<patch id="layout.startpage.changeStartPagePatch">
</patch>
@@ -226,6 +224,8 @@ Second \"item\" text"</string>
</patch>
<patch id="video.speed.button.playbackSpeedButtonPatch">
</patch>
<patch id="video.quality.button.videoQualityButtonPatch">
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">

View File

@@ -137,7 +137,7 @@ GÃļzlənilməz hallardan xəbərdar olmayacaqsÄąnÄąz."</string>
<string name="revanced_debug_logs_disabled">Sazlama qeydi qapalÄądÄąr</string>
<string name="revanced_debug_logs_none_found">Qeydlər tapılmadı</string>
<string name="revanced_debug_logs_copied_to_clipboard">Qeydlər kÃļçÃŧrÃŧldÃŧ</string>
<string name="revanced_debug_logs_failed_to_export">Qeydləri ixrac etmək alınmadı: $s</string>
<string name="revanced_debug_logs_failed_to_export">Qeydləri ixrac etmək alınmadı: %s</string>
<string name="revanced_debug_logs_clear_buffer_title">Sazlama qeydlərini təmizlə</string>
<string name="revanced_debug_logs_clear_buffer_summary">SaxlanÄąlan bÃŧtÃŧn ReVanced sazlama qeydlərini təmizləyir</string>
<string name="revanced_debug_logs_clear_toast">Qeydlər silindi</string>
@@ -164,9 +164,6 @@ GÃļzlənilməz hallardan xəbərdar olmayacaqsÄąnÄąz."</string>
<string name="revanced_hide_expandable_card_title">Genişlənən kartÄą gizlət</string>
<string name="revanced_hide_expandable_card_summary_on">VideolarÄąn aşağısÄąnda genişlənən kart gizlidir</string>
<string name="revanced_hide_expandable_card_summary_off">VideolarÄąn altÄąnda genişlənən kart gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_feed_survey_title">Axın sorğuların gizlət</string>
<string name="revanced_hide_feed_survey_summary_on">Axın sorğuları gizlidir</string>
<string name="revanced_hide_feed_survey_summary_off">AxÄąn sorğularÄą gÃļstərilir</string>
<string name="revanced_hide_floating_microphone_button_title">Üzən mikrofon dÃŧyməsini gizlət</string>
<string name="revanced_hide_floating_microphone_button_summary_on">AxtarÄąÅŸda Ãŧzən mikrofon dÃŧyməsi gizlidir</string>
<string name="revanced_hide_floating_microphone_button_summary_off">Üzən mikrofon dÃŧyməsi axtarÄąÅŸda gÃļstərilir</string>
@@ -192,7 +189,7 @@ GÃļzlənilməz hallardan xəbərdar olmayacaqsÄąnÄąz."</string>
<string name="revanced_hide_movies_section_summary_on">Filmlər bÃļlməsi gizlidir</string>
<string name="revanced_hide_movies_section_summary_off">Filmlər bÃļlməsi gÃļstərilir</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. -->
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
<string name="revanced_hide_notify_me_button_title">\"Mənə bildir\" dÃŧyməsini gizlət</string>
<string name="revanced_hide_notify_me_button_summary_on">Mənə bildir dÃŧyməsi gizlidir</string>
<string name="revanced_hide_notify_me_button_summary_off">Mənə bildir dÃŧyməsi gÃļrÃŧnÃŧr</string>
@@ -204,6 +201,9 @@ GÃļzlənilməz hallardan xəbərdar olmayacaqsÄąnÄąz."</string>
<string name="revanced_hide_show_more_button_title">\'Daha çox gÃļstər\' dÃŧyməsini gizlət</string>
<string name="revanced_hide_show_more_button_summary_on">Daha çox gÃļstər dÃŧyməsi axtarÄąÅŸ nəticələrində gizlidir</string>
<string name="revanced_hide_show_more_button_summary_off">Daha çox gÃļstər dÃŧyməsi axtarÄąÅŸ nəticələrində gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_surveys_title">Sorğuları gizlət</string>
<string name="revanced_hide_surveys_summary_on">Sorğular gizlədilib</string>
<string name="revanced_hide_surveys_summary_off">Sorğular gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_ticket_shelf_title">Bilet bÃļlməsin gizlət</string>
<string name="revanced_hide_ticket_shelf_summary_on">Bilet bÃļlməsi gizlidir</string>
<string name="revanced_hide_ticket_shelf_summary_off">Bilet bÃļlməsi gÃļrÃŧnÃŧr</string>
@@ -251,8 +251,8 @@ GÃļzlənilməz hallardan xəbərdar olmayacaqsÄąnÄąz."</string>
<string name="revanced_hide_timed_reactions_summary_on">ZamanlanmÄąÅŸ reaksiyalar gizlədilir</string>
<string name="revanced_hide_timed_reactions_summary_off">ZamanlanmÄąÅŸ reaksiyalar gÃļstərilir</string>
<string name="revanced_hide_ai_generated_video_summary_section_title">\"AI ilə yaradÄąlan video xÃŧlasəsini\" gizlət</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">Video xÃŧlasə bÃļlməsi gizlədilib</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">Video xÃŧlasə bÃļlməsi gÃļstərilir</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">Sİ ilə yaradÄąlan video xÃŧlasə bÃļlməsi gizlədilib</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">Sİ ilə yaradÄąlan video xÃŧlasə bÃļlməsi gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_ask_section_title">Soruş\'u Gizlət</string>
<string name="revanced_hide_ask_section_summary_on">Soruş bÃļlməsi gizlidir</string>
<string name="revanced_hide_ask_section_summary_off">\"Soruş\" bÃļlməsi gÃļstərilir</string>
@@ -280,19 +280,19 @@ GÃļzlənilməz hallardan xəbərdar olmayacaqsÄąnÄąz."</string>
<string name="revanced_hide_description_components_screen_title">Video təsviri</string>
<string name="revanced_hide_description_components_screen_summary">Video təsviri elementlərini gizlət və ya gÃļstər</string>
<string name="revanced_hide_filter_bar_screen_title">Filtr çubuğu</string>
<string name="revanced_hide_filter_bar_screen_summary">AxÄąnlar, tarixçə, axtarÄąÅŸ nəticələri və əlaqəli videolarda filtr panelini gizlət və ya gÃļstər</string>
<string name="revanced_hide_filter_bar_screen_summary">AxÄąnlar, əlaqəli videolar, axtarÄąÅŸ nəticələri və baxÄąÅŸ tarixçəsində filtr cərgəsin gizlət və ya gÃļstər</string>
<string name="revanced_hide_filter_bar_feed_in_feed_title">Axınlarda gizlət</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_on">AxÄąnlarda gizlidir</string>
<string name="revanced_hide_filter_bar_feed_in_feed_summary_off">AxÄąnlarda gÃļstər</string>
<string name="revanced_hide_filter_bar_feed_in_history_title">Tarixçədə gizlət</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">Tarixçədə gizlədilib</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">Tarixçədə gÃļstərilib</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">AxtarÄąÅŸ nəticələrində gizlət</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">AxtarÄąÅŸ nəticələrində gizlədilib</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">AxtarÄąÅŸ nəticələrində gÃļstərilir</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_title">Əlaqəli videolarda gizlət</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">Əlaqəli videolarda gizlidir</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">Əlaqəli videolarda gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">AxtarÄąÅŸ nəticələrində gizlət</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">AxtarÄąÅŸ nəticələrində gizlədilib</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">AxtarÄąÅŸ nəticələrində gÃļstərilir</string>
<string name="revanced_hide_filter_bar_feed_in_history_title">BaxÄąÅŸ tarixçəsində gizlət</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">BaxÄąÅŸ tarixçəsində gizlədilib</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">BaxÄąÅŸ tarixçəsində gÃļrÃŧnÃŧr</string>
<string name="revanced_channel_screen_title">Kanal səhifəsi</string>
<string name="revanced_channel_screen_summary">Kanal səhifə elementlərini gizlət və ya gÃļstər</string>
<!-- 'For You' should be translated using the same localized wording YouTube displays. -->
@@ -315,12 +315,12 @@ GÃļzlənilməz hallardan xəbərdar olmayacaqsÄąnÄąz."</string>
<string name="revanced_hide_visit_store_button_summary_off">Mağazaya baxÄąn dÃŧyməsi gÃļrÃŧnÃŧr</string>
<string name="revanced_comments_screen_title">Şərhlər</string>
<string name="revanced_comments_screen_summary">Şərhlər bÃļlməsi elementlərin gizlət və ya gÃļstər</string>
<string name="revanced_hide_comments_ai_chat_summary_title">AI SÃļhbət XÃŧlasəsini Gizlət</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">SÃļhbət yekunu gizlidir </string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">SÃļhbət yekunu gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_comments_ai_chat_summary_title">Sİ sÃļhbət xÃŧlasəsini gizlət</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">Sİ sÃļhbət xÃŧlasəsi gizlidir</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">Sİ sÃļhbət xÃŧlasəsi gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_comments_ai_summary_title">AI ášĸərhlər XÃŧlasəsini Gizlət</string>
<string name="revanced_hide_comments_ai_summary_summary_on">ášĸərh yekunu gizlidir </string>
<string name="revanced_hide_comments_ai_summary_summary_off">Şərh yekunu gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_comments_ai_summary_summary_on">Sİ şərhlər xÃŧlasəsi gizlidir</string>
<string name="revanced_hide_comments_ai_summary_summary_off">Sİ şərhlər xÃŧlasəsi gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_comments_channel_guidelines_title">Kanal təlimatlarını gizlət</string>
<string name="revanced_hide_comments_channel_guidelines_summary_on">Kanal təlimatları gizlidir</string>
<string name="revanced_hide_comments_channel_guidelines_summary_off">Kanal təlimatlarÄą gÃļrÃŧnÃŧr</string>
@@ -419,7 +419,6 @@ Bu xÃŧsusiyyət yalnÄąz kÃļhnə cihazlar ÃŧçÃŧn mÃļvcuddur"</string>
<string name="revanced_hide_shopping_links_title">AlÄąÅŸ-veriş linklərini gizlət</string>
<string name="revanced_hide_shopping_links_summary_on">AlÄąÅŸ-veriş linkləri video təsvirdə gizlidir</string>
<string name="revanced_hide_shopping_links_summary_off">AlÄąÅŸ-veriş linkləri video təsvirdə gÃļrÃŧnÃŧr</string>
<!-- 'View products' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_view_products_banner_title">“Məhsullara baxın” panelin gizlət</string>
<string name="revanced_hide_view_products_banner_summary_on">Məhsullara baxÄąÅŸ etiketi video ÃļrtÃŧyÃŧndə gizlidir</string>
<string name="revanced_hide_view_products_banner_summary_off">Məhsullara baxÄąÅŸ etiketi video ÃļrtÃŧyÃŧndə gÃļrÃŧnÃŧr</string>
@@ -453,7 +452,7 @@ Bu xÃŧsusiyyət yalnÄąz kÃļhnə cihazlar ÃŧçÃŧn mÃļvcuddur"</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_off">Dialoq gÃļstərilir</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">Bu, yaş məhdudiyyətini ÃļtÃŧrmÃŧr. Sadəcə birbaşa qəbul edir.</string>
</patch>
<patch id="interaction.doubletap.disableChapterSkipDoubleTapPatch">
<patch id="interaction.doubletap.disableDoubleTapActionsPatch">
<string name="revanced_disable_chapter_skip_double_tap_title">CÃŧt toxunuşla fəsil ÃļtÃŧrməsini qapat</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_on">CÃŧt toxunma heç vaxt nÃļvbəti/əvvəlki fəsilə keçidi zorlaya bilməz</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_off">CÃŧt toxunma bəzən nÃļvbəti/əvvəlki fəsilə keçidi zorlaya bilər</string>
@@ -469,8 +468,15 @@ Bu xÃŧsusiyyət yalnÄąz kÃļhnə cihazlar ÃŧçÃŧn mÃļvcuddur"</string>
<string name="revanced_external_downloader_action_button_summary_on">YÃŧkləmə dÃŧyməsi, xarici yÃŧkləyicinizi aÃ§Äąr</string>
<string name="revanced_external_downloader_action_button_summary_off">YÃŧkləmə dÃŧyməsi tətbiqə xas yÃŧkləyicini aÃ§Äąr</string>
<string name="revanced_external_downloader_name_title">YÃŧkləyici paketi adÄą</string>
<string name="revanced_external_downloader_name_summary">NewPipe və ya Seal kimi quraşdÄąrÄąlan xarici yÃŧkləmə tətbiqinizin paket adÄą</string>
<string name="revanced_external_downloader_name_summary">QuraşdÄąrÄąlan xarici yÃŧkləyici tətbiqinizin paket adÄą</string>
<string name="revanced_external_downloader_other_item_hint">Paket adÄąnÄą yerləşdir</string>
<string name="revanced_external_downloader_other_item">Digər</string>
<string name="revanced_external_downloader_not_found_title">Tətbiq quraşdÄąrÄąlmayÄąb</string>
<string name="revanced_external_downloader_not_installed_warning">%s quraşdÄąrÄąlmayÄąb. LÃŧtfən, bunu quraşdÄąr.</string>
<string name="revanced_external_downloader_package_not_found_warning">"Paket adÄą ilə quraşdÄąrÄąlan tətbiq tapÄąlmadÄą: %s
Paket adÄąnÄąn dÃŧzgÃŧn olduğun yoxla və tətbiqi quraşdÄąrÄąn"</string>
<string name="revanced_external_downloader_empty_warning">Paket adÄą boş ola bilməz</string>
</patch>
<patch id="interaction.seekbar.disablePreciseSeekingGesturePatch">
<string name="revanced_disable_precise_seeking_gesture_title">Dəqiq axtarÄąÅŸ jestini qapadÄąn</string>
@@ -730,27 +736,28 @@ Audio trek seçimin gÃļstərmək ÃŧçÃŧn \"Video axÄąnlarÄą saxtalaşdÄąr\"Äą iO
<string name="revanced_disable_rolling_number_animations_summary_off">SÃŧrÃŧşən say animasiyasÄą aÃ§ÄąqdÄąr</string>
</patch>
<patch id="layout.hide.seekbar.hideSeekbarPatch">
<string name="revanced_hide_seekbar_title">Video oynadÄącÄąda irəliləyiş cizgisin gizlə</string>
<string name="revanced_hide_seekbar_title">Video oynadÄącÄą irəliləyiş cizgisin gizlət</string>
<string name="revanced_hide_seekbar_summary_on">Video oynadÄącÄą irəliləyiş cizgisi gizlidir</string>
<string name="revanced_hide_seekbar_summary_off">Video oynadÄącÄą irəliləyiş cizgisi gÃļstərilir</string>
<string name="revanced_hide_seekbar_thumbnail_title">Video miniatÃŧrdə irəliləmə cizgisin gizlə</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">MiniatÃŧr irəliləmə cizgisi gizlədilib</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">MiniatÃŧr irəliləmə cizgisi gÃļstərilir</string>
<!-- Seekbar shown inside video thumbnails found the home/feed/search/history. The seekbar shows the prior watch progress when the video was last open. -->
<string name="revanced_hide_seekbar_thumbnail_title">Video miniatÃŧr irəliləyiş cizgisin gizlət</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">Video miniatÃŧr irəliləyiş cizgisi gizlidir</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">Video miniatÃŧr irəliləyiş cizgisi gÃļrÃŧnÃŧr</string>
</patch>
<patch id="layout.hide.shorts.hideShortsComponentsResourcePatch">
<string name="revanced_shorts_player_screen_title">Shorts oynadÄącÄą</string>
<string name="revanced_shorts_player_screen_summary">Shorts oynadÄącÄąda hissəcikləri gizlət və ya gÃļstər</string>
<string name="revanced_shorts_player_screen_summary">Shorts oynadÄącÄą elementlərini gizlət və ya gÃļstər</string>
<!-- 'Home' should be translated using the same localized wording YouTube displays for the Home tab. -->
<string name="revanced_hide_shorts_home_title">Shorts-u Ev axınında gizlət</string>
<string name="revanced_hide_shorts_home_summary_on">Ev axını və əlaqəli videolarda gizlidir</string>
<string name="revanced_hide_shorts_home_summary_off">Ev axÄąnÄą və əlaqəli videolarda gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_shorts_search_title">AxtarÄąÅŸ nəticələrindəki \"Shorts\"u gizlət</string>
<string name="revanced_hide_shorts_search_summary_on">AxtarÄąÅŸ nəticələrində gizlidir</string>
<string name="revanced_hide_shorts_search_summary_off">AxtarÄąÅŸ nəticələrində gÃļrÃŧnÃŧr</string>
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the Subscriptions tab. -->
<string name="revanced_hide_shorts_subscriptions_title">Shorts-u Abunəliklər axınında gizlət</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Abunəliklər axınında gizlidir</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">Abunəliklər axÄąnÄąnda gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_shorts_search_title">AxtarÄąÅŸ nəticələrindəki \"Shorts\"u gizlət</string>
<string name="revanced_hide_shorts_search_summary_on">AxtarÄąÅŸ nəticələrində gizlidir</string>
<string name="revanced_hide_shorts_search_summary_off">AxtarÄąÅŸ nəticələrində gÃļrÃŧnÃŧr</string>
<string name="revanced_hide_shorts_history_title">BaxÄąÅŸ tarixçəsində Shorts-u gizlət</string>
<string name="revanced_hide_shorts_history_summary_on">BaxÄąÅŸ tarixçəsində gizlidir</string>
<string name="revanced_hide_shorts_history_summary_off">BaxÄąÅŸ tarixçəsində gÃļstərilib</string>
@@ -1189,8 +1196,6 @@ Avtomobil tərtibatı
Bu tətbiqin gÃļrÃŧnÃŧşÃŧn və xÃŧsusiyyətlərin dəyişdirəcək, lakin bilinməyən yan təsirlər ola bilər.
Sonradan qapadÄąlarsa, UI səhvlərin Ãļnləmək ÃŧçÃŧn tətbiq məlumatlarÄąn silmək tÃļvsiyə olunur."</string>
<!-- 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. -->
<string name="revanced_spoof_app_version_target_title">Saxta tətbiq versiyası hədəfi</string>
<string name="revanced_spoof_app_version_target_entry_1">19.35.36 - KÃļhnə Shorts oynadÄącÄą işarələrin bərpa et</string>
<string name="revanced_spoof_app_version_target_entry_2">19.01.34 - KÃļhnə fəaliyyət simvollarÄąn bərpa et</string>
@@ -1459,10 +1464,15 @@ Bunu aktivləşdirmə daha yÃŧksək video keyfiyyətləri əngəlin silə bilər
<string name="revanced_playback_speed_dialog_button_summary_on">DÃŧymə gÃļstərilir. Oynatma sÃŧrətin standart olaraq qaytarmaq ÃŧçÃŧn toxunub saxla</string>
<string name="revanced_playback_speed_dialog_button_summary_off">DÃŧymə gÃļstərilmir</string>
</patch>
<patch id="video.quality.button.videoQualityButtonPatch">
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
<string name="revanced_custom_speed_menu_title">Fərdi oynatma sÃŧrəti siyahÄąsÄą</string>
<string name="revanced_custom_speed_menu_summary_on">Fərdi sÃŧrət siyahÄąsÄą gÃļstərilir</string>
<string name="revanced_custom_speed_menu_summary_off">Fərdi sÃŧrət siyahÄąsÄą gÃļstərilmir</string>
<string name="revanced_restore_old_speed_menu_title">KÃļhnə oynatma sÃŧrəti menyusunu bərpa et</string>
<string name="revanced_restore_old_speed_menu_summary_on">KÃļhnə sÃŧrət menyusu gÃļstərilir</string>
<string name="revanced_restore_old_speed_menu_summary_off">MÃŧasir sÃŧrət menyusu gÃļstərilir</string>
<string name="revanced_custom_playback_speeds_title">Fərdi oynatma sÃŧrəti</string>
<string name="revanced_custom_playback_speeds_summary">Fərdi oynatma sÃŧrətlərini əlavə et və ya dəyiş</string>
<string name="revanced_custom_playback_speeds_invalid">Fərdi sÃŧrətlər %s dəyərindən az olmalÄądÄąr</string>

View File

@@ -137,7 +137,7 @@ Second \"item\" text"</string>
<string name="revanced_debug_logs_disabled">АдĐģĐ°Đ´Đ°Ņ‡ĐŊаĐĩ ĐģĐ°ĐŗĐ°Đ˛Đ°ĐŊĐŊĐĩ адĐēĐģŅŽŅ‡Đ°ĐŊа</string>
<string name="revanced_debug_logs_none_found">Đ›Đ°ĐŗŅ– ĐŊĐĩ СĐŊОКдСĐĩĐŊŅ‹</string>
<string name="revanced_debug_logs_copied_to_clipboard">Đ›Đ°ĐŗŅ– ҁĐēаĐŋŅ–ŅĐ˛Đ°ĐŊŅ‹</string>
<string name="revanced_debug_logs_failed_to_export">НĐĩ Đ°Ņ‚Ņ€Ņ‹ĐŧаĐģĐ°ŅŅ ŅĐēҁĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°Ņ†ŅŒ ĐģĐ°ĐŗŅ–: $s</string>
<string name="revanced_debug_logs_failed_to_export">НĐĩ ŅžĐ´Đ°ĐģĐžŅŅ ŅĐēҁĐŋĐ°Ņ€Ņ‚Đ°Đ˛Đ°Ņ†ŅŒ ĐļŅƒŅ€ĐŊаĐģŅ‹: %s</string>
<string name="revanced_debug_logs_clear_buffer_title">ĐŅ‡Ņ‹ŅŅ†Ņ–Ņ†ŅŒ адĐģĐ°Đ´Đ°Ņ‡ĐŊŅ‹Ņ ĐģĐ°ĐŗŅ–</string>
<string name="revanced_debug_logs_clear_buffer_summary">ĐŅ‡Ņ‹ŅˆŅ‡Đ°Đĩ ŅžŅĐĩ ĐˇĐ°Ņ…Đ°Đ˛Đ°ĐŊŅ‹Ņ адĐģĐ°Đ´Đ°Ņ‡ĐŊŅ‹Ņ ĐģĐ°ĐŗŅ– ReVanced</string>
<string name="revanced_debug_logs_clear_toast">Đ›Đ°ĐŗŅ– Đ°Ņ‡Ņ‹ŅˆŅ‡Đ°ĐŊŅ‹</string>
@@ -164,9 +164,6 @@ Second \"item\" text"</string>
<string name="revanced_hide_expandable_card_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ Ņ€Đ°ŅĐēĐģадваĐģҌĐŊŅƒŅŽ ĐēĐ°Ņ€Ņ‚Đē҃</string>
<string name="revanced_hide_expandable_card_summary_on">Đ Đ°ŅĐēĐģадваĐģҌĐŊĐ°Ņ ĐēĐ°Ņ€Ņ‚Đēа Đŋад Đ˛Ņ–Đ´ŅĐ° ŅŅ…Đ°Đ˛Đ°ĐŊĐ°Ņ</string>
<string name="revanced_hide_expandable_card_summary_off">Đ Đ°ŅĐēĐģадваĐģҌĐŊĐ°Ņ ĐēĐ°Ņ€Ņ‚Đēа Đŋад Đ˛Ņ–Đ´ŅĐ° ĐŋаĐēаСаĐŊĐ°Ņ</string>
<string name="revanced_hide_feed_survey_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ аĐŋŅ‹Ņ‚Đ°ĐŊĐŊŅ– ŅŅ‚ŅƒĐļĐēŅ–</string>
<string name="revanced_hide_feed_survey_summary_on">АĐŋŅ‹Ņ‚Đ°ĐŊĐŊŅ– ĐēаĐŊаĐģĐ°Ņž ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹Ņ</string>
<string name="revanced_hide_feed_survey_summary_off">ПаĐēĐ°ĐˇĐ˛Đ°ŅŽŅ†Ņ†Đ° Đ´Đ°ŅĐģĐĩдаваĐŊĐŊŅ– ĐēĐ°Ņ€ĐŧĐžŅž</string>
<string name="revanced_hide_floating_microphone_button_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋĐģĐ°Đ˛Đ°ŅŽŅ‡ŅƒŅŽ ĐēĐŊĐžĐŋĐē҃ ĐŧŅ–ĐēŅ€Đ°Ņ„ĐžĐŊа</string>
<string name="revanced_hide_floating_microphone_button_summary_on">ПĐģĐ°Đ˛Đ°ŅŽŅ‡Đ°Ņ ĐēĐŊĐžĐŋĐēа ĐŧŅ–ĐēŅ€Đ°Ņ„ĐžĐŊа Ņž ĐŋĐžŅˆŅƒĐē҃ ŅŅ…Đ°Đ˛Đ°ĐŊĐ°Ņ</string>
<string name="revanced_hide_floating_microphone_button_summary_off">ПĐģĐ°Đ˛Đ°ŅŽŅ‡Đ°Ņ ĐēĐŊĐžĐŋĐēа ĐŧŅ–ĐēŅ€Đ°Ņ„ĐžĐŊа Ņž ĐŋĐžŅˆŅƒĐē҃ ĐŋаĐēаСаĐŊа</string>
@@ -192,7 +189,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_movies_section_summary_on">РаСдСĐĩĐģ ҄ҖĐģҌĐŧĐ°Ņž ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_hide_movies_section_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. -->
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
<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>
@@ -204,6 +201,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_show_more_button_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐēĐŊĐžĐŋĐē҃ \"ПаĐēĐ°ĐˇĐ°Ņ†ŅŒ йОĐģҌ҈\"</string>
<string name="revanced_hide_show_more_button_summary_on">КĐŊĐžĐŋĐēа ÂĢПаĐēĐ°ĐˇĐ°Ņ†ŅŒ йОĐģҌ҈Âģ ҃ Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃ ŅŅ…Đ°Đ˛Đ°ĐŊĐ°Ņ</string>
<string name="revanced_hide_show_more_button_summary_off">КĐŊĐžĐŋĐēа ÂĢПаĐēĐ°ĐˇĐ°Ņ†ŅŒ йОĐģҌ҈Âģ ҃ Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃ ĐŋаĐēаСаĐŊĐ°Ņ</string>
<string name="revanced_hide_surveys_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ аĐŋŅ‹Ņ‚Đ°ĐŊĐŊŅ–</string>
<string name="revanced_hide_surveys_summary_on">АĐŋŅ‹Ņ‚Đ°ĐŊĐŊŅ– ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹Ņ</string>
<string name="revanced_hide_surveys_summary_off">АĐŋŅ‹Ņ‚Đ°ĐŊĐŊŅ– ĐŋаĐēаСаĐŊŅ‹</string>
<string name="revanced_hide_ticket_shelf_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋаĐģŅ–Ņ†Ņƒ ĐąŅ–ĐģĐĩŅ‚Đ°Ņž</string>
<string name="revanced_hide_ticket_shelf_summary_on">ПаĐģŅ–Ņ†Đ° ĐąŅ–ĐģĐĩŅ‚Đ°Ņž ŅŅ…Đ°Đ˛Đ°ĐŊа</string>
<string name="revanced_hide_ticket_shelf_summary_off">ПаĐģŅ–Ņ†Đ° ĐąŅ–ĐģĐĩŅ‚Đ°Ņž ĐŋаĐēаСаĐŊа</string>
@@ -251,8 +251,8 @@ Second \"item\" text"</string>
<string name="revanced_hide_timed_reactions_summary_on">Đ§Đ°ŅĐžĐ˛Ņ‹Ņ Ņ€ŅĐ°Đē҆ҋҖ ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹Ņ</string>
<string name="revanced_hide_timed_reactions_summary_off">ПаĐēĐ°ĐˇĐ˛Đ°ŅŽŅ†Ņ†Đ° Ņ€ŅĐ°Đē҆ҋҖ Đŋа Ņ‡Đ°ŅĐĩ</string>
<string name="revanced_hide_ai_generated_video_summary_section_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ÂĢЗводĐē҃ Đ˛Ņ–Đ´ŅĐ°, ĐˇĐŗĐĩĐŊŅŅ€Đ°Đ˛Đ°ĐŊŅƒŅŽ ŅˆŅ‚ŅƒŅ‡ĐŊŅ‹Đŧ Ņ–ĐŊŅ‚ŅĐģĐĩĐēŅ‚Đ°ĐŧÂģ</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">РаСдСĐĩĐģ СвОдĐēŅ– Đ˛Ņ–Đ´ŅĐ° ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">РаСдСĐĩĐģ СвОдĐēŅ– Đ˛Ņ–Đ´ŅĐ° ĐŋаĐēаСаĐŊŅ‹</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">РаСдСĐĩĐģ ĐˇĐŗĐĩĐŊĐĩŅ€Đ°Đ˛Đ°ĐŊҋ҅ ŅˆŅ‚ŅƒŅ‡ĐŊŅ‹Đŧ Ņ–ĐŊŅ‚ŅĐģĐĩĐēŅ‚Đ°Đŧ Đ˛Ņ–Đ´ŅĐ°-Ņ€ŅĐˇŅŽĐŧŅ ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">ПаĐēаСаĐŊŅ‹ Ņ€Đ°ĐˇĐ´ĐˇĐĩĐģ СвОдĐēŅ– Đ˛Ņ–Đ´ŅĐ°, ŅŅ‚Đ˛ĐžŅ€Đ°ĐŊаК ŅˆŅ‚ŅƒŅ‡ĐŊŅ‹Đŧ Ņ–ĐŊŅ‚ŅĐģĐĩĐēŅ‚Đ°Đŧ</string>
<string name="revanced_hide_ask_section_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ÂĢĐĄĐŋŅ‹Ņ‚Đ°Ņ†ŅŒÂģ</string>
<string name="revanced_hide_ask_section_summary_on">РаСдСĐĩĐģ ÂĢĐĄĐŋŅ‹Ņ‚Đ°Ņ†ŅŒÂģ ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_hide_ask_section_summary_off">РаСдСĐĩĐģ ÂĢĐĄĐŋŅ‹Ņ‚Đ°Ņ†ŅŒÂģ ĐŋаĐēаСаĐŊŅ‹</string>
@@ -280,19 +280,19 @@ 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>
<string name="revanced_hide_filter_bar_feed_in_history_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ҃ ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ–</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊа Ņž ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ–</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">ПаĐēаСаĐŊа Ņž ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ–</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ҃ Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊа Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">ПаĐēаСаĐŊа Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ҃ ĐˇĐ˛ŅĐˇĐ°ĐŊҋ҅ Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊа Ņž ĐˇĐ˛ŅĐˇĐ°ĐŊҋ҅ Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">ПаĐēаСаĐŊа Ņž ĐˇĐ˛ŅĐˇĐ°ĐŊҋ҅ Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ҃ Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊа Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">ПаĐēаСаĐŊа Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_filter_bar_feed_in_history_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ҃ ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ– ĐŋŅ€Đ°ĐŗĐģŅĐ´Đ°Ņž</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊа Ņž ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ– ĐŋŅ€Đ°ĐŗĐģŅĐ´Đ°Ņž</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">ПаĐēаСаĐŊŅ‹ Ņž ĐŗŅ–ŅŅ‚ĐžŅ€Ņ‹Ņ– ĐŋŅ€Đ°ĐŗĐģŅĐ´Đ°Ņž</string>
<string name="revanced_channel_screen_title">ĐĄŅ‚Đ°Ņ€ĐžĐŊĐēа ĐēаĐŊаĐģа</string>
<string name="revanced_channel_screen_summary">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ айО ĐŋаĐēĐ°ĐˇĐ°Ņ†ŅŒ ĐēаĐŧĐŋаĐŊĐĩĐŊ҂ҋ ŅŅ‚Đ°Ņ€ĐžĐŊĐēŅ– ĐēаĐŊаĐģа</string>
<!-- 'For You' should be translated using the same localized wording YouTube displays. -->
@@ -316,11 +316,11 @@ Second \"item\" text"</string>
<string name="revanced_comments_screen_title">КаĐŧĐĩĐŊŅ‚Đ°Ņ€Ņ‹Ņ–</string>
<string name="revanced_comments_screen_summary">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ айО ĐŋаĐēĐ°ĐˇĐ°Ņ†ŅŒ ĐēаĐŧĐŋаĐŊĐĩĐŊ҂ҋ Ņ€Đ°ĐˇĐ´ĐˇĐĩĐģа ĐēаĐŧĐĩĐŊŅ‚Đ°Ņ€Ņ‹ŅŅž</string>
<string name="revanced_hide_comments_ai_chat_summary_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ СвОдĐē҃ Ņ‡Đ°Ņ‚Đ° ŅĐ° ŅˆŅ‚ŅƒŅ‡ĐŊŅ‹Đŧ Ņ–ĐŊŅ‚ŅĐģĐĩĐēŅ‚Đ°Đŧ</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">ЗводĐēа Ņ‡Đ°Ņ‚Đ° ŅŅ…Đ°Đ˛Đ°ĐŊĐ°Ņ</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">ЗводĐēа Ņ‡Đ°Ņ‚Đ° ĐŋаĐēаСаĐŊĐ°Ņ</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">ЗводĐēа Ņ‡Đ°Ņ‚Đ° ŅĐ° ŅˆŅ‚ŅƒŅ‡ĐŊŅ‹Đŧ Ņ–ĐŊŅ‚ŅĐģĐĩĐēŅ‚Đ°Đŧ ŅŅ…Đ°Đ˛Đ°ĐŊа</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">ЗводĐēа Ņ‡Đ°Ņ‚Đ° ŅĐ° ŅˆŅ‚ŅƒŅ‡ĐŊŅ‹Đŧ Ņ–ĐŊŅ‚ŅĐģĐĩĐēŅ‚Đ°Đŧ ĐŋаĐēаСаĐŊа</string>
<string name="revanced_hide_comments_ai_summary_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ СвОдĐē҃ ĐēаĐŧĐĩĐŊŅ‚Đ°Ņ€Ņ‹ŅŅž ŅĐ° ŅˆŅ‚ŅƒŅ‡ĐŊŅ‹Đŧ Ņ–ĐŊŅ‚ŅĐģĐĩĐēŅ‚Đ°Đŧ</string>
<string name="revanced_hide_comments_ai_summary_summary_on">ЗводĐēа ĐēаĐŧĐĩĐŊŅ‚Đ°Ņ€Ņ‹ŅŅž ŅŅ…Đ°Đ˛Đ°ĐŊĐ°Ņ</string>
<string name="revanced_hide_comments_ai_summary_summary_off">ЗводĐēа ĐēаĐŧĐĩĐŊŅ‚Đ°Ņ€Ņ‹ŅŅž ĐŋаĐēаСаĐŊĐ°Ņ</string>
<string name="revanced_hide_comments_ai_summary_summary_on">ЗводĐēа ĐēаĐŧĐĩĐŊŅ‚Đ°Ņ€Ņ‹ŅŅž ŅˆŅ‚ŅƒŅ‡ĐŊĐ°ĐŗĐ° Ņ–ĐŊŅ‚ŅĐģĐĩĐēŅ‚Ņƒ ŅŅ…Đ°Đ˛Đ°ĐŊа</string>
<string name="revanced_hide_comments_ai_summary_summary_off">ЗводĐēа ĐēаĐŧĐĩĐŊŅ‚Đ°Ņ€Ņ‹ŅŅž ŅˆŅ‚ŅƒŅ‡ĐŊĐ°ĐŗĐ° Ņ–ĐŊŅ‚ŅĐģĐĩĐēŅ‚Ņƒ ĐŋаĐēаСаĐŊа</string>
<string name="revanced_hide_comments_channel_guidelines_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋŅ€Đ°Đ˛Ņ–ĐģŅ‹ ĐēаĐŊаĐģа</string>
<string name="revanced_hide_comments_channel_guidelines_summary_on">Đ ŅĐēаĐŧĐĩĐŊĐ´Đ°Ņ†Ņ‹Ņ– ĐēаĐŊаĐģа ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_hide_comments_channel_guidelines_summary_off">Đ ŅĐēаĐŧĐĩĐŊĐ´Đ°Ņ†Ņ‹Ņ– ĐēаĐŊаĐģа ĐŋаĐēаСаĐŊŅ‹</string>
@@ -419,7 +419,6 @@ Second \"item\" text"</string>
<string name="revanced_hide_shopping_links_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ҁĐŋĐ°ŅŅ‹ĐģĐēŅ– Đ´ĐģŅ ĐŋаĐē҃ĐŋаĐē</string>
<string name="revanced_hide_shopping_links_summary_on">ĐĄĐŋĐ°ŅŅ‹ĐģĐēŅ– ĐŊа ĐŋаĐē҃ĐŋĐēŅ– Ņž аĐŋŅ–ŅĐ°ĐŊĐŊŅ– Đ˛Ņ–Đ´ŅĐ° ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹Ņ</string>
<string name="revanced_hide_shopping_links_summary_off">ĐĄĐŋĐ°ŅŅ‹ĐģĐēŅ– ĐŊа ĐŋаĐē҃ĐŋĐēŅ– Ņž аĐŋŅ–ŅĐ°ĐŊĐŊŅ– Đ˛Ņ–Đ´ŅĐ° ĐŋаĐēаСаĐŊŅ‹</string>
<!-- 'View products' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_view_products_banner_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ йаĐŊŅŅ€ \"ĐŸŅ€Đ°ĐŗĐģĐĩдСĐĩŅ†ŅŒ Ņ‚Đ°Đ˛Đ°Ņ€Ņ‹\"</string>
<string name="revanced_hide_view_products_banner_summary_on">БаĐŊĐĩŅ€ ÂĢĐŸĐ°ĐŗĐģŅĐ´ĐˇĐĩŅ†ŅŒ Ņ‚Đ°Đ˛Đ°Ņ€Ņ‹Âģ Ņž ĐŊаĐēĐģĐ°Đ´Ņ†Ņ‹ Đ˛Ņ–Đ´ŅĐ° ŅŅ…Đ°Đ˛Đ°ĐŊŅ‹</string>
<string name="revanced_hide_view_products_banner_summary_off">БаĐŊĐĩŅ€ ÂĢĐŸĐ°ĐŗĐģŅĐ´ĐˇĐĩŅ†ŅŒ Ņ‚Đ°Đ˛Đ°Ņ€Ņ‹Âģ Ņž ĐŊаĐēĐģĐ°Đ´Ņ†Ņ‹ Đ˛Ņ–Đ´ŅĐ° ĐŋаĐēаСаĐŊŅ‹</string>
@@ -453,7 +452,7 @@ Second \"item\" text"</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_off">Đ‘ŅƒĐ´ĐˇĐĩ ĐŋаĐēаСаĐŊа Đ´Ņ‹ŅĐģĐžĐŗĐ°Đ˛Đ°Đĩ аĐēĐŊĐž</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">Đ“ŅŅ‚Đ° ĐŊĐĩ Đ°ĐąŅ‹Ņ…ĐžĐ´ĐˇŅ–Ņ†ŅŒ ŅƒĐˇŅ€ĐžŅŅ‚Đ°Đ˛Đ°Đĩ айĐŧĐĩĐļаваĐŊĐŊĐĩ. ЁĐŊ ĐŋŅ€ĐžŅŅ‚Đ° ĐŋҀҋĐŧаĐĩ ĐŗŅŅ‚Đ° Đ°ŅžŅ‚Đ°ĐŧĐ°Ņ‚Ņ‹Ņ‡ĐŊа.</string>
</patch>
<patch id="interaction.doubletap.disableChapterSkipDoubleTapPatch">
<patch id="interaction.doubletap.disableDoubleTapActionsPatch">
<string name="revanced_disable_chapter_skip_double_tap_title">АдĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐŋŅ€Đ°Đŋ҃ҁĐē Ņ€Đ°ĐˇĐ´ĐˇĐĩĐģа Đŋа дваКĐŊŅ‹Đŧ ĐŊĐ°Ņ†Ņ–ŅĐē҃</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_on">ДвайĐŊŅ‹ ĐŊĐ°Ņ†Ņ–ŅĐē ĐŊŅ–ĐēĐžĐģŅ– ĐŊĐĩ ĐŧĐžĐļа Đ˛Ņ‹ĐēĐģŅ–ĐēĐ°Ņ†ŅŒ ĐŋŅ€Đ°Đŋ҃ҁĐē да ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊĐ°ĐŗĐ°/ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅĐŗĐ° Ņ€Đ°ĐˇĐ´ĐˇĐĩĐģа</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_off">ДвайĐŊŅ‹ ĐŊĐ°Ņ†Ņ–ŅĐē ĐŧĐžĐļа Ņ‡Đ°Ņ ад Ņ‡Đ°ŅŅƒ Đ˛Ņ‹ĐēĐģŅ–ĐēĐ°Ņ†ŅŒ ĐŋŅ€Đ°Đŋ҃ҁĐē да ĐŊĐ°ŅŅ‚ŅƒĐŋĐŊĐ°ĐŗĐ°/ĐŋаĐŋŅŅ€ŅĐ´ĐŊŅĐŗĐ° Ņ€Đ°ĐˇĐ´ĐˇĐĩĐģа</string>
@@ -469,8 +468,15 @@ Second \"item\" text"</string>
<string name="revanced_external_downloader_action_button_summary_on">КĐŊĐžĐŋĐēа \"ĐĄĐŋаĐŧĐŋĐ°Đ˛Đ°Ņ†ŅŒ\" адĐēŅ€Ņ‹Đ˛Đ°Đĩ Đ˛Đ°Ņˆ СĐŊĐĩ҈ĐŊŅ– ĐˇĐ°ĐŗŅ€ŅƒĐˇĐŊŅ–Đē</string>
<string name="revanced_external_downloader_action_button_summary_off">КĐŊĐžĐŋĐēа \"ĐĄĐŋаĐŧĐŋĐ°Đ˛Đ°Ņ†ŅŒ\" адĐēŅ€Ņ‹Đ˛Đ°Đĩ ŅžĐģĐ°ŅĐŊŅƒŅŽ ĐŋŅ€Đ°ĐŗŅ€Đ°Đŧ҃ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēŅ– Ņž ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧĐĩ</string>
<string name="revanced_external_downloader_name_title">Назва ĐŋаĐēĐĩŅ‚Đ° ĐˇĐ°ĐŗŅ€ŅƒĐˇĐŊŅ–Đēа</string>
<string name="revanced_external_downloader_name_summary">ІĐŧŅ ĐŋаĐēĐĩŅ‚Đ° ŅžŅŅ‚Đ°ĐģŅĐ˛Đ°ĐŊаК СĐŊĐĩ҈ĐŊŅĐš ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧŅ‹ ĐˇĐ°ĐŗŅ€ŅƒĐˇĐēŅ–, ĐŊаĐŋҀҋĐēĐģад NewPipe айО Seal</string>
<string name="revanced_external_downloader_name_summary">Назва ĐŋаĐēĐĩŅ‚Đ° Đ˛Đ°ŅˆĐ°ĐŗĐ° ŅžŅŅ‚Đ°ĐģŅĐ˛Đ°ĐŊĐ°ĐŗĐ° СĐŊĐĩ҈ĐŊŅĐŗĐ° ҁĐŋаĐŧĐŋĐžŅžŅˆŅ‡Ņ‹Đēа ĐŋŅ€Đ°ĐŗŅ€Đ°ĐŧŅ‹</string>
<string name="revanced_external_downloader_other_item_hint">ĐŖĐ˛ŅĐ´ĐˇŅ–Ņ†Đĩ ĐŊĐ°ĐˇĐ˛Ņƒ ĐŋаĐēĐĩŅ‚Đ°</string>
<string name="revanced_external_downloader_other_item">ІĐŊŅˆĐ°Đĩ</string>
<string name="revanced_external_downloader_not_found_title">ĐŸŅ€Đ°ĐŗŅ€Đ°Đŧа ĐŊĐĩ ŅžŅŅ‚Đ°ĐŊĐžŅžĐģĐĩĐŊа</string>
<string name="revanced_external_downloader_not_installed_warning">%s ĐŊĐĩ ŅžŅŅ‚Đ°ĐģŅĐ˛Đ°ĐŊŅ‹. КаĐģŅ– ĐģĐ°ŅĐēа, ŅƒŅŅ‚Đ°ĐģŅŽĐšŅ†Đĩ ŅĐŗĐž.</string>
<string name="revanced_external_downloader_package_not_found_warning">"НĐĩ ŅžĐ´Đ°ĐģĐžŅŅ СĐŊĐ°ĐšŅŅ†Ņ– ŅžŅŅ‚Đ°ĐŊĐžŅžĐģĐĩĐŊŅƒŅŽ ĐŋŅ€Đ°ĐŗŅ€Đ°Đŧ҃ С ĐŊаСваК ĐŋаĐēĐĩŅ‚Đ°: %s
ĐŸŅ€Đ°Đ˛ĐĩҀ҆Đĩ, ҆Җ ĐŋŅ€Đ°Đ˛Ņ–ĐģҌĐŊĐ°Ņ ĐŊаСва ĐŋаĐēĐĩŅ‚Đ° Ņ– ҆Җ ŅžŅŅ‚Đ°ĐŊĐžŅžĐģĐĩĐŊа ĐŋŅ€Đ°ĐŗŅ€Đ°Đŧа"</string>
<string name="revanced_external_downloader_empty_warning">Назва ĐŋаĐēĐĩŅ‚Đ° ĐŊĐĩ ĐŧĐžĐļа ĐąŅ‹Ņ†ŅŒ ĐŋŅƒŅŅ‚ĐžĐš</string>
</patch>
<patch id="interaction.seekbar.disablePreciseSeekingGesturePatch">
<string name="revanced_disable_precise_seeking_gesture_title">АдĐēĐģŅŽŅ‡Ņ‹Ņ†ŅŒ ĐļŅŅŅ‚ даĐēĐģадĐŊĐ°ĐŗĐ° ĐŋĐžŅˆŅƒĐē҃</string>
@@ -730,27 +736,28 @@ Second \"item\" text"</string>
<string name="revanced_disable_rolling_number_animations_summary_off">ĐŸŅ€Đ°ĐēĐ°Ņ‚ĐŊŅ‹Ņ ĐģŅ–Ņ‡ĐąŅ‹ аĐŊŅ–ĐŧŅ–Ņ€Đ°Đ˛Đ°ĐŊŅ‹Ņ</string>
</patch>
<patch id="layout.hide.seekbar.hideSeekbarPatch">
<string name="revanced_hide_seekbar_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋаĐŊŅĐģҌ ĐŋĐžŅˆŅƒĐē҃ Ņž Đ˛Ņ–Đ´ŅĐ°ĐŋĐģŅĐĩҀҋ</string>
<string name="revanced_hide_seekbar_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋаĐŊŅĐģҌ ĐŋŅ€Đ°ĐŗŅ€ŅŅŅƒ Đ˛Ņ–Đ´ŅĐ°ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐģҌĐŊŅ–Đēа</string>
<string name="revanced_hide_seekbar_summary_on">ПаĐŊŅĐģҌ ĐŋĐžŅˆŅƒĐē҃ Đ˛Ņ–Đ´ŅĐ°ĐŋĐģŅĐĩŅ€Đ° ŅŅ…Đ°Đ˛Đ°ĐŊа</string>
<string name="revanced_hide_seekbar_summary_off">АдĐģŅŽŅŅ‚Ņ€ĐžŅžĐ˛Đ°ĐĩŅ†Ņ†Đ° ĐŋаĐŊŅĐģҌ ĐŋĐžŅˆŅƒĐē҃ Đ˛Ņ–Đ´ŅĐ°ĐŋĐģŅĐĩŅ€Đ°</string>
<string name="revanced_hide_seekbar_thumbnail_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋаĐŊŅĐģҌ ĐŋĐžŅˆŅƒĐē҃ Ņž ĐŧŅ–ĐŊŅ–ŅŅ†ŅŽŅ€Đ°Ņ… Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">ПаĐŊŅĐģҌ ĐŋĐžŅˆŅƒĐē҃ ŅŅĐēŅ–ĐˇĐ°Ņž ŅŅ…Đ°Đ˛Đ°ĐŊа</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">АдĐģŅŽŅŅ‚Ņ€ĐžŅžĐ˛Đ°ĐĩŅ†Ņ†Đ° ĐŋаĐŊŅĐģҌ ĐŋĐžŅˆŅƒĐē҃ ŅŅĐēŅ–ĐˇĐ°Ņž</string>
<!-- Seekbar shown inside video thumbnails found the home/feed/search/history. The seekbar shows the prior watch progress when the video was last open. -->
<string name="revanced_hide_seekbar_thumbnail_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ ĐŋаĐŊŅĐģҌ ĐŋŅ€Đ°ĐŗŅ€ŅŅŅƒ С ŅŅĐēŅ–ĐˇĐ°ĐŧŅ– Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">ПаĐŊŅĐģҌ ĐŋŅ€Đ°ĐŗŅ€ŅŅŅƒ С ŅŅĐēŅ–ĐˇĐ°ĐŧŅ– Đ˛Ņ–Đ´ŅĐ° ŅŅ…Đ°Đ˛Đ°ĐŊа</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">ПаĐŊŅĐģҌ ĐŋŅ€Đ°ĐŗŅ€ŅŅŅƒ С ŅŅĐēŅ–ĐˇĐ°ĐŧŅ– Đ˛Ņ–Đ´ŅĐ° ĐŋаĐēаСаĐŊа</string>
</patch>
<patch id="layout.hide.shorts.hideShortsComponentsResourcePatch">
<string name="revanced_shorts_player_screen_title">ĐŸŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐģҌĐŊŅ–Đē Shorts</string>
<string name="revanced_shorts_player_screen_summary">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ айО ĐŋаĐēĐ°ĐˇĐ°Ņ†ŅŒ ĐēаĐŧĐŋаĐŊĐĩĐŊ҂ҋ Ņž ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐģҌĐŊŅ–Đē҃ Shorts</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">ĐĄŅ…Đ°Đ˛Đ°ĐŊа Ņž ŅŅ‚ŅƒĐļ҆ҋ ÂĢГаĐģĐžŅžĐŊĐ°ŅÂģ Ņ– ĐˇĐ˛ŅĐˇĐ°ĐŊҋ҅ Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_hide_shorts_home_summary_off">ПаĐēаСаĐŊа Ņž ŅŅ‚ŅƒĐļ҆ҋ ÂĢГаĐģĐžŅžĐŊĐ°ŅÂģ Ņ– ĐˇĐ˛ŅĐˇĐ°ĐŊҋ҅ Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_hide_shorts_search_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ Shorts ҃ Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_shorts_search_summary_on">ĐĄŅ…Đ°Đ˛Đ°ĐŊŅ‹ Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<string name="revanced_hide_shorts_search_summary_off">ПаĐēаСаĐŊа Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</string>
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the Subscriptions tab. -->
<string name="revanced_hide_shorts_subscriptions_title">ĐĄŅ…Đ°Đ˛Đ°Ņ†ŅŒ 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">ĐĄŅ…Đ°Đ˛Đ°ĐŊŅ‹ Ņž Đ˛Ņ‹ĐŊŅ–ĐēĐ°Ņ… ĐŋĐžŅˆŅƒĐē҃</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>
@@ -1191,8 +1198,6 @@ Second \"item\" text"</string>
Đ“ŅŅ‚Đ° СĐŧĐĩĐŊŅ–Ņ†ŅŒ СĐŊĐĩ҈ĐŊŅ– Đ˛Ņ‹ĐŗĐģŅĐ´ Ņ– Ņ„ŅƒĐŊĐē҆ҋҖ ĐŋҀҋĐēĐģадаĐŊĐŊŅ, аĐģĐĩ ĐŧĐžĐŗŅƒŅ†ŅŒ ŅƒĐˇĐŊŅ–ĐēĐŊŅƒŅ†ŅŒ ĐŊĐĩĐ˛ŅĐ´ĐžĐŧŅ‹Ņ ĐŋĐ°ĐąĐžŅ‡ĐŊŅ‹Ņ ŅŅ„ĐĩĐē҂ҋ.
КаĐģŅ– ĐŋаСĐŊĐĩĐš ĐąŅƒĐ´ĐˇĐĩ адĐēĐģŅŽŅ‡Đ°ĐŊа, Ņ€ŅĐēаĐŧĐĩĐŊĐ´ŅƒĐĩŅ†Ņ†Đ° Đ°Ņ‡Ņ‹ŅŅ†Ņ–Ņ†ŅŒ даĐŊŅ‹Ņ ĐŋҀҋĐēĐģадаĐŊĐŊŅ, Đēай ĐŋаСйĐĩĐŗĐŊŅƒŅ†ŅŒ ĐŋаĐŧŅ‹ĐģаĐē ҃ Ņ–ĐŊŅ‚ŅŅ€Ņ„ĐĩĐšŅĐĩ."</string>
<!-- 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. -->
<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.01.34 - АдĐŊĐ°ŅžĐģĐĩĐŊĐŊĐĩ ŅŅ‚Đ°Ņ€Ņ‹Ņ… СĐŊĐ°Ņ‡ĐēĐžŅž ĐŊĐ°Đ˛Ņ–ĐŗĐ°Ņ†Ņ‹Ņ–</string>
@@ -1461,10 +1466,18 @@ Second \"item\" text"</string>
<string name="revanced_playback_speed_dialog_button_summary_on">КĐŊĐžĐŋĐēа ĐŋаĐēаСаĐŊа. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ Ņ– ŅžŅ‚Ņ€Ņ‹ĐŧĐģŅ–Đ˛Đ°ĐšŅ†Đĩ, Đēай ҁĐēŅ–ĐŊŅƒŅ†ŅŒ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†ŅŒ ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊŅ да ŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊаК</string>
<string name="revanced_playback_speed_dialog_button_summary_off">КĐŊĐžĐŋĐēа ĐŊĐĩ ĐŋаĐēаСваĐĩŅ†Ņ†Đ°</string>
</patch>
<patch id="video.quality.button.videoQualityButtonPatch">
<string name="revanced_video_quality_dialog_button_title">ПаĐēĐ°ĐˇĐ°Ņ†ŅŒ ĐēĐŊĐžĐŋĐē҃ ŅĐēĐ°ŅŅ†Ņ– Đ˛Ņ–Đ´ŅĐ°</string>
<string name="revanced_video_quality_dialog_button_summary_on">КĐŊĐžĐŋĐēа ĐŋаĐēаСаĐŊа. ĐĐ°Ņ†Ņ–ŅĐŊҖ҆Đĩ Ņ– ŅžŅ‚Ņ€Ņ‹ĐŧĐģŅ–Đ˛Đ°ĐšŅ†Đĩ, Đēай ҁĐēŅ–ĐŊŅƒŅ†ŅŒ ŅĐēĐ°ŅŅ†ŅŒ да Đŋа СĐŧĐ°ŅžŅ‡Đ°ĐŊĐŊŅ–</string>
<string name="revanced_video_quality_dialog_button_summary_off">КĐŊĐžĐŋĐēа ĐŊĐĩ ĐŋаĐēаСваĐĩŅ†Ņ†Đ°</string>
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
<string name="revanced_custom_speed_menu_title">МĐĩĐŊŅŽ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēОК ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊĐ¸Ņ</string>
<string name="revanced_custom_speed_menu_summary_on">МĐĩĐŊŅŽ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēОК ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ ĐžŅ‚ĐžĐąŅ€Đ°ĐļаĐĩŅ‚ŅŅ</string>
<string name="revanced_custom_speed_menu_summary_off">МĐĩĐŊŅŽ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēОК ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ ĐŊĐĩ ĐžŅ‚ĐžĐąŅ€Đ°ĐļаĐĩŅ‚ŅŅ</string>
<string name="revanced_restore_old_speed_menu_title">АдĐŊĐ°Đ˛Ņ–Ņ†ŅŒ ŅŅ‚Đ°Ņ€ĐžĐĩ ĐŧĐĩĐŊŅŽ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†Ņ– ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊŅ</string>
<string name="revanced_restore_old_speed_menu_summary_on">ПаĐēаСаĐŊа ŅŅ‚Đ°Ņ€ĐžĐĩ ĐŧĐĩĐŊŅŽ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†Ņ–</string>
<string name="revanced_restore_old_speed_menu_summary_off">ПаĐēаСаĐŊа ŅŅƒŅ‡Đ°ŅĐŊаĐĩ ĐŧĐĩĐŊŅŽ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†Ņ–</string>
<string name="revanced_custom_playback_speeds_title">ĐšĐ°Ņ€Ņ‹ŅŅ‚Đ°ĐģҌĐŊҖ҆ĐēŅ–Ņ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†Ņ– ĐŋŅ€Đ°ĐšĐŗŅ€Đ°Đ˛Đ°ĐŊĐŊŅ</string>
<string name="revanced_custom_playback_speeds_summary">Đ”ĐžĐąĐ°Đ˛ŅŒŅ‚Đĩ иĐģи иСĐŧĐĩĐŊĐ¸Ņ‚Đĩ ĐŋĐžĐģŅŒĐˇĐžĐ˛Đ°Ņ‚ĐĩĐģҌҁĐēŅƒŅŽ ҁĐēĐžŅ€ĐžŅŅ‚ŅŒ Đ˛ĐžŅĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐ´ĐĩĐŊĐ¸Ņ</string>
<string name="revanced_custom_playback_speeds_invalid">НĐĩŅŅ‚Đ°ĐŊĐ´Đ°Ņ€Ņ‚ĐŊŅ‹Ņ Ņ…ŅƒŅ‚ĐēĐ°ŅŅ†Ņ– ĐŋĐ°Đ˛Ņ–ĐŊĐŊŅ‹ ĐąŅ‹Ņ†ŅŒ ĐŧĐĩĐŊ҈ Са %s</string>

View File

@@ -137,7 +137,7 @@ Second \"item\" text"</string>
<string name="revanced_debug_logs_disabled">ĐžŅ‚ŅŅ‚Ņ€Đ°ĐŊŅĐ˛Đ°ĐŊĐĩŅ‚Đž ĐŊа ĐŗŅ€Đĩ҈Đēи Đĩ Đ´ĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐž</string>
<string name="revanced_debug_logs_none_found">НĐĩ ŅĐ° ĐŊаĐŧĐĩŅ€ĐĩĐŊи ĐģĐžĐŗĐžĐ˛Đĩ</string>
<string name="revanced_debug_logs_copied_to_clipboard">Đ›ĐžĐŗĐžĐ˛ĐĩŅ‚Đĩ ŅĐ° ĐēĐžĐŋĐ¸Ņ€Đ°ĐŊи</string>
<string name="revanced_debug_logs_failed_to_export">НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐĩĐēҁĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐģĐžĐŗĐžĐ˛Đĩ: $s</string>
<string name="revanced_debug_logs_failed_to_export">НĐĩ҃ҁĐŋĐĩ҈ĐŊĐž ĐĩĐēҁĐŋĐžŅ€Ņ‚Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐģĐžĐŗĐžĐ˛Đĩ: %s</string>
<string name="revanced_debug_logs_clear_buffer_title">Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ˛Đ°ĐŊĐĩ ĐŊа ĐģĐžĐŗĐžĐ˛ĐĩŅ‚Đĩ Са ĐžŅ‚ŅŅ‚Ņ€Đ°ĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŗŅ€Đĩ҈Đēи</string>
<string name="revanced_debug_logs_clear_buffer_summary">Đ˜ĐˇŅ‡Đ¸ŅŅ‚Đ˛Đ° Đ˛ŅĐ¸Ņ‡Đēи ŅŅŠŅ…Ņ€Đ°ĐŊĐĩĐŊи ĐģĐžĐŗĐžĐ˛Đĩ Са ĐžŅ‚ŅŅ‚Ņ€Đ°ĐŊŅĐ˛Đ°ĐŊĐĩ ĐŊа ĐŗŅ€Đĩ҈Đēи ĐŊа ReVanced</string>
<string name="revanced_debug_logs_clear_toast">Đ›ĐžĐŗĐžĐ˛ĐĩŅ‚Đĩ ŅĐ° Đ¸ĐˇŅ‡Đ¸ŅŅ‚ĐĩĐŊи</string>
@@ -164,9 +164,6 @@ Second \"item\" text"</string>
<string name="revanced_hide_expandable_card_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Ņ€Đ°ĐˇĐŗŅŠĐ˛Đ°ĐĩĐŧĐ°Ņ‚Đ° ĐēĐ°Ņ€Ņ‚Đ°</string>
<string name="revanced_hide_expandable_card_summary_on">Đ Đ°ĐˇĐŗŅŠĐ˛Đ°ĐĩĐŧĐ°Ņ‚Đ° ĐēĐ°Ņ€Ņ‚Đ° ĐŋОд видĐĩĐžĐēĐģиĐŋОвĐĩŅ‚Đĩ Đĩ ҁĐēŅ€Đ¸Ņ‚Đ°</string>
<string name="revanced_hide_expandable_card_summary_off">Đ Đ°ĐˇĐŗŅŠĐ˛Đ°ĐĩĐŧĐ°Ņ‚Đ° ĐēĐ°Ņ€Ņ‚Đ° ĐŋОд видĐĩĐžĐēĐģиĐŋОвĐĩŅ‚Đĩ Đĩ ĐŋĐžĐēаСаĐŊа</string>
<string name="revanced_hide_feed_survey_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа аĐŊĐēĐĩŅ‚Đ¸ в ĐĩĐŧĐ¸ŅĐ¸Đ¸Ņ‚Đĩ</string>
<string name="revanced_hide_feed_survey_summary_on">АĐŊĐēĐĩŅ‚Đ¸Ņ‚Đĩ Са ĐĩĐŧĐ¸ŅĐ¸Đ¸ ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_feed_survey_summary_off">АĐŊĐēĐĩŅ‚Đ¸Ņ‚Đĩ Са ĐĩĐŧĐ¸ŅĐ¸Đ¸ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
<string name="revanced_hide_floating_microphone_button_title">ПĐģĐ°Đ˛Đ°Ņ‰ ĐąŅƒŅ‚ĐžĐŊ Са ĐŧиĐēŅ€ĐžŅ„ĐžĐŊа</string>
<string name="revanced_hide_floating_microphone_button_summary_on">ПĐģĐ°Đ˛Đ°Ņ‰ ĐąŅƒŅ‚ĐžĐŊ Са ĐŧиĐēŅ€ĐžŅ„ĐžĐŊ ĐŋŅ€Đ¸ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
<string name="revanced_hide_floating_microphone_button_summary_off">ПĐģĐ°Đ˛Đ°Ņ‰Đ¸ŅŅ‚ ĐąŅƒŅ‚ĐžĐŊ Са ĐŧиĐēŅ€ĐžŅ„ĐžĐŊ в Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž Đĩ ĐŋĐžĐēаСаĐŊ</string>
@@ -192,7 +189,7 @@ Second \"item\" text"</string>
<string name="revanced_hide_movies_section_summary_on">ĐĄĐĩĐēŅ†Đ¸ŅŅ‚Đ° ҁ ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸ Đĩ ҁĐēŅ€Đ¸Ņ‚Đ°</string>
<string name="revanced_hide_movies_section_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. -->
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
<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>
@@ -204,6 +201,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_show_more_button_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐąŅƒŅ‚ĐžĐŊа \"ПоĐēаĐļи ĐžŅ‰Đĩ\"</string>
<string name="revanced_hide_show_more_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Са ĐŋĐžĐēаСваĐŊĐĩ ĐŊа ĐžŅ‰Đĩ в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
<string name="revanced_hide_show_more_button_summary_off">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Са ĐŋĐžĐēаСваĐŊĐĩ ĐŊа ĐžŅ‰Đĩ в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ Đĩ ĐŋĐžĐēаСаĐŊ</string>
<string name="revanced_hide_surveys_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐžŅƒŅ‡Đ˛Đ°ĐŊĐ¸Ņ</string>
<string name="revanced_hide_surveys_summary_on">АĐŊĐēĐĩŅ‚Đ¸Ņ‚Đĩ ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_surveys_summary_off">АĐŊĐēĐĩŅ‚Đ¸Ņ‚Đĩ ŅĐ° ĐŋĐžĐēаСаĐŊи</string>
<string name="revanced_hide_ticket_shelf_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Ņ€Đ°Ņ„Ņ‚Đ° Са йиĐģĐĩŅ‚Đ¸</string>
<string name="revanced_hide_ticket_shelf_summary_on">Đ Đ°Ņ„Ņ‚ŅŠŅ‚ Са йиĐģĐĩŅ‚Đ¸ Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
<string name="revanced_hide_ticket_shelf_summary_off">Đ Đ°Ņ„Ņ‚ŅŠŅ‚ Са йиĐģĐĩŅ‚Đ¸ Đĩ ĐŋĐžĐēаСаĐŊ</string>
@@ -251,8 +251,8 @@ Second \"item\" text"</string>
<string name="revanced_hide_timed_reactions_summary_on">Đ’Ņ€ĐĩĐŧĐĩĐ˛Đ¸Ņ‚Đĩ Ņ€ĐĩаĐēŅ†Đ¸Đ¸ ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_timed_reactions_summary_off">Đ’Ņ€ĐĩĐŧĐĩĐ˛Đ¸Ņ‚Đĩ Ņ€ĐĩаĐēŅ†Đ¸Đ¸ ҁĐĩ ĐŋĐžĐēĐ°ĐˇĐ˛Đ°Ņ‚</string>
<string name="revanced_hide_ai_generated_video_summary_section_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа \"AI-ĐŗĐĩĐŊĐĩŅ€Đ¸Ņ€Đ°ĐŊĐž видĐĩĐž Ņ€ĐĩĐˇŅŽĐŧĐĩ\"</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">ĐĄĐēŅ€Đ¸Ņ‚ Đĩ Ņ€Đ°ĐˇĐ´ĐĩĐģŅŠŅ‚ ҁ видĐĩĐž Ņ€ĐĩĐˇŅŽĐŧĐĩ</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">ПоĐēаСва ҁĐĩ Ņ€Đ°ĐˇĐ´ĐĩĐģŅŠŅ‚ ҁ видĐĩĐž Ņ€ĐĩĐˇŅŽĐŧĐĩ</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">РаСдĐĩĐģŅŠŅ‚ Са Ņ€ĐĩĐˇŅŽĐŧĐĩ ĐŊа видĐĩĐžĐēĐģиĐŋОвĐĩ, ĐŗĐĩĐŊĐĩŅ€Đ¸Ņ€Đ°ĐŊи ĐžŅ‚ AI, Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">РаСдĐĩĐģŅŠŅ‚ ҁ ĐŗĐĩĐŊĐĩŅ€Đ¸Ņ€Đ°ĐŊи ĐžŅ‚ AI Ņ€ĐĩĐˇŅŽĐŧĐĩŅ‚Đ° ĐŊа видĐĩĐžĐēĐģиĐŋОвĐĩ Đĩ ĐŋĐžĐēаСаĐŊ</string>
<string name="revanced_hide_ask_section_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа „ПоĐŋĐ¸Ņ‚Đ°Đšâ€œ</string>
<string name="revanced_hide_ask_section_summary_on">ĐĄĐēŅ€Đ¸Ņ‚ Ņ€Đ°ĐˇĐ´ĐĩĐģ „ПоĐŋĐ¸Ņ‚Đ°Đšâ€œ</string>
<string name="revanced_hide_ask_section_summary_off">ПоĐēаСваĐŊĐĩ ĐŊа Ņ€Đ°ĐˇĐ´ĐĩĐģ „ПоĐŋĐ¸Ņ‚Đ°Đšâ€œ</string>
@@ -280,19 +280,19 @@ 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>
<string name="revanced_hide_filter_bar_feed_in_history_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ в Đ¸ŅŅ‚ĐžŅ€Đ¸ŅŅ‚Đ°</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">ĐĄĐēŅ€Đ¸Ņ‚Đ¸ в Đ¸ŅŅ‚ĐžŅ€Đ¸ŅŅ‚Đ°</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">ПоĐēаСаĐŊи в Đ¸ŅŅ‚ĐžŅ€Đ¸ŅŅ‚Đ°</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">ĐĄĐēŅ€Đ¸Ņ‚Đž в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">ПоĐēаСаĐŊĐž в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ в ŅŅ€ĐžĐ´ĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ в ŅŅ€ĐžĐ´ĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">ПоĐēаСаĐŊĐž в ŅŅ€ĐžĐ´ĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">ĐĄĐēŅ€Đ¸Ņ‚Đž в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">ПоĐēаСаĐŊĐž в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_filter_bar_feed_in_history_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ в Đ¸ŅŅ‚ĐžŅ€Đ¸ŅŅ‚Đ° ĐŊа ĐŗĐģĐĩдаĐŊĐ¸ŅŅ‚Đ°</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">ĐĄĐēŅ€Đ¸Ņ‚Đž в Đ¸ŅŅ‚ĐžŅ€Đ¸ŅŅ‚Đ° ĐŊа ĐŗĐģĐĩдаĐŊĐĩ</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">ПоĐēаСаĐŊĐž в Đ¸ŅŅ‚ĐžŅ€Đ¸ŅŅ‚Đ° ĐŊа ĐŗĐģĐĩдаĐŊĐ¸ŅŅ‚Đ°</string>
<string name="revanced_channel_screen_title">ĐĄŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ° ĐŊа ĐēаĐŊаĐģа</string>
<string name="revanced_channel_screen_summary">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ иĐģи ĐŋĐžĐēаСваĐŊĐĩ ĐŊа ĐēĐžĐŧĐŋĐžĐŊĐĩĐŊŅ‚Đ¸ ĐŊа ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ†Đ°Ņ‚Đ° ĐŊа ĐēаĐŊаĐģа</string>
<!-- 'For You' should be translated using the same localized wording YouTube displays. -->
@@ -315,12 +315,12 @@ Second \"item\" text"</string>
<string name="revanced_hide_visit_store_button_summary_off">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ â€žĐŸĐžŅĐĩŅ‚ĐĩŅ‚Đĩ ĐŧĐ°ĐŗĐ°ĐˇĐ¸ĐŊа“ Đĩ ĐŋĐžĐēаСаĐŊ</string>
<string name="revanced_comments_screen_title">КоĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸</string>
<string name="revanced_comments_screen_summary">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ иĐģи ĐŋĐžĐēаСваĐŊĐĩ ĐŊа ҁĐĩĐēŅ†Đ¸ŅŅ‚Đ° Са ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸</string>
<string name="revanced_hide_comments_ai_chat_summary_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Ņ€ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа AI Chat</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">Đ ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа Ņ‡Đ°Ņ‚Đ° Đĩ ҁĐēŅ€Đ¸Ņ‚Đž</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">Đ ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа Ņ‡Đ°Ņ‚Đ° Đĩ ĐŋĐžĐēаСаĐŊĐž</string>
<string name="revanced_hide_comments_ai_chat_summary_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Ņ€ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа AI Ņ‡Đ°Ņ‚Đ°</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">Đ ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа AI Ņ‡Đ°Ņ‚Đ° Đĩ ҁĐēŅ€Đ¸Ņ‚Đž</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">Đ ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа AI Ņ‡Đ°Ņ‚Đ° Đĩ ĐŋĐžĐēаСаĐŊĐž</string>
<string name="revanced_hide_comments_ai_summary_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Ņ€ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа AI ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ</string>
<string name="revanced_hide_comments_ai_summary_summary_on">Đ ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ Đĩ ҁĐēŅ€Đ¸Ņ‚Đž</string>
<string name="revanced_hide_comments_ai_summary_summary_off">Đ ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸Ņ‚Đĩ Đĩ ĐŋĐžĐēаСаĐŊĐž</string>
<string name="revanced_hide_comments_ai_summary_summary_on">Đ ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа AI ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸ Đĩ ҁĐēŅ€Đ¸Ņ‚Đž</string>
<string name="revanced_hide_comments_ai_summary_summary_off">Đ ĐĩĐˇŅŽĐŧĐĩŅ‚Đž ĐŊа AI ĐēĐžĐŧĐĩĐŊŅ‚Đ°Ņ€Đ¸ Đĩ ĐŋĐžĐēаСаĐŊĐž</string>
<string name="revanced_hide_comments_channel_guidelines_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐŊĐ°ŅĐžĐēĐ¸Ņ‚Đĩ ĐŊа ĐēаĐŊаĐģа</string>
<string name="revanced_hide_comments_channel_guidelines_summary_on">ĐŖĐēаСаĐŊĐ¸ŅŅ‚Đ° Са ĐēаĐŊаĐģа ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_comments_channel_guidelines_summary_off">ĐŖĐēаСаĐŊĐ¸ŅŅ‚Đ° Са ĐēаĐŊаĐģа ŅĐ° ĐŋĐžĐēаСаĐŊи</string>
@@ -419,7 +419,6 @@ Second \"item\" text"</string>
<string name="revanced_hide_shopping_links_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа Đ˛Ņ€ŅŠĐˇĐēи Са ĐŋĐ°ĐˇĐ°Ņ€ŅƒĐ˛Đ°ĐŊĐĩ</string>
<string name="revanced_hide_shopping_links_summary_on">ЛиĐŊĐēОвĐĩŅ‚Đĩ Са ĐŋĐ°ĐˇĐ°Ņ€ŅƒĐ˛Đ°ĐŊĐĩ Đ˛ŅŠĐ˛ видĐĩĐž ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩŅ‚Đž ŅĐ° ҁĐēŅ€Đ¸Ņ‚Đ¸</string>
<string name="revanced_hide_shopping_links_summary_off">ЛиĐŊĐēОвĐĩŅ‚Đĩ Са ĐŋĐ°ĐˇĐ°Ņ€ŅƒĐ˛Đ°ĐŊĐĩ в ĐžĐŋĐ¸ŅĐ°ĐŊиĐĩŅ‚Đž ĐŊа видĐĩĐžĐēĐģиĐŋа ŅĐ° ĐŋĐžĐēаСаĐŊи</string>
<!-- 'View products' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_view_products_banner_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа йаĐŊĐĩŅ€Đ° â€žĐŸŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ¸â€œ</string>
<string name="revanced_hide_view_products_banner_summary_on">БаĐŊĐĩŅ€ŅŠŅ‚ Са ĐŋŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ¸ Đ˛ŅŠĐ˛ видĐĩĐž ĐŊĐ°ŅĐģĐ°ĐŗĐ˛Đ°ĐŊĐĩŅ‚Đž Đĩ ҁĐēŅ€Đ¸Ņ‚</string>
<string name="revanced_hide_view_products_banner_summary_off">БаĐŊĐĩŅ€ŅŠŅ‚ Са ĐŋŅ€ĐĩĐŗĐģĐĩĐ´ ĐŊа ĐŋŅ€ĐžĐ´ŅƒĐēŅ‚Đ¸ Đ˛ŅŠĐ˛ видĐĩĐž ĐŊĐ°ŅĐģĐ°ĐŗĐ˛Đ°ĐŊĐĩŅ‚Đž Đĩ ĐŋĐžĐēаСаĐŊ</string>
@@ -453,7 +452,7 @@ Second \"item\" text"</string>
<string name="revanced_remove_viewer_discretion_dialog_summary_off">ДиаĐģĐžĐŗĐžĐ˛Đ¸ŅŅ‚ ĐŋŅ€ĐžĐˇĐžŅ€Đĩ҆ ҉Đĩ ĐąŅŠĐ´Đĩ ĐŋĐžĐēаСаĐŊ</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">ĐĸаСи Ņ„ŅƒĐŊĐēŅ†Đ¸Ņ ĐŊĐĩ СаОйиĐēаĐģŅ Đ˛ŅŠĐˇŅ€Đ°ŅŅ‚ĐžĐ˛ĐžŅ‚Đž ĐžĐŗŅ€Đ°ĐŊĐ¸Ņ‡ĐĩĐŊиĐĩ. ĐĸŅ ĐŋŅ€ĐžŅŅ‚Đž ĐŋŅ€Đ¸ĐĩĐŧа Đ˛ŅŠĐˇŅ€Đ°ŅŅ‚ĐžĐ˛Đ°Ņ‚Đ° ĐŗŅ€Đ°ĐŊĐ¸Ņ†Đ° Đ°Đ˛Ņ‚ĐžĐŧĐ°Ņ‚Đ¸Ņ‡ĐŊĐž.</string>
</patch>
<patch id="interaction.doubletap.disableChapterSkipDoubleTapPatch">
<patch id="interaction.doubletap.disableDoubleTapActionsPatch">
<string name="revanced_disable_chapter_skip_double_tap_title">ДĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐŋŅ€ĐžĐŋ҃ҁĐēаĐŊĐĩ ĐŊа ĐŗĐģава ҁ двОКĐŊĐž Đ´ĐžĐēĐžŅĐ˛Đ°ĐŊĐĩ</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_on">ДвойĐŊĐžŅ‚Đž Đ´ĐžĐēĐžŅĐ˛Đ°ĐŊĐĩ ĐŊиĐēĐžĐŗĐ° ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐŋŅ€ĐĩдиСвиĐēа ĐŋŅ€ĐžĐŋ҃ҁĐēаĐŊĐĩ Đ´Đž ҁĐģĐĩĐ´Đ˛Đ°Ņ‰Đ°/ĐŋŅ€ĐĩĐ´Đ¸ŅˆĐŊа ĐŗĐģава</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_off">ДвойĐŊĐžŅ‚Đž Đ´ĐžĐēĐžŅĐ˛Đ°ĐŊĐĩ ĐŧĐžĐļĐĩ ĐŋĐžĐŊŅĐēĐžĐŗĐ° да ĐŋŅ€ĐĩдиСвиĐēа ĐŋŅ€ĐžĐŋ҃ҁĐēаĐŊĐĩ Đ´Đž ҁĐģĐĩĐ´Đ˛Đ°Ņ‰Đ°/ĐŋŅ€ĐĩĐ´Đ¸ŅˆĐŊа ĐŗĐģава</string>
@@ -469,8 +468,15 @@ Second \"item\" text"</string>
<string name="revanced_external_downloader_action_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Са Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ ĐžŅ‚Đ˛Đ°Ņ€Ņ Đ¸ĐˇĐąŅ€Đ°ĐŊĐžŅ‚Đž ĐžŅ‚ Đ’Đ°Ņ Đ˛ŅŠĐŊ҈ĐŊĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Са Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ</string>
<string name="revanced_external_downloader_action_button_summary_off">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Са Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ ĐžŅ‚Đ˛Đ°Ņ€Ņ Đ˛ĐŗŅ€Đ°Đ´ĐĩĐŊĐžŅ‚Đž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Са Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ</string>
<string name="revanced_external_downloader_name_title">ИĐŧĐĩ ĐŊа ĐŋаĐēĐĩŅ‚Đ° ĐŊа Đ¸ĐˇŅ‚ĐĩĐŗĐģŅŅ‰ĐžŅ‚Đž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ</string>
<string name="revanced_external_downloader_name_summary">ИĐŧĐĩ ĐŊа ĐŋаĐēĐĩŅ‚Đ° ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž Са Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ, ĐēĐ°Ņ‚Đž NewPipe иĐģи Seal</string>
<string name="revanced_external_downloader_name_summary">ИĐŧĐĩ ĐŊа ĐŋаĐēĐĩŅ‚Đ° ĐŊа Đ˛Đ°ŅˆĐĩŅ‚Đž иĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐŊĐž Đ˛ŅŠĐŊ҈ĐŊĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ Са Đ¸ĐˇŅ‚ĐĩĐŗĐģŅĐŊĐĩ</string>
<string name="revanced_external_downloader_other_item_hint">Đ’ŅŠĐ˛ĐĩĐ´ĐĩŅ‚Đĩ иĐŧĐĩŅ‚Đž ĐŊа ĐŋаĐēĐĩŅ‚Đ°</string>
<string name="revanced_external_downloader_other_item">Đ”Ņ€ŅƒĐŗĐ¸</string>
<string name="revanced_external_downloader_not_found_title">ĐŸŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž ĐŊĐĩ Đĩ иĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐŊĐž</string>
<string name="revanced_external_downloader_not_installed_warning">%s ĐŊĐĩ Đĩ иĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐŊ. ИĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐšŅ‚Đĩ ĐŗĐž.</string>
<string name="revanced_external_downloader_package_not_found_warning">"НĐĩ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ ĐŊаĐŧĐĩŅ€ĐĩĐŊĐž иĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐŊĐž ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩ ҁ иĐŧĐĩ ĐŊа ĐŋаĐēĐĩŅ‚: %s
ĐŸŅ€ĐžĐ˛ĐĩŅ€ĐĩŅ‚Đĩ даĐģи иĐŧĐĩŅ‚Đž ĐŊа ĐŋаĐēĐĩŅ‚Đ° Đĩ ĐŋŅ€Đ°Đ˛Đ¸ĐģĐŊĐž и ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž Đĩ иĐŊŅŅ‚Đ°ĐģĐ¸Ņ€Đ°ĐŊĐž"</string>
<string name="revanced_external_downloader_empty_warning">ИĐŧĐĩŅ‚Đž ĐŊа ĐŋаĐēĐĩŅ‚Đ° ĐŊĐĩ ĐŧĐžĐļĐĩ да ĐąŅŠĐ´Đĩ ĐŋŅ€Đ°ĐˇĐŊĐž</string>
</patch>
<patch id="interaction.seekbar.disablePreciseSeekingGesturePatch">
<string name="revanced_disable_precise_seeking_gesture_title">ДĐĩаĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊĐĩ ĐŊа ĐļĐĩŅŅ‚Đ° Са Ņ‚ĐžŅ‡ĐŊĐž Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ</string>
@@ -730,27 +736,28 @@ Second \"item\" text"</string>
<string name="revanced_disable_rolling_number_animations_summary_off">АĐŊиĐŧĐ°Ņ†Đ¸ŅŅ‚Đ° Đĩ аĐēŅ‚Đ¸Đ˛Đ¸Ņ€Đ°ĐŊа</string>
</patch>
<patch id="layout.hide.seekbar.hideSeekbarPatch">
<string name="revanced_hide_seekbar_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐģĐĩĐŊŅ‚Đ° Са Đ˛Ņ€ĐĩĐŧĐĩ ĐŊа ĐŋĐģĐĩĐšŅŠŅ€Đ°</string>
<string name="revanced_hide_seekbar_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐģĐĩĐŊŅ‚Đ°Ņ‚Đ° Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа видĐĩĐž ĐŋĐģĐĩĐšŅŠŅ€</string>
<string name="revanced_hide_seekbar_summary_on">ЛĐĩĐŊŅ‚Đ°Ņ‚Đ° Са Đ˛Ņ€ĐĩĐŧĐĩ ĐŊа ĐŋĐģĐĩĐšŅŠŅ€Đ° Đĩ ҁĐēŅ€Đ¸Ņ‚Đ°</string>
<string name="revanced_hide_seekbar_summary_off">ЛĐĩĐŊŅ‚Đ°Ņ‚Đ° Са Đ˛Ņ€ĐĩĐŧĐĩ ĐŊа ĐŋĐģĐĩĐšŅŠŅ€Đ° ҁĐĩ ĐŋĐžĐēаСва</string>
<string name="revanced_hide_seekbar_thumbnail_title">ĐĄĐēŅ€. ĐģĐĩĐŊŅ‚Đ° Са Đ˛Ņ€ĐĩĐŧĐĩ ĐŋŅ€Đ¸ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸Ņ‚Đĩ</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">ЛĐĩĐŊŅ‚Đ°Ņ‚Đ° Са Đ˛Ņ€ĐĩĐŧĐĩ ĐŋŅ€Đ¸ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸Ņ‚Đĩ Đĩ ҁĐēŅ€Đ¸Ņ‚Đ°</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">ЛĐĩĐŊŅ‚Đ°Ņ‚Đ° Са Đ˛Ņ€ĐĩĐŧĐĩ ĐŋŅ€Đ¸ ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸Ņ‚Đĩ ҁĐĩ ĐŋĐžĐēаСва</string>
<!-- Seekbar shown inside video thumbnails found the home/feed/search/history. The seekbar shows the prior watch progress when the video was last open. -->
<string name="revanced_hide_seekbar_thumbnail_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа ĐģĐĩĐŊŅ‚Đ°Ņ‚Đ° Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸ ĐŊа видĐĩĐžĐēĐģиĐŋОвĐĩ</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">ЛĐĩĐŊŅ‚Đ°Ņ‚Đ° Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸ ĐŊа видĐĩĐžĐēĐģиĐŋОвĐĩ Đĩ ҁĐēŅ€Đ¸Ņ‚Đ°</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">ЛĐĩĐŊŅ‚Đ°Ņ‚Đ° Са Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩ ĐŊа ĐŧиĐŊĐ¸Đ°Ņ‚ŅŽŅ€Đ¸ ĐŊа видĐĩĐžĐēĐģиĐŋОвĐĩ Đĩ ĐŋĐžĐēаСаĐŊа</string>
</patch>
<patch id="layout.hide.shorts.hideShortsComponentsResourcePatch">
<string name="revanced_shorts_player_screen_title">Đ˜ĐŗŅ€Đ°Ņ‡ ĐŊа Shorts</string>
<string name="revanced_shorts_player_screen_summary">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ иĐģи ĐŋĐžĐēаСваĐŊĐĩ ĐŊа ĐēĐžĐŧĐŋĐžĐŊĐĩĐŊŅ‚Đ¸ в Shorts ĐŋĐģĐĩĐšŅŠŅ€Đ°</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">ĐĄĐēŅ€Đ¸Ņ‚Đž в ĐĩĐŧĐ¸ŅĐ¸ŅŅ‚Đ° ĐĐ°Ņ‡Đ°ĐģĐž и ŅĐ˛ŅŠŅ€ĐˇĐ°ĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ</string>
<string name="revanced_hide_shorts_home_summary_off">ПоĐēаСаĐŊĐž в ĐĩĐŧĐ¸ŅĐ¸ŅŅ‚Đ° ĐĐ°Ņ‡Đ°ĐģĐž и ŅĐ˛ŅŠŅ€ĐˇĐ°ĐŊи видĐĩĐžĐēĐģиĐŋОвĐĩ</string>
<string name="revanced_hide_shorts_search_title">Shorts в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_shorts_search_summary_on">ĐĄĐēŅ€Đ¸Ņ‚Đ¸ в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<string name="revanced_hide_shorts_search_summary_off">ПоĐēаСаĐŊĐž в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</string>
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the Subscriptions tab. -->
<string name="revanced_hide_shorts_subscriptions_title">ĐĄĐēŅ€Đ¸Đ˛Đ°ĐŊĐĩ ĐŊа 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">ĐĄĐēŅ€Đ¸Ņ‚Đ¸ в Ņ€ĐĩĐˇŅƒĐģŅ‚Đ°Ņ‚Đ¸Ņ‚Đĩ ĐžŅ‚ Ņ‚ŅŠŅ€ŅĐĩĐŊĐĩŅ‚Đž</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>
@@ -1190,8 +1197,6 @@ Second \"item\" text"</string>
ĐĸОва ҉Đĩ ĐŋŅ€ĐžĐŧĐĩĐŊи Đ˛ŅŠĐŊ҈ĐŊĐ¸Ņ вид и Ņ„ŅƒĐŊĐēŅ†Đ¸Đ¸Ņ‚Đĩ ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž, ĐŊĐž ĐŧĐžĐļĐĩ да Đ˛ŅŠĐˇĐŊиĐēĐŊĐ°Ņ‚ ĐŊĐĩиСвĐĩҁ҂ĐŊи ŅŅ‚Ņ€Đ°ĐŊĐ¸Ņ‡ĐŊи ĐĩŅ„ĐĩĐēŅ‚Đ¸.
АĐēĐž ĐŋĐž-ĐēҊҁĐŊĐž ĐąŅŠĐ´Đĩ иСĐēĐģŅŽŅ‡ĐĩĐŊĐž, ĐŋŅ€ĐĩĐŋĐžŅ€ŅŠŅ‡Đ¸Ņ‚ĐĩĐģĐŊĐž Đĩ да Đ¸ĐˇŅ‡Đ¸ŅŅ‚Đ¸Ņ‚Đĩ даĐŊĐŊĐ¸Ņ‚Đĩ ĐŊа ĐŋŅ€Đ¸ĐģĐžĐļĐĩĐŊиĐĩŅ‚Đž, Са да ĐŋŅ€ĐĩĐ´ĐžŅ‚Đ˛Ņ€Đ°Ņ‚Đ¸Ņ‚Đĩ ĐŗŅ€Đĩ҈Đēи в ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģҁĐēĐ¸Ņ иĐŊŅ‚ĐĩҀ҄ĐĩĐšŅ."</string>
<!-- 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. -->
<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.01.34 - Đ’ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ŅŅ‚Đ°Ņ€Đ¸ иĐēĐžĐŊи Са ĐŊĐ°Đ˛Đ¸ĐŗĐ°Ņ†Đ¸Ņ</string>
@@ -1460,10 +1465,18 @@ Second \"item\" text"</string>
<string name="revanced_playback_speed_dialog_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Đĩ ĐŋĐžĐēаСаĐŊ. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ и ĐˇĐ°Đ´Ņ€ŅŠĐļŅ‚Đĩ, Са да Đ˛ŅŠŅ€ĐŊĐĩŅ‚Đĩ ҁĐēĐžŅ€ĐžŅŅ‚Ņ‚Đ° ĐŊа Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ ĐēҊĐŧ ŅŅ‚ĐžĐšĐŊĐžŅŅ‚Ņ‚Đ° ĐŋĐž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ</string>
<string name="revanced_playback_speed_dialog_button_summary_off">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ ĐŊĐĩ Đĩ ĐŋĐžĐēаСаĐŊ</string>
</patch>
<patch id="video.quality.button.videoQualityButtonPatch">
<string name="revanced_video_quality_dialog_button_title">ПоĐēаĐļи ĐąŅƒŅ‚ĐžĐŊа Са ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛Đž ĐŊа видĐĩĐžŅ‚Đž</string>
<string name="revanced_video_quality_dialog_button_summary_on">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ Đĩ ĐŋĐžĐēаСаĐŊ. ДоĐēĐžŅĐŊĐĩŅ‚Đĩ и ĐˇĐ°Đ´Ņ€ŅŠĐļŅ‚Đĩ, Са да Đ˛ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛Đ¸Ņ‚Đĩ ĐēĐ°Ņ‡ĐĩŅŅ‚Đ˛ĐžŅ‚Đž Đ´Đž ĐŋĐžĐ´Ņ€Đ°ĐˇĐąĐ¸Ņ€Đ°ĐŊĐĩ</string>
<string name="revanced_video_quality_dialog_button_summary_off">Đ‘ŅƒŅ‚ĐžĐŊŅŠŅ‚ ĐŊĐĩ Đĩ ĐŋĐžĐēаСаĐŊ</string>
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
<string name="revanced_custom_speed_menu_title">МĐĩĐŊŅŽŅ‚Đž Са ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģҁĐēа ҁĐēĐžŅ€ĐžŅŅ‚</string>
<string name="revanced_custom_speed_menu_summary_on">МĐĩĐŊŅŽŅ‚Đž Са ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģҁĐēа ҁĐēĐžŅ€ĐžŅŅ‚ ҁĐĩ ĐŋĐžĐēаСва</string>
<string name="revanced_custom_speed_menu_summary_off">МĐĩĐŊŅŽŅ‚Đž Са ĐŋĐžŅ‚Ņ€ĐĩĐąĐ¸Ņ‚ĐĩĐģҁĐēа ҁĐēĐžŅ€ĐžŅŅ‚ ĐŊĐĩ ҁĐĩ ĐŋĐžĐēаСва</string>
<string name="revanced_restore_old_speed_menu_title">Đ’ŅŠĐˇŅŅ‚Đ°ĐŊĐžĐ˛ŅĐ˛Đ°ĐŊĐĩ ĐŊа ŅŅ‚Đ°Ņ€ĐžŅ‚Đž ĐŧĐĩĐŊŅŽ Са ҁĐēĐžŅ€ĐžŅŅ‚ ĐŊа Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ</string>
<string name="revanced_restore_old_speed_menu_summary_on">ПоĐēаСва ҁĐĩ ŅŅ‚Đ°Ņ€ĐžŅ‚Đž ĐŧĐĩĐŊŅŽ Са ҁĐēĐžŅ€ĐžŅŅ‚</string>
<string name="revanced_restore_old_speed_menu_summary_off">ПоĐēаСва ҁĐĩ ĐŧОдĐĩŅ€ĐŊĐžŅ‚Đž ĐŧĐĩĐŊŅŽ Са ҁĐēĐžŅ€ĐžŅŅ‚</string>
<string name="revanced_custom_playback_speeds_title">ПĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊи ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ ĐŊа Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ</string>
<string name="revanced_custom_playback_speeds_summary">ДобавĐĩŅ‚Đĩ иĐģи ĐŋŅ€ĐžĐŧĐĩĐŊĐĩŅ‚Đĩ ҁĐēĐžŅ€ĐžŅŅ‚a ĐŊа Đ˛ŅŠĐˇĐŋŅ€ĐžĐ¸ĐˇĐ˛ĐĩĐļдаĐŊĐĩ</string>
<string name="revanced_custom_playback_speeds_invalid">ПĐĩŅ€ŅĐžĐŊаĐģĐ¸ĐˇĐ¸Ņ€Đ°ĐŊĐ¸Ņ‚Đĩ ҁĐēĐžŅ€ĐžŅŅ‚Đ¸ Ņ‚Ņ€ŅĐąĐ˛Đ° да ŅĐ° ĐŋĐž-ĐŧаĐģĐēи ĐžŅ‚ %s</string>

View File

@@ -133,7 +133,7 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_debug_logs_disabled">āĻĄāĻŋāĻŦāĻžāĻ— āϞāĻ—āĻŋāĻ‚ āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋāϝāĻŧ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_debug_logs_none_found">āϕ⧋āύ⧋ āϞāĻ— āĻĒāĻžāĻ“āϝāĻŧāĻž āϝāĻžāϝāĻŧāύāĻŋ</string>
<string name="revanced_debug_logs_copied_to_clipboard">āϞāĻ— āĻ…āύ⧁āϞāĻŋāĻĒāĻŋ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_debug_logs_failed_to_export">āϞāĻ— āϰāĻĒā§āϤāĻžāύāĻŋ āĻ•āϰāϤ⧇ āĻŦā§āϝāĻ°ā§āĻĨ: $s</string>
<string name="revanced_debug_logs_failed_to_export">āϞāĻ— āĻāĻ•ā§āϏāĻĒā§‹āĻ°ā§āϟ āĻ•āϰāĻž āϝāĻžāϝāĻŧāύāĻŋ: %s</string>
<string name="revanced_debug_logs_clear_buffer_title">āĻĄāĻŋāĻŦāĻžāĻ— āϞāĻ—āϗ⧁āϞāĻŋ āϏāĻžāĻĢ āĻ•āϰ⧁āύ</string>
<string name="revanced_debug_logs_clear_buffer_summary">āϏāĻŽāĻ¸ā§āϤ āϏāĻžā§āϚāĻŋāϤ ReVanced āĻĄāĻŋāĻŦāĻžāĻ— āϞāĻ— āϏāĻžāĻĢ āĻ•āϰ⧇</string>
<string name="revanced_debug_logs_clear_toast">āϞāĻ— āϏāĻžāĻĢ āĻ•āϰāĻž āĻšāϝāĻŧ⧇āϛ⧇</string>
@@ -160,9 +160,6 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_expandable_card_title">āĻĒā§āϰāϏāĻžāϰāĻŋāϤ āĻ•āĻžāĻ°ā§āĻĄ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_expandable_card_summary_on">āĻ­āĻŋāĻĄāĻŋāĻ“āϰ āύāĻŋāĻšā§‡ āĻĒā§āϰāϏāĻžāϰāĻŋāϤ āĻ•āĻžāĻ°ā§āĻĄ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_expandable_card_summary_off">āĻ­āĻŋāĻĄāĻŋāĻ“āϰ āύāĻŋāĻšā§‡ āĻĒā§āϰāϏāĻžāϰāĻŋāϤ āĻ•āĻžāĻ°ā§āĻĄ āĻĻ⧇āĻ–āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_feed_survey_title">āĻĢāĻŋāĻĄ āϜāϰāĻŋāĻĒ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_feed_survey_summary_on">āĻĢāĻŋāĻĄ āϜāϰāĻŋāĻĒ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_feed_survey_summary_off">āĻĢāĻŋāĻĄ āϜāϰāĻŋāĻĒ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_floating_microphone_button_title">āĻ­āĻžāϏāĻŽāĻžāύ āĻŽāĻžāχāĻ•ā§āϰ⧋āĻĢā§‹āύ āĻŦā§‹āϤāĻžāĻŽ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_floating_microphone_button_summary_on">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ⧇ āĻĢā§āϞ⧋āϟāĻŋāĻ‚ āĻŽāĻžāχāĻ•ā§āϰ⧋āĻĢā§‹āύ āĻŦā§‹āϤāĻžāĻŽ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_floating_microphone_button_summary_off">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ⧇ āĻ­āĻžāϏāĻŽāĻžāύ āĻŽāĻžāχāĻ•ā§āϰ⧋āĻĢā§‹āύ āĻŦā§‹āϤāĻžāĻŽ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ</string>
@@ -188,7 +185,7 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_movies_section_summary_on">āϚāϞāĻšā§āϚāĻŋāĻ¤ā§āϰ āĻŦāĻŋāĻ­āĻžāĻ— āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_movies_section_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. -->
This item appear in the Subscriptions feed for future livestreams or unreleased videos. -->
<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>
@@ -200,6 +197,9 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_show_more_button_title">\'āφāϰāĻ“ āĻĻ⧇āĻ–āĻžāύ\' āĻŦā§‹āϤāĻžāĻŽ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_show_more_button_summary_on">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ \"āφāϰāĻ“ āĻĻ⧇āĻ–āĻžāύ\" āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_show_more_button_summary_off">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ \"āφāϰāĻ“ āĻĻ⧇āĻ–āĻžāύ\" āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_hide_surveys_title">āϏāĻžāĻ°ā§āϭ⧇ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_surveys_summary_on">āϜāϰāĻŋāĻĒāϗ⧁āϞāĻŋ āϞ⧁āĻ•āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_hide_surveys_summary_off">āϜāϰāĻŋāĻĒāϗ⧁āϞāĻŋ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_hide_ticket_shelf_title">āϟāĻŋāĻ•āĻŋāϟ āϤāĻžāĻ• āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_ticket_shelf_summary_on">āϟāĻŋāĻ•āĻŋāϟ āϤāĻžāĻ• āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_ticket_shelf_summary_off">āϟāĻŋāĻ•āĻŋāϟ āϤāĻžāĻ• āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
@@ -247,8 +247,8 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_timed_reactions_summary_on">āϏāĻŽā§Ÿ āĻ…āύ⧁āϝāĻžā§Ÿā§€ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋ⧟āĻž āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_timed_reactions_summary_off">āϏāĻŽā§Ÿ āĻ…āύ⧁āϝāĻžā§Ÿā§€ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋ⧟āĻž āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_ai_generated_video_summary_section_title">\'AI-āĻœā§‡āύāĻžāϰ⧇āĻŸā§‡āĻĄ āĻ­āĻŋāĻĄāĻŋāĻ“ āϏāĻžāϰāϏāĻ‚āĻ•ā§āώ⧇āĻĒ\' āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">āĻ­āĻŋāĻĄāĻŋāĻ“ āϏāĻžāϰāϏāĻ‚āĻ•ā§āώ⧇āĻĒ āĻŦāĻŋāĻ­āĻžāĻ— āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">āĻ­āĻŋāĻĄāĻŋāĻ“ āϏāĻžāϰāϏāĻ‚āĻ•ā§āώ⧇āĻĒ āĻŦāĻŋāĻ­āĻžāĻ— āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_on">āĻāφāχ-āĻœā§‡āύāĻžāϰ⧇āĻŸā§‡āĻĄ āĻ­āĻŋāĻĄāĻŋāĻ“ āϏāĻžāϰāĻžāĻ‚āĻļ āĻŦāĻŋāĻ­āĻžāĻ— āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_ai_generated_video_summary_section_summary_off">āĻāφāχ-āĻœā§‡āύāĻžāϰ⧇āĻŸā§‡āĻĄ āĻ­āĻŋāĻĄāĻŋāĻ“ āϏāĻžāϰāĻžāĻ‚āĻļ āĻŦāĻŋāĻ­āĻžāĻ— āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_hide_ask_section_title">āϜāĻŋāĻœā§āĻžāĻžāϏāĻž āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_ask_section_summary_on">āϜāĻŋāĻœā§āĻžāĻžāϏāĻž āĻŦāĻŋāĻ­āĻžāĻ— āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_ask_section_summary_off">āϜāĻŋāĻœā§āĻžāĻžāϏāĻž āĻŦāĻŋāĻ­āĻžāĻ— āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
@@ -276,19 +276,19 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<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>
<string name="revanced_hide_filter_bar_feed_in_history_title">āχāϤāĻŋāĻšāĻžāϏ⧇ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">āχāϤāĻŋāĻšāĻžāϏ⧇ āϞ⧁āĻ•āĻžāύ⧋</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">āχāϤāĻŋāĻšāĻžāϏ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_title">āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_on">āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_filter_bar_feed_in_search_title">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_on">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_filter_bar_feed_in_search_summary_off">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_filter_bar_feed_in_history_title">āĻĻ⧇āĻ–āĻžāϰ āχāϤāĻŋāĻšāĻžāϏ⧇ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_on">āĻĻ⧇āĻ–āĻžāϰ āχāϤāĻŋāĻšāĻžāϏ⧇ āϞ⧁āĻ•āĻžāύ⧋</string>
<string name="revanced_hide_filter_bar_feed_in_history_summary_off">āĻĻ⧇āĻ–āĻžāϰ āχāϤāĻŋāĻšāĻžāϏ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_channel_screen_title">āĻšā§āϝāĻžāύ⧇āϞ āĻĒ⧃āĻˇā§āĻ āĻž</string>
<string name="revanced_channel_screen_summary">āĻšā§āϝāĻžāύ⧇āϞ āĻĒ⧃āĻˇā§āĻ āĻžāϰ āωāĻĒāĻžāĻĻāĻžāύāϗ⧁āϞāĻŋ āϞ⧁āĻ•āĻžāύ āĻŦāĻž āĻĻ⧇āĻ–āĻžāύ</string>
<!-- 'For You' should be translated using the same localized wording YouTube displays. -->
@@ -311,12 +311,12 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_visit_store_button_summary_off">āĻ¸ā§āĻŸā§‹āϰ āĻ­āĻŋāϜāĻŋāϟ āĻŦāĻžāϟāύ āĻĻ⧇āĻ–āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_comments_screen_title">āĻŽāĻ¨ā§āϤāĻŦā§āϝ</string>
<string name="revanced_comments_screen_summary">āĻŽāĻ¨ā§āϤāĻŦā§āϝ āĻŦāĻŋāĻ­āĻžāϗ⧇āϰ āωāĻĒāĻžāĻĻāĻžāύāϗ⧁āϞāĻŋ āϞ⧁āĻ•āĻžāύ āĻŦāĻž āĻĻ⧇āĻ–āĻžāύ⧎</string>
<string name="revanced_hide_comments_ai_chat_summary_title">āĻāφāχ āĻšā§āϝāĻžāϟ āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤāϏāĻžāϰ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">āĻšā§āϝāĻžāϟ āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤāϏāĻžāϰ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">āĻšā§āϝāĻžāϟ āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤāϏāĻžāϰ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_comments_ai_chat_summary_title">āĻāφāχ āĻšā§āϝāĻžāϟ āϏāĻžāϰāĻžāĻ‚āĻļ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_on">āĻāφāχ āĻšā§āϝāĻžāϟ āϏāĻžāϰāĻžāĻ‚āĻļ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_comments_ai_chat_summary_summary_off">āĻāφāχ āĻšā§āϝāĻžāϟ āϏāĻžāϰāĻžāĻ‚āĻļ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_hide_comments_ai_summary_title">āĻāφāχ āĻŽāĻ¨ā§āϤāĻŦā§āϝ āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤāϏāĻžāϰ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_comments_ai_summary_summary_on">āĻŽāĻ¨ā§āϤāĻŦā§āϝ āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤāϏāĻžāϰ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_comments_ai_summary_summary_off">āĻŽāĻ¨ā§āϤāĻŦā§āϝ āϏāĻ‚āĻ•ā§āώāĻŋāĻĒā§āϤāϏāĻžāϰ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_comments_ai_summary_summary_on">āĻāφāχ āĻŽāĻ¨ā§āϤāĻŦā§āϝ āϏāĻžāϰāĻžāĻ‚āĻļ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_comments_ai_summary_summary_off">āĻāφāχ āĻŽāĻ¨ā§āϤāĻŦā§āϝ āϏāĻžāϰāĻžāĻ‚āĻļ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_hide_comments_channel_guidelines_title">āĻšā§āϝāĻžāύ⧇āϞ āύāĻŋāĻ°ā§āĻĻ⧇āĻļāĻŋāĻ•āĻž āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_comments_channel_guidelines_summary_on">āĻšā§āϝāĻžāύ⧇āϞ āύāĻŋāĻ°ā§āĻĻ⧇āĻļāĻŋāĻ•āĻž āϞ⧁āĻ•āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_comments_channel_guidelines_summary_off">āĻšā§āϝāĻžāύ⧇āϞ āύāĻŋāĻ°ā§āĻĻ⧇āĻļāĻŋāĻ•āĻž āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
@@ -415,7 +415,6 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_hide_shopping_links_title">āϕ⧇āύāĻžāĻ•āĻžāϟāĻžāϰ āϞāĻŋāĻ™ā§āĻ• āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_shopping_links_summary_on">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻŦāĻŋāĻŦāϰāϪ⧇ āϕ⧇āύāĻžāĻ•āĻžāϟāĻžāϰ āϞāĻŋāĻ™ā§āĻ• āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_shopping_links_summary_off">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻŦāĻ°ā§āĻŖāύāĻžāϝāĻŧ āϕ⧇āύāĻžāĻ•āĻžāϟāĻžāϰ āϞāĻŋāĻ™ā§āĻ• āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ</string>
<!-- 'View products' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_view_products_banner_title">\'āĻĒāĻŖā§āϝ āĻĻ⧇āϖ⧁āύ\' āĻŦā§āϝāĻžāύāĻžāϰ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_view_products_banner_summary_on">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻ“āĻ­āĻžāϰāϞ⧇āϤ⧇ \"āĻĒāĻŖā§āϝ āĻĻ⧇āϖ⧁āύ\" āĻŦā§āϝāĻžāύāĻžāϰ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_view_products_banner_summary_off">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻ“āĻ­āĻžāϰāϞ⧇āϤ⧇ \"āĻĒāĻŖā§āϝ āĻĻ⧇āϖ⧁āύ\" āĻŦā§āϝāĻžāύāĻžāϰ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
@@ -449,7 +448,7 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_remove_viewer_discretion_dialog_summary_off">āĻĄāĻžā§ŸāĻžāϞāĻ— āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšāĻŦ⧇</string>
<string name="revanced_remove_viewer_discretion_dialog_user_dialog_message">āĻāϟāĻŋ āĻŦāϝāĻŧāϏ⧇āϰ āϏ⧀āĻŽāĻžāĻŦāĻĻā§āϧāϤāĻžāϕ⧇ āĻŦāĻžāχāĻĒāĻžāϏ āĻ•āϰ⧇ āύāĻžāĨ¤ āĻāϟāĻž āĻļ⧁āϧ⧁ āĻ¸ā§āĻŦāϝāĻŧāĻ‚āĻ•ā§āϰāĻŋāϝāĻŧāĻ­āĻžāĻŦ⧇ āĻ—ā§āϰāĻšāĻŖ āĻ•āϰ⧇āĨ¤</string>
</patch>
<patch id="interaction.doubletap.disableChapterSkipDoubleTapPatch">
<patch id="interaction.doubletap.disableDoubleTapActionsPatch">
<string name="revanced_disable_chapter_skip_double_tap_title">āĻĄāĻžāĻŦāϞ āĻŸā§āϝāĻžāĻĒ āĻ…āĻ§ā§āϝāĻžāϝāĻŧ āĻ¸ā§āĻ•āĻŋāĻĒ āĻ…āĻ•ā§āώāĻŽ āĻ•āϰ⧁āύ</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_on">āĻĄāĻžāĻŦāϞ āĻŸā§āϝāĻžāĻĒ āĻ•āĻ–āύāĻ“ āĻĒāϰāĻŦāĻ°ā§āϤ⧀/āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āĻ…āĻ§ā§āϝāĻžāϝāĻŧ⧇ āĻ¸ā§āĻ•āĻŋāĻĒ āĻŸā§āϰāĻŋāĻ—āĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇ āύāĻž</string>
<string name="revanced_disable_chapter_skip_double_tap_summary_off">āĻĄāĻžāĻŦāϞ āĻŸā§āϝāĻžāĻĒ āĻŽāĻžāĻā§‡ āĻŽāĻžāĻā§‡ āĻĒāϰāĻŦāĻ°ā§āϤ⧀/āĻĒā§‚āĻ°ā§āĻŦāĻŦāĻ°ā§āϤ⧀ āĻ…āĻ§ā§āϝāĻžāϝāĻŧ⧇ āĻ¸ā§āĻ•āĻŋāĻĒ āĻŸā§āϰāĻŋāĻ—āĻžāϰ āĻ•āϰāϤ⧇ āĻĒāĻžāϰ⧇</string>
@@ -465,8 +464,15 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_external_downloader_action_button_summary_on">āĻĄāĻžāωāύāϞ⧋āĻĄ āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āφāĻĒāύāĻžāϰ āĻŦāĻžāĻšāĻŋāϰ⧇āϰ āĻĄāĻžāωāύāϞ⧋āĻĄāĻžāϰ āϖ⧁āϞāĻŦ⧇</string>
<string name="revanced_external_downloader_action_button_summary_off">āĻĄāĻžāωāύāϞ⧋āĻĄ āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āύ⧇āϟāĻŋāĻ­ āχāύ-āĻ…ā§āϝāĻžāĻĒ āĻĄāĻžāωāύāϞ⧋āĻĄāĻžāϰ āϖ⧁āϞāĻŦ⧇</string>
<string name="revanced_external_downloader_name_title">āĻĄāĻžāωāύāϞ⧋āĻĄāĻžāϰ⧇āϰ āĻĒā§āϝāĻžāϕ⧇āϜ āύāĻžāĻŽ</string>
<string name="revanced_external_downloader_name_summary">āφāĻĒāύāĻžāϰ āχāύāĻ¸ā§āϟāϞ āĻ•āϰāĻž āĻŦāĻžāχāϰ⧇āϰ āĻĄāĻžāωāύāϞ⧋āĻĄāĻžāϰ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻĒā§āϝāĻžāϕ⧇āϜ āύāĻžāĻŽ, āϝ⧇āĻŽāύ NewPipe āĻŦāĻž Seal</string>
<string name="revanced_external_downloader_name_summary">āφāĻĒāύāĻžāϰ āχāύāĻ¸ā§āϟāϞ āĻ•āϰāĻž āĻŦāĻžāĻšā§āϝāĻŋāĻ• āĻĄāĻžāωāύāϞ⧋āĻĄāĻžāϰ āĻ…ā§āϝāĻžāĻĒ⧇āϰ āĻĒā§āϝāĻžāϕ⧇āĻœā§‡āϰ āύāĻžāĻŽ</string>
<string name="revanced_external_downloader_other_item_hint">āĻĒā§āϝāĻžāϕ⧇āĻœā§‡āϰ āύāĻžāĻŽ āϞāĻŋāϖ⧁āύ</string>
<string name="revanced_external_downloader_other_item">āĻ…āĻ¨ā§āϝāĻžāĻ¨ā§āϝ</string>
<string name="revanced_external_downloader_not_found_title">āĻ…ā§āϝāĻžāĻĒ āχāύāĻ¸ā§āϟāϞ āĻ•āϰāĻž āύ⧇āχ</string>
<string name="revanced_external_downloader_not_installed_warning">%s āχāύāĻ¸ā§āϟāϞ āĻ•āϰāĻž āύ⧇āχ, āχāύāĻ¸ā§āϟāϞ āĻ•āϰ⧁āύāĨ¤</string>
<string name="revanced_external_downloader_package_not_found_warning">"āĻĒā§āϝāĻžāϕ⧇āĻœā§‡āϰ āύāĻžāĻŽ: %s āϏāĻš āχāύāĻ¸ā§āϟāϞ āĻ•āϰāĻž āĻ…ā§āϝāĻžāĻĒāϟāĻŋ āϖ⧁āρāĻœā§‡ āĻĒāĻžāĻ“ā§ŸāĻž āϝāĻžā§ŸāύāĻŋ
āĻĒā§āϝāĻžāϕ⧇āĻœā§‡āϰ āύāĻžāĻŽāϟāĻŋ āϏāĻ āĻŋāĻ• āĻāĻŦāĻ‚ āĻ…ā§āϝāĻžāĻĒāϟāĻŋ āχāύāĻ¸ā§āϟāϞ āĻ•āϰāĻž āφāϛ⧇ āĻ•āĻŋāύāĻž āϤāĻž āϝāĻžāϚāĻžāχ āĻ•āϰ⧁āύ"</string>
<string name="revanced_external_downloader_empty_warning">āĻĒā§āϝāĻžāϕ⧇āĻœā§‡āϰ āύāĻžāĻŽ āĻ–āĻžāϞāĻŋ āϰāĻžāĻ–āĻž āϝāĻžāĻŦ⧇ āύāĻž</string>
</patch>
<patch id="interaction.seekbar.disablePreciseSeekingGesturePatch">
<string name="revanced_disable_precise_seeking_gesture_title">āĻ­āĻŋāĻĄāĻŋāĻ“āϰ āύāĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āϟ āĻ…āĻ‚āĻļ⧇ āϝāĻžāĻ“ā§ŸāĻžāϰ āĻ…āĻ™ā§āĻ—āĻ­āĻ™ā§āĻ—āĻŋ āύāĻŋāĻˇā§āĻ•ā§āϰāĻŋ⧟ āĻ•āϰ⧁āύ</string>
@@ -726,27 +732,28 @@ MicroG-āĻāϰ āϜāĻ¨ā§āϝ āĻŦā§āϝāĻžāϟāĻžāϰāĻŋ āĻ…āĻĒā§āϟāĻŋāĻŽāĻžāχāϜ
<string name="revanced_disable_rolling_number_animations_summary_off">āϰ⧋āϞāĻŋāĻ‚ āύāĻžāĻŽā§āĻŦāĻžāϰ āĻ…ā§āϝāĻžāύāĻŋāĻŽā§‡āĻŸā§‡āĻĄ</string>
</patch>
<patch id="layout.hide.seekbar.hideSeekbarPatch">
<string name="revanced_hide_seekbar_title">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ⧇ āϏāĻŋāĻ•āĻŦāĻžāϰ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_seekbar_title">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āϞ⧇āϝāĻŧāĻžāϰ⧇āϰ āϏāĻŋāĻ•āĻŦāĻžāϰ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_seekbar_summary_on">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ⧇ āϏāĻŋāĻ•āĻŦāĻžāϰ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_seekbar_summary_off">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĒā§āĻ˛ā§‡ā§ŸāĻžāϰ⧇ āϏāĻŋāĻ•āĻŦāĻžāϰ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_seekbar_thumbnail_title">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ⧇ āϏāĻŋāĻ•āĻŦāĻžāϰ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ⧇ āϏāĻŋāĻ•āĻŦāĻžāϰ āϞ⧁āĻ•āĻŋā§Ÿā§‡ āĻ°ā§Ÿā§‡āϛ⧇</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ⧇ āϏāĻŋāĻ•āĻŦāĻžāϰ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§Ÿā§‡āϛ⧇</string>
<!-- Seekbar shown inside video thumbnails found the home/feed/search/history. The seekbar shows the prior watch progress when the video was last open. -->
<string name="revanced_hide_seekbar_thumbnail_title">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ āϏāĻŋāĻ•āĻŦāĻžāϰ āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_seekbar_thumbnail_summary_on">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ āϏāĻŋāĻ•āĻŦāĻžāϰ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_seekbar_thumbnail_summary_off">āĻ­āĻŋāĻĄāĻŋāĻ“ āĻĨāĻžāĻŽā§āĻŦāύ⧇āχāϞ āϏāĻŋāĻ•āĻŦāĻžāϰ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
</patch>
<patch id="layout.hide.shorts.hideShortsComponentsResourcePatch">
<string name="revanced_shorts_player_screen_title">Shorts āĻĒā§āϞ⧇āϝāĻŧāĻžāϰ</string>
<string name="revanced_shorts_player_screen_summary">Shorts āĻĒā§āϞ⧇āϝāĻŧāĻžāϰ⧇ āωāĻĒāĻžāĻĻāĻžāύ āϞ⧁āĻ•āĻžāύ⧋ āĻŦāĻž āĻĻ⧇āĻ–āĻžāύ⧋</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">āĻšā§‹āĻŽ āĻĢāĻŋāĻĄ āĻāĻŦāĻ‚ āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āϞ⧁āĻ•āĻžāύ⧋ āφāϛ⧇</string>
<string name="revanced_hide_shorts_home_summary_off">āĻšā§‹āĻŽ āĻĢāĻŋāĻĄ āĻāĻŦāĻ‚ āϏāĻŽā§āĻĒāĻ°ā§āĻ•āĻŋāϤ āĻ­āĻŋāĻĄāĻŋāĻ“āϤ⧇ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_hide_shorts_search_title">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ Shorts āϞ⧁āĻ•āĻžāύ</string>
<string name="revanced_hide_shorts_search_summary_on">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āϞ⧁āĻ•āĻžāύ⧋</string>
<string name="revanced_hide_shorts_search_summary_off">āϏāĻžāĻ°ā§āϚ āϰ⧇āϜāĻžāĻ˛ā§āĻŸā§‡ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇</string>
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays for the Subscriptions tab. -->
<string name="revanced_hide_shorts_subscriptions_title">āϏāĻžāĻŦāĻ¸ā§āĻ•ā§āϰāĻŋāĻĒāĻļāύāϏ āĻĢāĻŋāĻĄā§‡ 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">āĻ…āύ⧁āϏāĻ¨ā§āϧāĻžāύ āĻĢāϞāĻžāĻĢāϞ⧇ āϞ⧁āĻ•āĻžāύ⧋</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>
@@ -1186,8 +1193,6 @@ YouTube āϏ⧇āϟāĻŋāĻ‚āϏ⧇ āĻ…āĻŸā§‹ āĻĒā§āϞ⧇ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ
āĻāϟāĻŋ āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύāϟāĻŋāϰ āĻšā§‡āĻšāĻžāϰāĻž āĻāĻŦāĻ‚ āĻŦ⧈āĻļāĻŋāĻˇā§āĻŸā§āϝ āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰāĻŦ⧇, āϤāĻŦ⧇ āĻ…āϜāĻžāύāĻž āĻĒāĻžāĻ°ā§āĻļā§āĻŦ āĻĒā§āϰāϤāĻŋāĻ•ā§āϰāĻŋāϝāĻŧāĻž āĻšāϤ⧇ āĻĒāĻžāϰ⧇āĨ¤
āĻĒāϰ⧇ āϝāĻĻāĻŋ āĻŦāĻ¨ā§āϧ āĻ•āϰāĻž āĻšāϝāĻŧ, UI āĻŦāĻžāĻ— āĻāĻĄāĻŧāĻžāϤ⧇ āĻ…ā§āϝāĻžāĻĒā§āϞāĻŋāϕ⧇āĻļāύ āĻĄā§‡āϟāĻž āĻĒāϰāĻŋāĻˇā§āĻ•āĻžāϰ āĻ•āϰāĻžāϰ āĻĒāϰāĻžāĻŽāĻ°ā§āĻļ āĻĻ⧇āĻ“āϝāĻŧāĻž āĻšāϝāĻŧāĨ¤"</string>
<!-- 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. -->
<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.01.34 - āĻĒ⧁āϰāύ⧋ āύ⧇āĻ­āĻŋāϗ⧇āĻļāύ āφāχāĻ•āύ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰ⧁āύ</string>
@@ -1456,10 +1461,18 @@ DeArrow āϏāĻŽā§āĻĒāĻ°ā§āϕ⧇ āφāϰāĻ“ āϜāĻžāύāϤ⧇ āĻāĻ–āĻžāύ⧇ āϟ
<string name="revanced_playback_speed_dialog_button_summary_on">āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšā§Ÿā§‡āϛ⧇āĨ¤ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ¸ā§āĻĒā§€āĻĄ āĻĄāĻŋāĻĢāĻ˛ā§āĻŸā§‡ āϰāĻŋāϏ⧇āϟ āĻ•āϰāϤ⧇ āĻŸā§āϝāĻžāĻĒ āĻ•āϰ⧇ āϧāϰ⧇ āϰāĻžāϖ⧁āύāĨ¤</string>
<string name="revanced_playback_speed_dialog_button_summary_off">āĻŦā§‹āϤāĻžāĻŽ āĻĒā§āϰāĻĻāĻ°ā§āĻļāĻŋāϤ āĻšā§ŸāύāĻŋ</string>
</patch>
<patch id="video.quality.button.videoQualityButtonPatch">
<string name="revanced_video_quality_dialog_button_title">āĻ­āĻŋāĻĄāĻŋāĻ“ āϗ⧁āĻŖāĻŽāĻžāύ āĻŦā§‹āϤāĻžāĻŽ āĻĻ⧇āĻ–āĻžāύ</string>
<string name="revanced_video_quality_dialog_button_summary_on">āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇āĨ¤ āϗ⧁āĻŖāĻŽāĻžāύ āĻĄāĻŋāĻĢāĻ˛ā§āĻŸā§‡ āϰāĻŋāϏ⧇āϟ āĻ•āϰāϤ⧇ āĻŸā§āϝāĻžāĻĒ āĻ•āϰ⧇ āϧāϰ⧇ āϰāĻžāϖ⧁āύāĨ¤</string>
<string name="revanced_video_quality_dialog_button_summary_off">āĻŦā§‹āϤāĻžāĻŽāϟāĻŋ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧāύāĻŋāĨ¤</string>
</patch>
<patch id="video.speed.custom.customPlaybackSpeedPatch">
<string name="revanced_custom_speed_menu_title">āĻ•āĻžāĻ¸ā§āϟāĻŽ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ—āϤāĻŋ āĻŽā§‡āύ⧁</string>
<string name="revanced_custom_speed_menu_summary_on">āĻ•āĻžāĻ¸ā§āϟāĻŽ āĻ¸ā§āĻĒāĻŋāĻĄ āĻŽā§‡āύ⧁ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāĻšā§āϛ⧇</string>
<string name="revanced_custom_speed_menu_summary_off">āĻ•āĻžāĻ¸ā§āϟāĻŽ āĻ¸ā§āĻĒāĻŋāĻĄ āĻŽā§‡āύ⧁ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāĻšā§āϛ⧇ āύāĻž</string>
<string name="revanced_restore_old_speed_menu_title">āĻĒ⧁āϰāĻžāύ⧋ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ—āϤāĻŋ āĻŽā§‡āύ⧁ āĻĒ⧁āύāϰ⧁āĻĻā§āϧāĻžāϰ āĻ•āϰ⧁āύ</string>
<string name="revanced_restore_old_speed_menu_summary_on">āĻĒ⧁āϰāĻžāύ⧋ āĻ—āϤāĻŋāϰ āĻŽā§‡āύ⧁ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_restore_old_speed_menu_summary_off">āφāϧ⧁āύāĻŋāĻ• āĻ—āϤāĻŋāϰ āĻŽā§‡āύ⧁ āĻĻ⧇āĻ–āĻžāύ⧋ āĻšāϝāĻŧ⧇āϛ⧇</string>
<string name="revanced_custom_playback_speeds_title">āύāĻŋāϜāĻ¸ā§āĻŦ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ¸ā§āĻĒāĻŋāĻĄ</string>
<string name="revanced_custom_playback_speeds_summary">āĻ•āĻžāĻ¸ā§āϟāĻŽ āĻĒā§āϞ⧇āĻŦā§āϝāĻžāĻ• āĻ—āϤāĻŋ āϝ⧋āĻ— āĻ•āϰ⧁āύ āĻ…āĻĨāĻŦāĻž āĻĒāϰāĻŋāĻŦāĻ°ā§āϤāύ āĻ•āϰ⧁āύ</string>
<string name="revanced_custom_playback_speeds_invalid">āĻ•āĻžāĻ¸ā§āϟāĻŽ āĻ—āϤāĻŋ %s āĻāϰ āĻšā§‡āϝāĻŧ⧇ āĻ•āĻŽ āĻšāϤ⧇ āĻšāĻŦ⧇</string>

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