Compare commits

...

81 Commits

Author SHA1 Message Date
semantic-release-bot
19140e5918 chore: Release v5.6.0-dev.1 [skip ci]
# [5.6.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.2...v5.6.0-dev.1) (2024-12-19)

### Features

* **YouTube:** Add `Open Shorts in regular player` patch ([#4153](https://github.com/ReVanced/revanced-patches/issues/4153)) ([1dde485](1dde485013))
2024-12-19 08:14:57 +00:00
LisoUseInAIKyrios
1dde485013 feat(YouTube): Add Open Shorts in regular player patch (#4153) 2024-12-19 12:11:19 +04:00
LisoUseInAIKyrios
5efcdd31c8 ci: Don't upload strings to Crowdin when pulling (#4164) 2024-12-18 19:04:09 +01:00
github-actions[bot]
e6529837cb chore: Sync translations (#4162) 2024-12-18 16:29:33 +04:00
semantic-release-bot
fe07033444 chore: Release v5.5.2-dev.2 [skip ci]
## [5.5.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.1...v5.5.2-dev.2) (2024-12-17)

### Bug Fixes

* **YouTube - Hide layout components:** Hide new kind of community post ([#4155](https://github.com/ReVanced/revanced-patches/issues/4155)) ([246333f](246333f3dc))
2024-12-17 15:50:15 +00:00
Bceez
246333f3dc fix(YouTube - Hide layout components): Hide new kind of community post (#4155) 2024-12-17 19:46:46 +04:00
semantic-release-bot
d82b02e4f5 chore: Release v5.5.2-dev.1 [skip ci]
## [5.5.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.1...v5.5.2-dev.1) (2024-12-17)

### Bug Fixes

* **YouTube - Miniplayer:** Use estimated maximum on screen size for devices with low density screens ([#4150](https://github.com/ReVanced/revanced-patches/issues/4150)) ([c87c788](c87c788a26))
* **YouTube - SponsorBlock:** Show create new segment error messages using a dialog ([#4148](https://github.com/ReVanced/revanced-patches/issues/4148)) ([44995a9](44995a9f15))
2024-12-17 00:25:18 +00:00
LisoUseInAIKyrios
44995a9f15 fix(YouTube - SponsorBlock): Show create new segment error messages using a dialog (#4148) 2024-12-17 04:22:37 +04:00
LisoUseInAIKyrios
c87c788a26 fix(YouTube - Miniplayer): Use estimated maximum on screen size for devices with low density screens (#4150) 2024-12-17 04:22:16 +04:00
github-actions[bot]
4ef30618d1 chore: Sync translations (#4151) 2024-12-17 04:21:11 +04:00
oSumAtrIX
b23e6c39fc ci: Schedule pulling strings separately from opening a PR (#4146) 2024-12-16 23:02:13 +01:00
semantic-release-bot
de26766543 chore: Release v5.5.1 [skip ci]
## [5.5.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1) (2024-12-16)

### Bug Fixes

* **YouTube:** Fix string translations ([5e8dfed](5e8dfed3e8))
2024-12-16 20:04:21 +00:00
oSumAtrIX
9168b5eaaf chore: Merge branch dev to main (#4142) 2024-12-16 21:01:56 +01:00
semantic-release-bot
c43b9b3b03 chore: Release v5.5.1-dev.1 [skip ci]
## [5.5.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1-dev.1) (2024-12-16)

### Bug Fixes

* **YouTube:** Fix string translations ([5e8dfed](5e8dfed3e8))
2024-12-16 20:00:52 +00:00
github-actions[bot]
5e8dfed3e8 fix(YouTube): Fix string translations 2024-12-16 23:58:23 +04:00
semantic-release-bot
d67dbba76f chore: Release v5.5.0 [skip ci]
# [5.5.0](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.5.0) (2024-12-16)

### Bug Fixes

* **Twitch:** Change recommended target to the latest app version ([9525137](9525137800))
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client   ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([5965478](59654788fc))
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([ccb6a7f](ccb6a7f161))

### Features

* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([a7aab9a](a7aab9aeca))
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([9e6669d](9e6669d962))
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([68304fd](68304fd96a))
2024-12-16 19:28:30 +00:00
oSumAtrIX
5dc93156e0 chore: Merge branch dev to main (#4123) 2024-12-16 20:25:26 +01:00
github-actions[bot]
5275413ab7 chore: Sync translations (#4136)
Co-authored-by: revanced-bot <github@revanced.app>
2024-12-16 20:24:58 +01:00
semantic-release-bot
248c05b670 chore: Release v5.5.0-dev.5 [skip ci]
# [5.5.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.4...v5.5.0-dev.5) (2024-12-16)

### Features

* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([9e6669d](9e6669d962))
2024-12-16 19:10:30 +00:00
LisoUseInAIKyrios
9e6669d962 feat(YouTube - Navigation buttons): Add options to disable translucent status bar and navigation bar (#4133) 2024-12-16 23:07:08 +04:00
semantic-release-bot
9c81d01cc8 chore: Release v5.5.0-dev.4 [skip ci]
# [5.5.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.3...v5.5.0-dev.4) (2024-12-16)

### Bug Fixes

* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client   ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([5965478](59654788fc))
2024-12-16 18:46:48 +00:00
LisoUseInAIKyrios
59654788fc fix(YouTube - Spoof video streams): Make livestreams start at the current time when using iOS client (#4137) 2024-12-16 22:43:50 +04:00
semantic-release-bot
4c44982cde chore: Release v5.5.0-dev.3 [skip ci]
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)

### Features

* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([a7aab9a](a7aab9aeca))
2024-12-16 18:18:23 +00:00
ILoveOpenSourceApplications
a7aab9aeca feat(YouTube - Hide feed components): Remove obsolete Hide search result shelf header option (#4134) 2024-12-16 19:15:22 +01:00
semantic-release-bot
7a8486f562 chore: Release v5.5.0-dev.2 [skip ci]
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)

### Bug Fixes

* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([ccb6a7f](ccb6a7f161))
2024-12-16 18:10:37 +00:00
oSumAtrIX
ccb6a7f161 fix(YouTube Music): Add Spoof client patch to fix playback (#4132) 2024-12-16 19:07:37 +01:00
LisoUseInAIKyrios
c792edfb77 chore: fix typo 2024-12-15 17:06:36 +04:00
semantic-release-bot
339cd6cc70 chore: Release v5.5.0-dev.1 [skip ci]
# [5.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.1-dev.1...v5.5.0-dev.1) (2024-12-15)

### Features

* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([68304fd](68304fd96a))
2024-12-15 12:55:13 +00:00
LisoUseInAIKyrios
68304fd96a feat(YouTube): Add Force original audio patch (#4122) 2024-12-15 16:51:34 +04:00
semantic-release-bot
4033048c9b chore: Release v5.4.1-dev.1 [skip ci]
## [5.4.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.4.1-dev.1) (2024-12-14)

### Bug Fixes

* **Twitch:** Change recommended target to the latest app version ([9525137](9525137800))
2024-12-14 20:18:28 +00:00
LisoUseInAIKyrios
9525137800 fix(Twitch): Change recommended target to the latest app version 2024-12-15 00:15:00 +04:00
semantic-release-bot
0cf05fa2b0 chore: Release v5.4.0 [skip ci]
# [5.4.0](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0) (2024-12-14)

### Bug Fixes

* **GmsCore support:** Adjust presentation of battery optimization dialog  ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([2062660](2062660d60))
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([18f1884](18f18849f3))
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([3c47bff](3c47bfff1a))
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([c348b10](c348b10a35))
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([3ec2577](3ec25778eb))
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([32be03c](32be03c28d))
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([11216cd](11216cd942))
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([78c5118](78c51182f2))

### Features

* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([89c45af](89c45afcc6))
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([c44a4af](c44a4af406))
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([b217ca9](b217ca9f9d))
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([8d06a4a](8d06a4a8ad))
2024-12-14 07:40:53 +00:00
oSumAtrIX
a9bfaf44e2 chore: Merge branch dev to main (#4078) 2024-12-14 08:37:30 +01:00
semantic-release-bot
7b08051371 chore: Release v5.4.0-dev.11 [skip ci]
# [5.4.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.10...v5.4.0-dev.11) (2024-12-14)

### Features

* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([b217ca9](b217ca9f9d))
2024-12-14 07:36:35 +00:00
oSumAtrIX
b217ca9f9d feat(Twitch): Make patches compatible with latest versions (#4099) 2024-12-14 08:33:27 +01:00
github-actions[bot]
9482092579 chore: Sync translations (#4116) 2024-12-14 11:18:19 +04:00
semantic-release-bot
134c2e52bd chore: Release v5.4.0-dev.10 [skip ci]
# [5.4.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.9...v5.4.0-dev.10) (2024-12-13)

### Bug Fixes

* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([c348b10](c348b10a35))
2024-12-13 21:36:09 +00:00
ILoveOpenSourceApplications
c348b10a35 fix(YouTube - Hide ads): Hide new type of featured promotions (#4113) 2024-12-14 01:33:25 +04:00
github-actions[bot]
9a9ec7ef18 chore: Sync translations (#4114) 2024-12-14 01:32:59 +04:00
oSumAtrIX
e746507339 ci: Upload PR build artifact 2024-12-12 23:17:44 +01:00
oSumAtrIX
862ca077db ci: Only build relevant project 2024-12-12 23:17:09 +01:00
semantic-release-bot
138d43b34b chore: Release v5.4.0-dev.9 [skip ci]
# [5.4.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.8...v5.4.0-dev.9) (2024-12-12)

### Features

* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([8d06a4a](8d06a4a8ad))
2024-12-12 18:42:30 +00:00
ILoveOpenSourceApplications
8d06a4a8ad feat(YouTube - Comments): Add Hide 'Chat summary' (#4110) 2024-12-12 22:39:08 +04:00
github-actions[bot]
d7ca7c1733 chore: Sync translations (#4105) 2024-12-12 19:01:48 +04:00
LisoUseInAIKyrios
8e0b7db82a refactor: Use raw string resources (#4109) 2024-12-12 17:01:16 +04:00
semantic-release-bot
b9d7867cee chore: Release v5.4.0-dev.8 [skip ci]
# [5.4.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.7...v5.4.0-dev.8) (2024-12-11)

### Bug Fixes

* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([11216cd](11216cd942))
2024-12-11 20:04:06 +00:00
LisoUseInAIKyrios
11216cd942 fix(YouTube Music - Bypass certificate checks): Add a recommended target version (#4104) 2024-12-11 21:00:32 +01:00
LisoUseInAIKyrios
b163e5f64d chore(YouTube): Simplify strings for translations 2024-12-11 17:54:17 +04:00
semantic-release-bot
5c2bbd0671 chore: Release v5.4.0-dev.7 [skip ci]
# [5.4.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.6...v5.4.0-dev.7) (2024-12-10)

### Bug Fixes

* **GmsCore support:** Adjust presentation of battery optimization dialog  ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([2062660](2062660d60))
2024-12-10 21:47:35 +00:00
LisoUseInAIKyrios
2062660d60 fix(GmsCore support): Adjust presentation of battery optimization dialog (#4091)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-12-11 01:43:57 +04:00
semantic-release-bot
2d9f08a08e chore: Release v5.4.0-dev.6 [skip ci]
# [5.4.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.5...v5.4.0-dev.6) (2024-12-10)

### Bug Fixes

* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([78c5118](78c51182f2))
2024-12-10 21:08:47 +00:00
LisoUseInAIKyrios
78c51182f2 fix(YouTube Music - Spoof video streams): Disable stable volume (#4097) 2024-12-11 01:04:48 +04:00
semantic-release-bot
feac2ab439 chore: Release v5.4.0-dev.5 [skip ci]
# [5.4.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.4...v5.4.0-dev.5) (2024-12-10)

### Bug Fixes

* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([32be03c](32be03c28d))
2024-12-10 20:46:59 +00:00
LisoUseInAIKyrios
32be03c28d fix(YouTube - Spoof video streams): Resolve playback of age restricted videos (#4096) 2024-12-11 00:42:56 +04:00
github-actions[bot]
6a345eee37 chore: Sync translations (#4093) 2024-12-10 21:26:57 +04:00
LisoUseInAIKyrios
61be7731e3 refactor(YouTube - Spoof streaming data): Add more debug logging 2024-12-10 21:12:38 +04:00
semantic-release-bot
8295356f88 chore: Release v5.4.0-dev.4 [skip ci]
# [5.4.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.3...v5.4.0-dev.4) (2024-12-10)

### Bug Fixes

* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([3ec2577](3ec25778eb))
2024-12-10 16:30:47 +00:00
LisoUseInAIKyrios
3ec25778eb fix(YouTube - Spoof video streams): Fix error toast that is sometimes shown (#4090) 2024-12-10 20:27:22 +04:00
LisoUseInAIKyrios
3faf0ac160 refactor(YouTube Music): Show error toast if GmsCore Support is included with root installation 2024-12-10 15:15:02 +04:00
LisoUseInAIKyrios
3ff559878b refactor(YouTube - Miniplayer): Use 'Default' language to describe unpatched Miniplayer type 2024-12-10 03:13:39 +04:00
LisoUseInAIKyrios
ed9c78da1e chore(YouTube - Spoof video streams): Improve error logging 2024-12-10 02:56:40 +04:00
semantic-release-bot
eefb59020e chore: Release v5.4.0-dev.3 [skip ci]
# [5.4.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.2...v5.4.0-dev.3) (2024-12-09)

### Bug Fixes

* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([18f1884](18f18849f3))
2024-12-09 20:50:09 +00:00
LisoUseInAIKyrios
18f18849f3 fix(TikTok - Settings): Use correct colors for dark mode (#4087) 2024-12-10 00:46:53 +04:00
semantic-release-bot
b172c38284 chore: Release v5.4.0-dev.2 [skip ci]
# [5.4.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.1...v5.4.0-dev.2) (2024-12-09)

### Bug Fixes

* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([3c47bff](3c47bfff1a))

### Features

* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([89c45af](89c45afcc6))
2024-12-09 17:52:14 +00:00
github-actions[bot]
5b15602896 chore: Sync translations (#4085) 2024-12-09 21:49:37 +04:00
Tim Schneeberger
89c45afcc6 feat: Add Internal data documents provider patch (#3830)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-12-09 18:46:31 +01:00
LisoUseInAIKyrios
3c47bfff1a fix(TikTok - SIM Spoof): Change patch to default off to fix login (#4084) 2024-12-09 18:45:42 +01:00
semantic-release-bot
6af8e1b625 chore: Release v5.4.0-dev.1 [skip ci]
# [5.4.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0-dev.1) (2024-12-09)

### Features

* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([c44a4af](c44a4af406))
2024-12-09 06:55:58 +00:00
1fexd
c44a4af406 feat(Change package name): Add options to change provider and permission package names to handle installation conflicts
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-12-09 07:53:05 +01:00
semantic-release-bot
cb857b0fce chore: Release v5.3.0 [skip ci]
# [5.3.0](https://github.com/ReVanced/revanced-patches/compare/v5.2.3...v5.3.0) (2024-12-09)

### Bug Fixes

* **Change package name:** Prevent applying the patch to known incompatible apps ([#3943](https://github.com/ReVanced/revanced-patches/issues/3943)) ([b04a11a](b04a11a885))
* **Reddit:** Fix patches by using correct extension class ([95d56b1](95d56b1529))
* **Sync for Reddit:** Fix patches by using correct extension name ([5ae76f4](5ae76f4df8))
* **Twitter:** Merge correct extension by depending on correct extension patch ([35594d0](35594d0a20))
* **YouTube - Spoof video streams:** Add missing preferred language preference to the settings ([6462fb8](6462fb8cba))
* **YouTube - Spoof video streams:** Enable opus codec by updating iOS client version ([#4063](https://github.com/ReVanced/revanced-patches/issues/4063)) ([d1ae1f1](d1ae1f1da7))
* **YouTube - Spoof video streams:** Update `Force AVC` client data ([#4064](https://github.com/ReVanced/revanced-patches/issues/4064)) ([4a88f65](4a88f650c2))
* **YouTube Music - Permanent shuffle:** Remove obsolete and non functional patch ([#4073](https://github.com/ReVanced/revanced-patches/issues/4073)) ([4983e02](4983e021f9))

### Features

* **Nyx:** Remove broken `Unlock pro` patch ([87fe83a](87fe83aacf))
* **YouTube - Spoof video streams:** Allow picking a default audio language track ([#4050](https://github.com/ReVanced/revanced-patches/issues/4050)) ([538ed6d](538ed6d876))
* **YouTube Music:** Add `Spoof video streams` patch to fix playback ([#4065](https://github.com/ReVanced/revanced-patches/issues/4065)) ([cf4456c](cf4456c2ba))
* **YouTube:** Add `Open videos fullscreen` patch ([#4069](https://github.com/ReVanced/revanced-patches/issues/4069)) ([bee917f](bee917f4ed))
2024-12-09 06:10:59 +00:00
oSumAtrIX
e0322afbf0 chore: Merge branch dev to main (#4060) 2024-12-09 07:06:49 +01:00
semantic-release-bot
5f02f583be chore: Release v5.3.0-dev.7 [skip ci]
# [5.3.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.6...v5.3.0-dev.7) (2024-12-09)

### Bug Fixes

* **YouTube - Spoof video streams:** Add missing preferred language preference to the settings ([6462fb8](6462fb8cba))
2024-12-09 02:23:18 +00:00
LisoUseInAIKyrios
6462fb8cba fix(YouTube - Spoof video streams): Add missing preferred language preference to the settings 2024-12-09 06:19:49 +04:00
github-actions[bot]
f9dcce927e chore: Sync translations (#4076) 2024-12-09 06:19:26 +04:00
github-actions[bot]
69f9ab8345 chore: Sync translations (#4075) 2024-12-09 05:15:29 +04:00
semantic-release-bot
dd400ac2a0 chore: Release v5.3.0-dev.6 [skip ci]
# [5.3.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.5...v5.3.0-dev.6) (2024-12-09)

### Features

* **YouTube - Spoof video streams:** Allow picking a default audio language track ([#4050](https://github.com/ReVanced/revanced-patches/issues/4050)) ([538ed6d](538ed6d876))
2024-12-09 01:14:21 +00:00
LisoUseInAIKyrios
538ed6d876 feat(YouTube - Spoof video streams): Allow picking a default audio language track (#4050) 2024-12-09 05:11:00 +04:00
semantic-release-bot
5ff94dc34a chore: Release v5.3.0-dev.5 [skip ci]
# [5.3.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.4...v5.3.0-dev.5) (2024-12-09)

### Bug Fixes

* **Change package name:** Prevent applying the patch to known incompatible apps ([#3943](https://github.com/ReVanced/revanced-patches/issues/3943)) ([b04a11a](b04a11a885))
* **YouTube Music - Permanent shuffle:** Remove obsolete and non functional patch ([#4073](https://github.com/ReVanced/revanced-patches/issues/4073)) ([4983e02](4983e021f9))

### Features

* **YouTube:** Add `Open videos fullscreen` patch ([#4069](https://github.com/ReVanced/revanced-patches/issues/4069)) ([bee917f](bee917f4ed))
2024-12-09 00:50:48 +00:00
LisoUseInAIKyrios
b04a11a885 fix(Change package name): Prevent applying the patch to known incompatible apps (#3943) 2024-12-09 04:46:47 +04:00
LisoUseInAIKyrios
4983e021f9 fix(YouTube Music - Permanent shuffle): Remove obsolete and non functional patch (#4073) 2024-12-09 04:44:12 +04:00
LisoUseInAIKyrios
bee917f4ed feat(YouTube): Add Open videos fullscreen patch (#4069)
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2024-12-09 04:43:20 +04:00
173 changed files with 23807 additions and 7938 deletions

View File

@@ -28,4 +28,10 @@ jobs:
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew build --no-daemon
run: ./gradlew :patches:buildAndroid --no-daemon
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: revanced-patches
path: patches/build/libs

View File

@@ -1,6 +1,8 @@
name: Pull strings
on:
schedule:
- cron: "0 */8 * * *"
workflow_dispatch:
jobs:
@@ -21,16 +23,20 @@ jobs:
uses: crowdin/github-action@v2
with:
config: crowdin.yml
upload_sources: false
download_translations: true
localization_branch_name: feat/translations
create_pull_request: true
pull_request_title: "chore: Sync translations"
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
pull_request_base_branch_name: "dev"
commit_message: "chore: Sync translations"
github_user_name: revanced-bot
github_user_email: github@revanced.app
create_pull_request: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
- name: Open pull request
if: github.event_name == 'workflow_dispatch'
uses: repo-sync/pull-request@v2
with:
source_branch: feat/translations
destination_branch: dev
pr_title: "chore: Sync translations"
pr_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"

View File

@@ -35,7 +35,7 @@ jobs:
- name: Build
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: ./gradlew build clean
run: ./gradlew :patches:buildAndroid clean
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@@ -1,3 +1,250 @@
# [5.6.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.2...v5.6.0-dev.1) (2024-12-19)
### Features
* **YouTube:** Add `Open Shorts in regular player` patch ([#4153](https://github.com/ReVanced/revanced-patches/issues/4153)) ([c7c5e5b](https://github.com/ReVanced/revanced-patches/commit/c7c5e5b2b9cf63d8225bb6bd5e735ddf945b6c29))
## [5.5.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.1...v5.5.2-dev.2) (2024-12-17)
### Bug Fixes
* **YouTube - Hide layout components:** Hide new kind of community post ([#4155](https://github.com/ReVanced/revanced-patches/issues/4155)) ([08f68cb](https://github.com/ReVanced/revanced-patches/commit/08f68cb5d33f2cfe656d2f93d159c69981f31418))
## [5.5.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.1...v5.5.2-dev.1) (2024-12-17)
### Bug Fixes
* **YouTube - Miniplayer:** Use estimated maximum on screen size for devices with low density screens ([#4150](https://github.com/ReVanced/revanced-patches/issues/4150)) ([2694158](https://github.com/ReVanced/revanced-patches/commit/2694158c3c9935ede21c96832533222f850068df))
* **YouTube - SponsorBlock:** Show create new segment error messages using a dialog ([#4148](https://github.com/ReVanced/revanced-patches/issues/4148)) ([5870906](https://github.com/ReVanced/revanced-patches/commit/587090636dfff0b358b15026cf7d47c65a4296dc))
## [5.5.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1) (2024-12-16)
### Bug Fixes
* **YouTube:** Fix string translations ([52e04d3](https://github.com/ReVanced/revanced-patches/commit/52e04d340c1a85f3d683c67a15ae96529432d5fe))
## [5.5.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1-dev.1) (2024-12-16)
### Bug Fixes
* **YouTube:** Fix string translations ([52e04d3](https://github.com/ReVanced/revanced-patches/commit/52e04d340c1a85f3d683c67a15ae96529432d5fe))
# [5.5.0](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.5.0) (2024-12-16)
### Bug Fixes
* **Twitch:** Change recommended target to the latest app version ([fb32972](https://github.com/ReVanced/revanced-patches/commit/fb32972f4de92dac1fc5d73f56a392a671c4e94b))
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([140f484](https://github.com/ReVanced/revanced-patches/commit/140f484b4b251b0dfa94163a63f61f45f5302052))
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
### Features
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([a2d2141](https://github.com/ReVanced/revanced-patches/commit/a2d2141cec9b0b4929e07a8010889b21c324b229))
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([f4aa440](https://github.com/ReVanced/revanced-patches/commit/f4aa4406080b91f01d623e54b11b99ea849ddcdf))
# [5.5.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.4...v5.5.0-dev.5) (2024-12-16)
### Features
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([a2d2141](https://github.com/ReVanced/revanced-patches/commit/a2d2141cec9b0b4929e07a8010889b21c324b229))
# [5.5.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.3...v5.5.0-dev.4) (2024-12-16)
### Bug Fixes
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([140f484](https://github.com/ReVanced/revanced-patches/commit/140f484b4b251b0dfa94163a63f61f45f5302052))
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)
### Features
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)
### Bug Fixes
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
# [5.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.1-dev.1...v5.5.0-dev.1) (2024-12-15)
### Features
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([f4aa440](https://github.com/ReVanced/revanced-patches/commit/f4aa4406080b91f01d623e54b11b99ea849ddcdf))
## [5.4.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.4.1-dev.1) (2024-12-14)
### Bug Fixes
* **Twitch:** Change recommended target to the latest app version ([fb32972](https://github.com/ReVanced/revanced-patches/commit/fb32972f4de92dac1fc5d73f56a392a671c4e94b))
# [5.4.0](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0) (2024-12-14)
### Bug Fixes
* **GmsCore support:** Adjust presentation of battery optimization dialog ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([5d8fc1b](https://github.com/ReVanced/revanced-patches/commit/5d8fc1bcd4e453298cfac086cdbdf279612bfb63))
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([6bd22ff](https://github.com/ReVanced/revanced-patches/commit/6bd22ffa7e8af4d8f5d2d3b1711bd92c44b4e4aa))
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([f4659a3](https://github.com/ReVanced/revanced-patches/commit/f4659a328eaf600e1e5f02a66fa2af4b6d8dc7c1))
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([13c7592](https://github.com/ReVanced/revanced-patches/commit/13c7592b21defd27e3a7aa9b219ffc0247bb5914))
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([4c46cb2](https://github.com/ReVanced/revanced-patches/commit/4c46cb27a02c6f29626cd769b6a8e825645d5b16))
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([839a404](https://github.com/ReVanced/revanced-patches/commit/839a4045f1bb1759d89047834e0b7695781e82a3))
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([17a5a6c](https://github.com/ReVanced/revanced-patches/commit/17a5a6c1691b0c23f601d3355b72f122c2bd5dcb))
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([16bb9df](https://github.com/ReVanced/revanced-patches/commit/16bb9dfc299612f3922724c136878606987ab132))
### Features
* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([cb22f65](https://github.com/ReVanced/revanced-patches/commit/cb22f652ed678d81ffda9ece659b3971225d6931))
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([75c740c](https://github.com/ReVanced/revanced-patches/commit/75c740c6ba2e0c62e567f7dc90cdad368fc4f372))
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([eecfbb7](https://github.com/ReVanced/revanced-patches/commit/eecfbb7122a9072e55e687f2c003f63108654888))
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([269493c](https://github.com/ReVanced/revanced-patches/commit/269493cd198604f1438ea2850fb68fe900d0e56f))
# [5.4.0-dev.11](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.10...v5.4.0-dev.11) (2024-12-14)
### Features
* **Twitch:** Make patches compatible with latest versions ([#4099](https://github.com/ReVanced/revanced-patches/issues/4099)) ([eecfbb7](https://github.com/ReVanced/revanced-patches/commit/eecfbb7122a9072e55e687f2c003f63108654888))
# [5.4.0-dev.10](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.9...v5.4.0-dev.10) (2024-12-13)
### Bug Fixes
* **YouTube - Hide ads:** Hide new type of featured promotions ([#4113](https://github.com/ReVanced/revanced-patches/issues/4113)) ([13c7592](https://github.com/ReVanced/revanced-patches/commit/13c7592b21defd27e3a7aa9b219ffc0247bb5914))
# [5.4.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.8...v5.4.0-dev.9) (2024-12-12)
### Features
* **YouTube - Comments:** Add `Hide 'Chat summary'` ([#4110](https://github.com/ReVanced/revanced-patches/issues/4110)) ([269493c](https://github.com/ReVanced/revanced-patches/commit/269493cd198604f1438ea2850fb68fe900d0e56f))
# [5.4.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.7...v5.4.0-dev.8) (2024-12-11)
### Bug Fixes
* **YouTube Music - Bypass certificate checks:** Add a recommended target version ([#4104](https://github.com/ReVanced/revanced-patches/issues/4104)) ([17a5a6c](https://github.com/ReVanced/revanced-patches/commit/17a5a6c1691b0c23f601d3355b72f122c2bd5dcb))
# [5.4.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.6...v5.4.0-dev.7) (2024-12-10)
### Bug Fixes
* **GmsCore support:** Adjust presentation of battery optimization dialog ([#4091](https://github.com/ReVanced/revanced-patches/issues/4091)) ([5d8fc1b](https://github.com/ReVanced/revanced-patches/commit/5d8fc1bcd4e453298cfac086cdbdf279612bfb63))
# [5.4.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.5...v5.4.0-dev.6) (2024-12-10)
### Bug Fixes
* **YouTube Music - Spoof video streams:** Disable stable volume ([#4097](https://github.com/ReVanced/revanced-patches/issues/4097)) ([16bb9df](https://github.com/ReVanced/revanced-patches/commit/16bb9dfc299612f3922724c136878606987ab132))
# [5.4.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.4...v5.4.0-dev.5) (2024-12-10)
### Bug Fixes
* **YouTube - Spoof video streams:** Resolve playback of age restricted videos ([#4096](https://github.com/ReVanced/revanced-patches/issues/4096)) ([839a404](https://github.com/ReVanced/revanced-patches/commit/839a4045f1bb1759d89047834e0b7695781e82a3))
# [5.4.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.3...v5.4.0-dev.4) (2024-12-10)
### Bug Fixes
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([4c46cb2](https://github.com/ReVanced/revanced-patches/commit/4c46cb27a02c6f29626cd769b6a8e825645d5b16))
# [5.4.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.2...v5.4.0-dev.3) (2024-12-09)
### Bug Fixes
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([6bd22ff](https://github.com/ReVanced/revanced-patches/commit/6bd22ffa7e8af4d8f5d2d3b1711bd92c44b4e4aa))
# [5.4.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.1...v5.4.0-dev.2) (2024-12-09)
### Bug Fixes
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([f4659a3](https://github.com/ReVanced/revanced-patches/commit/f4659a328eaf600e1e5f02a66fa2af4b6d8dc7c1))
### Features
* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([cb22f65](https://github.com/ReVanced/revanced-patches/commit/cb22f652ed678d81ffda9ece659b3971225d6931))
# [5.4.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0-dev.1) (2024-12-09)
### Features
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([75c740c](https://github.com/ReVanced/revanced-patches/commit/75c740c6ba2e0c62e567f7dc90cdad368fc4f372))
# [5.3.0](https://github.com/ReVanced/revanced-patches/compare/v5.2.3...v5.3.0) (2024-12-09)
### Bug Fixes
* **Change package name:** Prevent applying the patch to known incompatible apps ([#3943](https://github.com/ReVanced/revanced-patches/issues/3943)) ([44936e7](https://github.com/ReVanced/revanced-patches/commit/44936e71e846f72f7279950232a5dba37765ceb3))
* **Reddit:** Fix patches by using correct extension class ([70bdc68](https://github.com/ReVanced/revanced-patches/commit/70bdc6840d465399625aa1ae0259f49e72711955))
* **Sync for Reddit:** Fix patches by using correct extension name ([030093e](https://github.com/ReVanced/revanced-patches/commit/030093e913aab3fab43935eedbaeba0f6c0491bb))
* **Twitter:** Merge correct extension by depending on correct extension patch ([8281cf6](https://github.com/ReVanced/revanced-patches/commit/8281cf6a3eead8cc25a277371e0b0ab2be982497))
* **YouTube - Spoof video streams:** Add missing preferred language preference to the settings ([630633c](https://github.com/ReVanced/revanced-patches/commit/630633cf57c65c65e5578046413e17670ae336e8))
* **YouTube - Spoof video streams:** Enable opus codec by updating iOS client version ([#4063](https://github.com/ReVanced/revanced-patches/issues/4063)) ([0af156f](https://github.com/ReVanced/revanced-patches/commit/0af156f18972c5f089af4bb69824968d2a47d18f))
* **YouTube - Spoof video streams:** Update `Force AVC` client data ([#4064](https://github.com/ReVanced/revanced-patches/issues/4064)) ([7d537dd](https://github.com/ReVanced/revanced-patches/commit/7d537ddff4bb5421fa320741275131a66ef5c7bb))
* **YouTube Music - Permanent shuffle:** Remove obsolete and non functional patch ([#4073](https://github.com/ReVanced/revanced-patches/issues/4073)) ([fbc6ab6](https://github.com/ReVanced/revanced-patches/commit/fbc6ab6a357b351f02d4d486ddc2072cf53199c3))
### Features
* **Nyx:** Remove broken `Unlock pro` patch ([1fe8b16](https://github.com/ReVanced/revanced-patches/commit/1fe8b164eab0c4fa80ab2da2581977f5111a2858))
* **YouTube - Spoof video streams:** Allow picking a default audio language track ([#4050](https://github.com/ReVanced/revanced-patches/issues/4050)) ([ede666b](https://github.com/ReVanced/revanced-patches/commit/ede666b5cb64fcbaa1334ad8bef79e2634ced113))
* **YouTube Music:** Add `Spoof video streams` patch to fix playback ([#4065](https://github.com/ReVanced/revanced-patches/issues/4065)) ([cf3116a](https://github.com/ReVanced/revanced-patches/commit/cf3116a7583d09c25c798a85687a056f143656f0))
* **YouTube:** Add `Open videos fullscreen` patch ([#4069](https://github.com/ReVanced/revanced-patches/issues/4069)) ([296d63b](https://github.com/ReVanced/revanced-patches/commit/296d63bd42c338a01efbcb2df702e5822d05a5f1))
# [5.3.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.6...v5.3.0-dev.7) (2024-12-09)
### Bug Fixes
* **YouTube - Spoof video streams:** Add missing preferred language preference to the settings ([630633c](https://github.com/ReVanced/revanced-patches/commit/630633cf57c65c65e5578046413e17670ae336e8))
# [5.3.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.5...v5.3.0-dev.6) (2024-12-09)
### Features
* **YouTube - Spoof video streams:** Allow picking a default audio language track ([#4050](https://github.com/ReVanced/revanced-patches/issues/4050)) ([ede666b](https://github.com/ReVanced/revanced-patches/commit/ede666b5cb64fcbaa1334ad8bef79e2634ced113))
# [5.3.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.4...v5.3.0-dev.5) (2024-12-09)
### Bug Fixes
* **Change package name:** Prevent applying the patch to known incompatible apps ([#3943](https://github.com/ReVanced/revanced-patches/issues/3943)) ([44936e7](https://github.com/ReVanced/revanced-patches/commit/44936e71e846f72f7279950232a5dba37765ceb3))
* **YouTube Music - Permanent shuffle:** Remove obsolete and non functional patch ([#4073](https://github.com/ReVanced/revanced-patches/issues/4073)) ([fbc6ab6](https://github.com/ReVanced/revanced-patches/commit/fbc6ab6a357b351f02d4d486ddc2072cf53199c3))
### Features
* **YouTube:** Add `Open videos fullscreen` patch ([#4069](https://github.com/ReVanced/revanced-patches/issues/4069)) ([296d63b](https://github.com/ReVanced/revanced-patches/commit/296d63bd42c338a01efbcb2df702e5822d05a5f1))
# [5.3.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.3...v5.3.0-dev.4) (2024-12-09)

View File

@@ -0,0 +1,3 @@
dependencies {
compileOnly(libs.annotation)
}

View File

@@ -0,0 +1,334 @@
package app.revanced.extension.all.misc.directory.documentsprovider;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract;
import android.provider.DocumentsProvider;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Objects;
/**
* A DocumentsProvider that allows access to the app's internal data directory.
*/
public class InternalDataDocumentsProvider extends DocumentsProvider {
private static final String[] rootColumns =
{"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"};
private static final String[] directoryColumns =
{"document_id", "mime_type", "_display_name", "last_modified", "flags",
"_size", "full_path", "lstat_info"};
private static final int S_IFLNK = 0x8000;
private String packageName;
private File dataDirectory;
/**
* Recursively delete a file or directory and all its children.
*
* @param root The file or directory to delete.
* @return True if the file or directory and all its children were successfully deleted.
*/
private static boolean deleteRecursively(File root) {
// If root is a directory, delete all children first
if (root.isDirectory()) {
try {
// Only delete recursively if the directory is not a symlink
if ((Os.lstat(root.getPath()).st_mode & S_IFLNK) != S_IFLNK) {
File[] files = root.listFiles();
if (files != null) {
for (File file : files) {
if (!deleteRecursively(file)) {
return false;
}
}
}
}
} catch (ErrnoException e) {
Log.e("InternalDocumentsProvider", "Failed to lstat " + root.getPath(), e);
}
}
// Delete file or empty directory
return root.delete();
}
/**
* Resolve the MIME type of a file based on its extension.
*
* @param file The file to resolve the MIME type for.
* @return The MIME type of the file.
*/
private static String resolveMimeType(File file) {
if (file.isDirectory()) {
return DocumentsContract.Document.MIME_TYPE_DIR;
}
String name = file.getName();
int indexOfExtDot = name.lastIndexOf('.');
if (indexOfExtDot < 0) {
// No extension
return "application/octet-stream";
}
String extension = name.substring(indexOfExtDot + 1).toLowerCase();
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
return mimeType != null ? mimeType : "application/octet-stream";
}
@Override
public final boolean onCreate() {
return true;
}
@Override
public final void attachInfo(Context context, ProviderInfo providerInfo) {
super.attachInfo(context, providerInfo);
this.packageName = context.getPackageName();
this.dataDirectory = context.getFilesDir().getParentFile();
}
@Override
public final String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException {
File directory = resolveDocumentId(parentDocumentId);
File file = new File(directory, displayName);
// If file already exists, append a number to the name
int i = 2;
while (file.exists()) {
file = new File(directory, displayName + " (" + i + ")");
i++;
}
try {
// Create the file or directory
if (mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR) ? file.mkdir() : file.createNewFile()) {
// Return the document ID of the new entity
if (!parentDocumentId.endsWith("/")) {
parentDocumentId = parentDocumentId + "/";
}
return parentDocumentId + file.getName();
}
} catch (IOException e) {
// Do nothing. We are throwing a FileNotFoundException later if the file could not be created.
}
throw new FileNotFoundException("Failed to create document in " + parentDocumentId + " with name " + displayName);
}
@Override
public final void deleteDocument(String documentId) throws FileNotFoundException {
File file = resolveDocumentId(documentId);
if (!deleteRecursively(file)) {
throw new FileNotFoundException("Failed to delete document " + documentId);
}
}
@Override
public final String getDocumentType(String documentId) throws FileNotFoundException {
return resolveMimeType(resolveDocumentId(documentId));
}
@Override
public final boolean isChildDocument(String parentDocumentId, String documentId) {
return documentId.startsWith(parentDocumentId);
}
@Override
public final String moveDocument(String sourceDocumentId, String sourceParentDocumentId, String targetParentDocumentId) throws FileNotFoundException {
File source = resolveDocumentId(sourceDocumentId);
File dest = resolveDocumentId(targetParentDocumentId);
File file = new File(dest, source.getName());
if (!file.exists() && source.renameTo(file)) {
// Return the new document ID
if (targetParentDocumentId.endsWith("/")) {
return targetParentDocumentId + file.getName();
}
return targetParentDocumentId + "/" + file.getName();
}
throw new FileNotFoundException("Failed to move document from " + sourceDocumentId + " to " + targetParentDocumentId);
}
@Override
public final ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal) throws FileNotFoundException {
File file = resolveDocumentId(documentId);
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode));
}
@Override
public final Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException {
if (parentDocumentId.endsWith("/")) {
parentDocumentId = parentDocumentId.substring(0, parentDocumentId.length() - 1);
}
if (projection == null) {
projection = directoryColumns;
}
MatrixCursor cursor = new MatrixCursor(projection);
File children = resolveDocumentId(parentDocumentId);
// Collect all children
File[] files = children.listFiles();
if (files != null) {
for (File file : files) {
addRowForDocument(cursor, parentDocumentId + "/" + file.getName(), file);
}
}
return cursor;
}
@Override
public final Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException {
if (projection == null) {
projection = directoryColumns;
}
MatrixCursor cursor = new MatrixCursor(projection);
addRowForDocument(cursor, documentId, null);
return cursor;
}
@Override
public final Cursor queryRoots(String[] projection) {
ApplicationInfo info = Objects.requireNonNull(getContext()).getApplicationInfo();
String appName = info.loadLabel(getContext().getPackageManager()).toString();
if (projection == null) {
projection = rootColumns;
}
MatrixCursor cursor = new MatrixCursor(projection);
MatrixCursor.RowBuilder row = cursor.newRow();
row.add(DocumentsContract.Root.COLUMN_ROOT_ID, this.packageName);
row.add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, this.packageName);
row.add(DocumentsContract.Root.COLUMN_SUMMARY, this.packageName);
row.add(DocumentsContract.Root.COLUMN_FLAGS,
DocumentsContract.Root.FLAG_LOCAL_ONLY |
DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD);
row.add(DocumentsContract.Root.COLUMN_TITLE, appName);
row.add(DocumentsContract.Root.COLUMN_MIME_TYPES, "*/*");
row.add(DocumentsContract.Root.COLUMN_ICON, info.icon);
return cursor;
}
@Override
public final void removeDocument(String documentId, String parentDocumentId) throws FileNotFoundException {
deleteDocument(documentId);
}
@Override
public final String renameDocument(String documentId, String displayName) throws FileNotFoundException {
File file = resolveDocumentId(documentId);
if (!file.renameTo(new File(file.getParentFile(), displayName))) {
throw new FileNotFoundException("Failed to rename document from " + documentId + " to " + displayName);
}
// Return the new document ID
return documentId.substring(0, documentId.lastIndexOf('/', documentId.length() - 2)) + "/" + displayName;
}
/**
* Resolve a file instance for a given document ID.
*
* @param fullContentPath The document ID to resolve.
* @return File object for the given document ID.
* @throws FileNotFoundException If the document ID is invalid or the file does not exist.
*/
private File resolveDocumentId(String fullContentPath) throws FileNotFoundException {
if (!fullContentPath.startsWith(this.packageName)) {
throw new FileNotFoundException(fullContentPath + " not found");
}
String path = fullContentPath.substring(this.packageName.length());
// Resolve the relative path within /data/data/{PKG}
File file;
if (path.equals("/") || path.isEmpty()) {
file = this.dataDirectory;
} else {
// Remove leading slash
String relativePath = path.substring(1);
file = new File(this.dataDirectory, relativePath);
}
if (!file.exists()) {
throw new FileNotFoundException(fullContentPath + " not found");
}
return file;
}
/**
* Add a row containing all file properties to a MatrixCursor for a given document ID.
*
* @param cursor The cursor to add the row to.
* @param documentId The document ID to add the row for.
* @param file The file to add the row for. If null, the file will be resolved from the document ID.
* @throws FileNotFoundException If the file does not exist.
*/
private void addRowForDocument(MatrixCursor cursor, String documentId, File file) throws FileNotFoundException {
if (file == null) {
file = resolveDocumentId(documentId);
}
int flags = 0;
if (file.isDirectory()) {
// Prefer list view for directories
flags = flags | DocumentsContract.Document.FLAG_DIR_PREFERS_LAST_MODIFIED;
}
if (file.canWrite()) {
if (file.isDirectory()) {
flags = flags | DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
}
flags = flags | DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
DocumentsContract.Document.FLAG_SUPPORTS_RENAME |
DocumentsContract.Document.FLAG_SUPPORTS_MOVE;
}
MatrixCursor.RowBuilder row = cursor.newRow();
row.add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, documentId);
row.add(DocumentsContract.Document.COLUMN_DISPLAY_NAME, file.getName());
row.add(DocumentsContract.Document.COLUMN_SIZE, file.length());
row.add(DocumentsContract.Document.COLUMN_MIME_TYPE, resolveMimeType(file));
row.add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, file.lastModified());
row.add(DocumentsContract.Document.COLUMN_FLAGS, flags);
// Custom columns
row.add("full_path", file.getAbsolutePath());
// Add lstat column
String path = file.getPath();
try {
StringBuilder sb = new StringBuilder();
StructStat lstat = Os.lstat(path);
sb.append(lstat.st_mode);
sb.append(";");
sb.append(lstat.st_uid);
sb.append(";");
sb.append(lstat.st_gid);
// Append symlink target if it is a symlink
if ((lstat.st_mode & S_IFLNK) == S_IFLNK) {
sb.append(";");
sb.append(Os.readlink(path));
}
row.add("lstat_info", sb.toString());
} catch (Exception ex) {
Log.e("InternalDocumentsProvider", "Failed to get lstat info for " + path, ex);
}
}
}

View File

@@ -0,0 +1 @@
// Do not remove. Necessary for the extension plugin to be applied to the project.

View File

@@ -0,0 +1 @@
<manifest/>

View File

@@ -0,0 +1,27 @@
package app.revanced.extension.music.spoof;
/**
* @noinspection unused
*/
public class SpoofClientPatch {
private static final int CLIENT_TYPE_ID = 26;
private static final String CLIENT_VERSION = "6.21";
private static final String DEVICE_MODEL = "iPhone16,2";
private static final String OS_VERSION = "17.7.2.21H221";
public static int getClientId() {
return CLIENT_TYPE_ID;
}
public static String getClientVersion() {
return CLIENT_VERSION;
}
public static String getClientModel() {
return DEVICE_MODEL;
}
public static String getOsVersion() {
return OS_VERSION;
}
}

View File

@@ -24,7 +24,9 @@ import java.net.URL;
* @noinspection unused
*/
public class GmsCoreSupport {
public static final String ORIGINAL_UNPATCHED_PACKAGE_NAME = "com.google.android.youtube";
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube";
private static final String PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music";
private static final String GMS_CORE_PACKAGE_NAME
= getGmsCoreVendorGroupId() + ".android.gms";
private static final Uri GMS_CORE_PROVIDER
@@ -52,17 +54,20 @@ public class GmsCoreSupport {
private static void showBatteryOptimizationDialog(Activity context,
String dialogMessageRef,
String positiveButtonStringRef,
String positiveButtonTextRef,
DialogInterface.OnClickListener onPositiveClickListener) {
// Do not set cancelable to false, to allow using back button to skip the action,
// just in case the check can never be satisfied.
var dialog = new AlertDialog.Builder(context)
.setIconAttribute(android.R.attr.alertDialogIcon)
.setTitle(str("gms_core_dialog_title"))
.setMessage(str(dialogMessageRef))
.setPositiveButton(str(positiveButtonStringRef), onPositiveClickListener)
.create();
Utils.showDialog(context, dialog);
// Use a delay to allow the activity to finish initializing.
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
Utils.runOnMainThreadDelayed(() -> {
// Do not set cancelable to false, to allow using back button to skip the action,
// just in case the battery change can never be satisfied.
var dialog = new AlertDialog.Builder(context)
.setTitle(str("gms_core_dialog_title"))
.setMessage(str(dialogMessageRef))
.setPositiveButton(str(positiveButtonTextRef), onPositiveClickListener)
.create();
Utils.showDialog(context, dialog);
}, 100);
}
/**
@@ -74,7 +79,8 @@ public class GmsCoreSupport {
// Verify the user has not included GmsCore for a root installation.
// GmsCore Support changes the package name, but with a mounted installation
// all manifest changes are ignored and the original package name is used.
if (context.getPackageName().equals(ORIGINAL_UNPATCHED_PACKAGE_NAME)) {
String packageName = context.getPackageName();
if (packageName.equals(PACKAGE_NAME_YOUTUBE) || packageName.equals(PACKAGE_NAME_YOUTUBE_MUSIC)) {
Logger.printInfo(() -> "App is mounted with root, but GmsCore patch was included");
// Cannot use localize text here, since the app will load
// resources from the unpatched app and all patch strings are missing.
@@ -99,7 +105,18 @@ public class GmsCoreSupport {
return;
}
// Check if GmsCore is running in the background.
// Check if GmsCore is whitelisted from battery optimizations.
if (batteryOptimizationsEnabled(context)) {
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
showBatteryOptimizationDialog(context,
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
"gms_core_dialog_continue_text",
(dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
return;
}
// Check if GmsCore is currently running in the background.
try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
if (client == null) {
Logger.printInfo(() -> "GmsCore is not running in the background");
@@ -108,18 +125,8 @@ public class GmsCoreSupport {
"gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
"gms_core_dialog_open_website_text",
(dialog, id) -> open(DONT_KILL_MY_APP_LINK));
return;
}
}
// Check if GmsCore is whitelisted from battery optimizations.
if (batteryOptimizationsEnabled(context)) {
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
showBatteryOptimizationDialog(context,
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
"gms_core_dialog_continue_text",
(dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
}
} catch (Exception ex) {
Logger.printException(() -> "checkGmsCore failure", ex);
}
@@ -143,12 +150,10 @@ public class GmsCoreSupport {
private static String getGmsCoreDownload() {
final var vendorGroupId = getGmsCoreVendorGroupId();
//noinspection SwitchStatementWithTooFewBranches
switch (vendorGroupId) {
case "app.revanced":
return "https://github.com/revanced/gmscore/releases/latest";
default:
return vendorGroupId + ".android.gms";
}
return switch (vendorGroupId) {
case "app.revanced" -> "https://github.com/revanced/gmscore/releases/latest";
default -> vendorGroupId + ".android.gms";
};
}
// Modified by a patch. Do not touch.

View File

@@ -4,8 +4,10 @@ import android.annotation.SuppressLint;
import android.app.*;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.os.Build;
@@ -47,6 +49,7 @@ public class Utils {
private static Context context;
private static String versionName;
private static String applicationLabel;
private Utils() {
} // utility class
@@ -61,28 +64,30 @@ public class Utils {
return ""; // Value is replaced during patching.
}
private static PackageInfo getPackageInfo() throws PackageManager.NameNotFoundException {
final var packageName = Objects.requireNonNull(getContext()).getPackageName();
PackageManager packageManager = context.getPackageManager();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
return packageManager.getPackageInfo(
packageName,
PackageManager.PackageInfoFlags.of(0)
);
}
return packageManager.getPackageInfo(
packageName,
0
);
}
/**
* @return The version name of the app, such as 19.11.43
*/
public static String getAppVersionName() {
if (versionName == null) {
try {
final var packageName = Objects.requireNonNull(getContext()).getPackageName();
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
packageInfo = packageManager.getPackageInfo(
packageName,
PackageManager.PackageInfoFlags.of(0)
);
} else {
packageInfo = packageManager.getPackageInfo(
packageName,
0
);
}
versionName = packageInfo.versionName;
versionName = getPackageInfo().versionName;
} catch (Exception ex) {
Logger.printException(() -> "Failed to get package info", ex);
versionName = "Unknown";
@@ -92,6 +97,19 @@ public class Utils {
return versionName;
}
public static String getApplicationName() {
if (applicationLabel == null) {
try {
ApplicationInfo applicationInfo = getPackageInfo().applicationInfo;
applicationLabel = (String) applicationInfo.loadLabel(context.getPackageManager());
} catch (Exception ex) {
Logger.printException(() -> "Failed to get application name", ex);
applicationLabel = "Unknown";
}
}
return applicationLabel;
}
/**
* Hide a view by setting its layout height and width to 1dp.
@@ -325,7 +343,7 @@ public class Utils {
public static void restartApp(@NonNull Context context) {
String packageName = context.getPackageName();
Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
Intent intent = Objects.requireNonNull(context.getPackageManager().getLaunchIntentForPackage(packageName));
Intent mainIntent = Intent.makeRestartActivityTask(intent.getComponent());
// Required for API 34 and later
// Ref: https://developer.android.com/about/versions/14/behavior-changes-14#safer-intents
@@ -499,6 +517,12 @@ public class Utils {
);
}
public static boolean isDarkModeEnabled(Context context) {
Configuration config = context.getResources().getConfiguration();
final int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
}
/**
* Automatically logs any exceptions the runnable throws.
*

View File

@@ -1,11 +1,12 @@
package app.revanced.extension.shared.settings;
import app.revanced.extension.shared.spoof.ClientType;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
import app.revanced.extension.shared.spoof.ClientType;
/**
* Settings shared across multiple apps.
@@ -21,8 +22,9 @@ public class BaseSettings {
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, new SpoofiOSAvailability());
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofVideoStreamsPatch.ForceiOSAVCAvailability());
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
}

View File

@@ -7,7 +7,6 @@ import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.StringRef;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
import org.jetbrains.annotations.NotNull;
import org.json.JSONException;
import org.json.JSONObject;
@@ -330,7 +329,7 @@ public abstract class Setting<T> {
return value.equals(defaultValue);
}
@NotNull
@NonNull
@Override
public String toString() {
return key + "=" + get();

View File

@@ -54,9 +54,7 @@ public class ReVancedAboutPreference extends Preference {
}
protected boolean isDarkModeEnabled() {
Configuration config = getContext().getResources().getConfiguration();
final int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
return Utils.isDarkModeEnabled(getContext());
}
/**

View File

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

View File

@@ -10,15 +10,17 @@ public enum ClientType {
// Specific purpose for age restricted, or private videos, because the iOS client is not logged in.
// https://dumps.tadiphone.dev/dumps/oculus/eureka
ANDROID_VR(28,
"ANDROID_VR",
"Quest 3",
"12",
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
"32", // Android 12.1
"1.56.21",
true
),
true,
false),
// Specific for kids videos.
IOS(5,
"IOS",
forceAVC()
? "iPhone12,5" // 11 Pro Max (last device with iOS 13)
: "iPhone16,2", // 15 Pro Max
@@ -37,7 +39,8 @@ public enum ClientType {
// but 17.40 is the last version that supports iOS 13.
? "17.40.5"
: "19.47.7",
false
false,
true
);
private static boolean forceAVC() {
@@ -50,6 +53,8 @@ public enum ClientType {
*/
public final int id;
public final String clientName;
/**
* Device model, equivalent to {@link Build#MODEL} (System property: ro.product.model)
*/
@@ -82,20 +87,28 @@ public enum ClientType {
*/
public final boolean canLogin;
/**
* If a language code should be used.
*/
public final boolean useLanguageCode;
ClientType(int id,
String clientName,
String deviceModel,
String osVersion,
String userAgent,
@Nullable String androidSdkVersion,
String clientVersion,
boolean canLogin
) {
boolean canLogin,
boolean useLanguageCode) {
this.id = id;
this.clientName = clientName;
this.deviceModel = deviceModel;
this.osVersion = osVersion;
this.userAgent = userAgent;
this.androidSdkVersion = androidSdkVersion;
this.clientVersion = clientVersion;
this.canLogin = canLogin;
this.useLanguageCode = useLanguageCode;
}
}

View File

@@ -6,18 +6,20 @@ import androidx.annotation.Nullable;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
import app.revanced.extension.shared.settings.BaseSettings;
@SuppressWarnings("unused")
public class SpoofVideoStreamsPatch {
private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_VIDEO_STREAMS.get();
private static final boolean FIX_HLS_CURRENT_TIME = SPOOF_STREAMING_DATA
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
/**
* Any unreachable ip address. Used to intentionally fail requests.
*/
@@ -88,10 +90,19 @@ public class SpoofVideoStreamsPatch {
try {
Uri uri = Uri.parse(url);
String path = uri.getPath();
// 'heartbeat' has no video id and appears to be only after playback has started.
if (path != null && path.contains("player") && !path.contains("heartbeat")) {
String videoId = Objects.requireNonNull(uri.getQueryParameter("id"));
StreamingDataRequest.fetchRequest(videoId, requestHeaders);
// 'refresh' has no video id and appears to happen when waiting for a livestream to start.
if (path != null && path.contains("player") && !path.contains("heartbeat")
&& !path.contains("refresh")) {
String id = uri.getQueryParameter("id");
if (id == null) {
Logger.printException(() -> "Ignoring request that has no video id." +
" Url: " + url + " headers: " + requestHeaders);
return;
}
StreamingDataRequest.fetchRequest(id, requestHeaders);
}
} catch (Exception ex) {
Logger.printException(() -> "buildRequest failure", ex);
@@ -158,10 +169,24 @@ public class SpoofVideoStreamsPatch {
return postData;
}
public static final class ForceiOSAVCAvailability implements Setting.Availability {
/**
* Injection point.
*
* Fixes iOS livestreams starting from the beginning.
*/
public static boolean fixHLSCurrentTime(boolean original) {
if (FIX_HLS_CURRENT_TIME) {
return false;
}
return original;
}
public static final class SpoofiOSAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return BaseSettings.SPOOF_VIDEO_STREAMS.get() && BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
}
}
}

View File

@@ -7,9 +7,9 @@ import java.io.IOException;
import java.net.HttpURLConnection;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.requests.Requester;
import app.revanced.extension.shared.requests.Route;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.spoof.ClientType;
final class PlayerRoutes {
@@ -19,15 +19,14 @@ final class PlayerRoutes {
"?fields=streamingData" +
"&alt=proto"
).compile();
private static final String YT_API_URL = "https://youtubei.googleapis.com/youtubei/v1/";
/**
* TCP connection and HTTP read timeout
*/
private static final int CONNECTION_TIMEOUT_MILLISECONDS = 10 * 1000; // 10 Seconds.
private static final String LOCALE_LANGUAGE = Utils.getContext().getResources()
.getConfiguration().locale.getLanguage();
private PlayerRoutes() {
}
@@ -38,9 +37,10 @@ final class PlayerRoutes {
JSONObject context = new JSONObject();
JSONObject client = new JSONObject();
// Required to use correct default audio channel with iOS.
client.put("hl", LOCALE_LANGUAGE);
client.put("clientName", clientType.name());
if (clientType.useLanguageCode) {
client.put("hl", BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getIso639_1());
}
client.put("clientName", clientType.clientName);
client.put("clientVersion", clientType.clientVersion);
client.put("deviceModel", clientType.deviceModel);
client.put("osVersion", clientType.osVersion);

View File

@@ -115,8 +115,7 @@ public class StreamingDataRequest {
Objects.requireNonNull(playerHeaders);
final long startTime = System.currentTimeMillis();
String clientTypeName = clientType.name();
Logger.printDebug(() -> "Fetching video streams for: " + videoId + " using client: " + clientType.name());
Logger.printDebug(() -> "Fetching video streams for: " + videoId + " using client: " + clientType);
try {
HttpURLConnection connection = PlayerRoutes.getPlayerResponseConnectionFromRoute(GET_STREAMING_DATA, clientType);
@@ -124,12 +123,16 @@ public class StreamingDataRequest {
connection.setReadTimeout(HTTP_TIMEOUT_MILLISECONDS);
for (String key : REQUEST_HEADER_KEYS) {
if (!clientType.canLogin && key.equals(AUTHORIZATION_HEADER)) {
continue;
}
String value = playerHeaders.get(key);
if (value != null) {
if (key.equals(AUTHORIZATION_HEADER)) {
if (!clientType.canLogin) {
Logger.printDebug(() -> "Not including request header: " + key);
continue;
}
}
Logger.printDebug(() -> "Including request header: " + key);
connection.setRequestProperty(key, value);
}
}
@@ -142,8 +145,10 @@ public class StreamingDataRequest {
final int responseCode = connection.getResponseCode();
if (responseCode == 200) return connection;
handleConnectionError(clientTypeName + " not available with response code: "
+ responseCode + " message: " + connection.getResponseMessage(),
// This situation likely means the patches are outdated.
// Use a toast message that suggests updating.
handleConnectionError("Playback error (App is outdated?) " + clientType + ": "
+ responseCode + " response: " + connection.getResponseMessage(),
null, showErrorToasts);
} catch (SocketTimeoutException ex) {
handleConnectionError("Connection timeout", ex, showErrorToasts);
@@ -172,17 +177,19 @@ public class StreamingDataRequest {
try {
// gzip encoding doesn't response with content length (-1),
// but empty response body does.
if (connection.getContentLength() != 0) {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream())) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) >= 0) {
baos.write(buffer, 0, bytesRead);
}
if (connection.getContentLength() == 0) {
Logger.printDebug(() -> "Received empty response for video: " + videoId);
} else {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
return ByteBuffer.wrap(baos.toByteArray());
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) >= 0) {
baos.write(buffer, 0, bytesRead);
}
return ByteBuffer.wrap(baos.toByteArray());
}
}
} catch (IOException ex) {

View File

@@ -1,9 +1,20 @@
package app.revanced.extension.tiktok;
import static app.revanced.extension.shared.Utils.isDarkModeEnabled;
import android.content.Context;
import android.graphics.Color;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import app.revanced.extension.shared.settings.StringSetting;
public class Utils {
private static final long[] DEFAULT_MIN_MAX_VALUES = {0L, Long.MAX_VALUE};
// Edit: This could be handled using a custom Setting<Long[]> class
// that saves its value to preferences and JSON using the formatted String created here.
public static long[] parseMinMax(StringSetting setting) {
@@ -20,6 +31,29 @@ public class Utils {
}
setting.save("0-" + Long.MAX_VALUE);
return new long[]{0L, Long.MAX_VALUE};
return DEFAULT_MIN_MAX_VALUES;
}
// Colors picked by hand. These should be replaced with the styled resources TikTok uses.
private static final @ColorInt int TEXT_DARK_MODE_TITLE = Color.WHITE;
private static final @ColorInt int TEXT_DARK_MODE_SUMMARY
= Color.argb(255, 170, 170, 170);
private static final @ColorInt int TEXT_LIGHT_MODE_TITLE = Color.BLACK;
private static final @ColorInt int TEXT_LIGHT_MODE_SUMMARY
= Color.argb(255, 80, 80, 80);
public static void setTitleAndSummaryColor(Context context, View view) {
final boolean darkModeEnabled = isDarkModeEnabled(context);
TextView title = view.findViewById(android.R.id.title);
title.setTextColor(darkModeEnabled
? TEXT_DARK_MODE_TITLE
: TEXT_LIGHT_MODE_TITLE);
TextView summary = view.findViewById(android.R.id.summary);
summary.setTextColor(darkModeEnabled
? TEXT_DARK_MODE_SUMMARY
: TEXT_LIGHT_MODE_SUMMARY);
}
}

View File

@@ -1,17 +1,18 @@
package app.revanced.extension.tiktok.feedfilter;
import app.revanced.extension.tiktok.settings.Settings;
import com.ss.android.ugc.aweme.feed.model.Aweme;
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
import static app.revanced.extension.tiktok.Utils.parseMinMax;
import app.revanced.extension.tiktok.Utils;
import app.revanced.extension.tiktok.settings.Settings;
public final class LikeCountFilter implements IFilter {
final long minLike;
final long maxLike;
LikeCountFilter() {
long[] minMax = parseMinMax(Settings.MIN_MAX_LIKES);
long[] minMax = Utils.parseMinMax(Settings.MIN_MAX_LIKES);
minLike = minMax[0];
maxLike = minMax[1];
}

View File

@@ -1,17 +1,17 @@
package app.revanced.extension.tiktok.feedfilter;
import app.revanced.extension.tiktok.Utils;
import app.revanced.extension.tiktok.settings.Settings;
import com.ss.android.ugc.aweme.feed.model.Aweme;
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
import static app.revanced.extension.tiktok.Utils.parseMinMax;
public class ViewCountFilter implements IFilter {
final long minView;
final long maxView;
ViewCountFilter() {
long[] minMax = parseMinMax(Settings.MIN_MAX_VIEWS);
long[] minMax = Utils.parseMinMax(Settings.MIN_MAX_VIEWS);
minView = minMax[0];
maxView = minMax[1];
}

View File

@@ -19,7 +19,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting DOWNLOAD_WATERMARK = new BooleanSetting("down_watermark", TRUE);
public static final BooleanSetting CLEAR_DISPLAY = new BooleanSetting("clear_display", FALSE);
public static final FloatSetting REMEMBERED_SPEED = new FloatSetting("REMEMBERED_SPEED", 1.0f);
public static final BooleanSetting SIM_SPOOF = new BooleanSetting("simspoof", TRUE, true);
public static final BooleanSetting SIM_SPOOF = new BooleanSetting("simspoof", FALSE, true);
public static final StringSetting SIM_SPOOF_ISO = new StringSetting("simspoof_iso", "us");
public static final StringSetting SIMSPOOF_MCCMNC = new StringSetting("simspoof_mccmnc", "310160");
public static final StringSetting SIMSPOOF_OP_NAME = new StringSetting("simspoof_op_name", "T-Mobile");

View File

@@ -16,10 +16,10 @@ import android.widget.RadioButton;
import android.widget.RadioGroup;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.tiktok.Utils;
@SuppressWarnings("deprecation")
public class DownloadPathPreference extends DialogPreference {
private final Context context;
private final String[] entryValues = {"DCIM", "Movies", "Pictures"};
private String mValue;
@@ -29,11 +29,10 @@ public class DownloadPathPreference extends DialogPreference {
public DownloadPathPreference(Context context, String title, StringSetting setting) {
super(context);
this.context = context;
this.setTitle(title);
this.setSummary(Environment.getExternalStorageDirectory().getPath() + "/" + setting.get());
this.setKey(setting.key);
this.setValue(setting.get());
setTitle(title);
setSummary(Environment.getExternalStorageDirectory().getPath() + "/" + setting.get());
setKey(setting.key);
setValue(setting.get());
}
public String getValue() {
@@ -59,6 +58,7 @@ public class DownloadPathPreference extends DialogPreference {
childDownloadPath = getValue().substring(getValue().indexOf("/") + 1);
mediaPathIndex = findIndexOf(currentMedia);
Context context = getContext();
LinearLayout dialogView = new LinearLayout(context);
RadioGroup mediaPath = new RadioGroup(context);
mediaPath.setLayoutParams(new RadioGroup.LayoutParams(-1, -2));
@@ -79,12 +79,10 @@ public class DownloadPathPreference extends DialogPreference {
downloadPath.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
@@ -99,6 +97,13 @@ public class DownloadPathPreference extends DialogPreference {
return dialogView;
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
Utils.setTitleAndSummaryColor(getContext(), view);
}
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
builder.setTitle("Download Path");

View File

@@ -2,16 +2,26 @@ package app.revanced.extension.tiktok.settings.preference;
import android.content.Context;
import android.preference.EditTextPreference;
import android.view.View;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.tiktok.Utils;
@SuppressWarnings("deprecation")
public class InputTextPreference extends EditTextPreference {
public InputTextPreference(Context context, String title, String summary, StringSetting setting) {
super(context);
this.setTitle(title);
this.setSummary(summary);
this.setKey(setting.key);
this.setText(setting.get());
setTitle(title);
setSummary(summary);
setKey(setting.key);
setText(setting.get());
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
Utils.setTitleAndSummaryColor(getContext(), view);
}
}

View File

@@ -1,5 +1,6 @@
package app.revanced.extension.tiktok.settings.preference;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
@@ -14,11 +15,10 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import app.revanced.extension.shared.settings.StringSetting;
import app.revanced.extension.tiktok.Utils;
@SuppressWarnings("deprecation")
public class RangeValuePreference extends DialogPreference {
private final Context context;
private String minValue;
private String maxValue;
@@ -29,7 +29,6 @@ public class RangeValuePreference extends DialogPreference {
public RangeValuePreference(Context context, String title, String summary, StringSetting setting) {
super(context);
this.context = context;
setTitle(title);
setSummary(summary);
setKey(setting.key);
@@ -53,41 +52,52 @@ public class RangeValuePreference extends DialogPreference {
return mValue;
}
@SuppressLint("SetTextI18n")
@Override
protected View onCreateDialogView() {
minValue = getValue().split("-")[0];
maxValue = getValue().split("-")[1];
Context context = getContext();
LinearLayout dialogView = new LinearLayout(context);
dialogView.setOrientation(LinearLayout.VERTICAL);
// Min view
LinearLayout minView = new LinearLayout(context);
minView.setOrientation(LinearLayout.HORIZONTAL);
dialogView.addView(minView);
TextView min = new TextView(context);
min.setText("Min: ");
minView.addView(min);
EditText minEditText = new EditText(context);
minEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
minEditText.setText(minValue);
minView.addView(minEditText);
dialogView.addView(minView);
// Max view
LinearLayout maxView = new LinearLayout(context);
maxView.setOrientation(LinearLayout.HORIZONTAL);
dialogView.addView(maxView);
TextView max = new TextView(context);
max.setText("Max: ");
maxView.addView(max);
EditText maxEditText = new EditText(context);
maxEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
maxEditText.setText(maxValue);
maxView.addView(maxEditText);
dialogView.addView(maxView);
minEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
@@ -98,12 +108,10 @@ public class RangeValuePreference extends DialogPreference {
maxEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
@@ -111,12 +119,21 @@ public class RangeValuePreference extends DialogPreference {
maxValue = editable.toString();
}
});
return dialogView;
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
Utils.setTitleAndSummaryColor(getContext(), view);
}
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> this.onClick(dialog, DialogInterface.BUTTON_POSITIVE));
builder.setPositiveButton(android.R.string.ok, (dialog, which)
-> this.onClick(dialog, DialogInterface.BUTTON_POSITIVE));
builder.setNegativeButton(android.R.string.cancel, null);
}

View File

@@ -18,14 +18,12 @@ import org.jetbrains.annotations.NotNull;
public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
@Override
protected void syncSettingWithPreference(@NonNull @NotNull Preference pref,
@NonNull @NotNull Setting<?> setting,
protected void syncSettingWithPreference(@NonNull Preference pref,
@NonNull Setting<?> setting,
boolean applySettingToPreference) {
if (pref instanceof RangeValuePreference) {
RangeValuePreference rangeValuePref = (RangeValuePreference) pref;
if (pref instanceof RangeValuePreference rangeValuePref) {
Setting.privateSetValueFromString(setting, rangeValuePref.getValue());
} else if (pref instanceof DownloadPathPreference) {
DownloadPathPreference downloadPathPref = (DownloadPathPreference) pref;
} else if (pref instanceof DownloadPathPreference downloadPathPref) {
Setting.privateSetValueFromString(setting, downloadPathPref.getValue());
} else {
super.syncSettingWithPreference(pref, setting, applySettingToPreference);

View File

@@ -1,13 +1,15 @@
package app.revanced.extension.tiktok.settings.preference;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import java.util.Map;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
import app.revanced.extension.tiktok.Utils;
@SuppressWarnings("deprecation")
public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
/**
@@ -23,22 +25,11 @@ public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
"revanced_settings_about_links_header", "Official links"
);
{
//noinspection deprecation
setTitle("About");
}
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ReVancedTikTokAboutPreference(Context context) {
super(context);
setTitle("About");
setSummary("About ReVanced");
}
@Override
@@ -52,4 +43,11 @@ public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
return String.format(format, args);
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
Utils.setTitleAndSummaryColor(getContext(), view);
}
}

View File

@@ -2,16 +2,26 @@ package app.revanced.extension.tiktok.settings.preference;
import android.content.Context;
import android.preference.SwitchPreference;
import android.view.View;
import app.revanced.extension.shared.settings.BooleanSetting;
import app.revanced.extension.tiktok.Utils;
@SuppressWarnings("deprecation")
public class TogglePreference extends SwitchPreference {
public TogglePreference(Context context, String title, String summary, BooleanSetting setting) {
super(context);
this.setTitle(title);
this.setSummary(summary);
this.setKey(setting.key);
this.setChecked(setting.get());
setTitle(title);
setSummary(summary);
setKey(setting.key);
setChecked(setting.get());
}
@Override
protected void onBindView(View view) {
super.onBindView(view);
Utils.setTitleAndSummaryColor(getContext(), view);
}
}

View File

@@ -18,7 +18,7 @@ public final class ChangeStartPagePatch {
/**
* Unmodified type, and same as un-patched.
*/
ORIGINAL("", null),
DEFAULT("", null),
/**
* Browse id.

View File

@@ -0,0 +1,41 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class ForceOriginalAudioPatch {
private static final String DEFAULT_AUDIO_TRACKS_IDENTIFIER = "original";
/**
* Injection point.
*/
public static boolean isDefaultAudioStream(boolean isDefault, String audioTrackId, String audioTrackDisplayName) {
try {
if (!Settings.FORCE_ORIGINAL_AUDIO.get()) {
return isDefault;
}
if (audioTrackDisplayName.isEmpty()) {
// Older app targets can have empty audio tracks and these might be placeholders.
// The real audio tracks are called after these.
return isDefault;
}
Logger.printDebug(() -> "default: " + String.format("%-5s", isDefault) + " id: "
+ String.format("%-8s", audioTrackId) + " name:" + audioTrackDisplayName);
final boolean isOriginal = audioTrackDisplayName.contains(DEFAULT_AUDIO_TRACKS_IDENTIFIER);
if (isOriginal) {
Logger.printDebug(() -> "Using audio: " + audioTrackId);
}
return isOriginal;
} catch (Exception ex) {
Logger.printException(() -> "isDefaultAudioStream failure", ex);
}
return isDefault;
}
}

View File

@@ -30,7 +30,7 @@ public final class MiniplayerPatch {
*/
DISABLED(false, null),
/** Unmodified type, and same as un-patched. */
ORIGINAL(null, null),
DEFAULT(null, null),
/**
* Exactly the same as MINIMAL and only here for migration of user settings.
* Eventually this should be deleted.
@@ -82,7 +82,13 @@ public final class MiniplayerPatch {
final int WIDTH_DIP_MIN = 170; // Seems to be the smallest that works.
final int HORIZONTAL_PADDING_DIP = 15; // Estimated padding.
// Round down to the nearest 5 pixels, to keep any error toasts easier to read.
final int WIDTH_DIP_MAX = 5 * ((deviceDipWidth - HORIZONTAL_PADDING_DIP) / 5);
final int estimatedWidthDipMax = 5 * ((deviceDipWidth - HORIZONTAL_PADDING_DIP) / 5);
// On some ultra low end devices the pixel width and density are the same number,
// which causes the estimate to always give a value of 1.
// Fix this by using a fixed size of double the min width.
final int WIDTH_DIP_MAX = estimatedWidthDipMax <= WIDTH_DIP_MIN
? 2 * WIDTH_DIP_MIN
: estimatedWidthDipMax;
Logger.printDebug(() -> "Screen dip width: " + deviceDipWidth + " maxWidth: " + WIDTH_DIP_MAX);
int dipWidth = Settings.MINIPLAYER_WIDTH_DIP.get();
@@ -127,8 +133,10 @@ public final class MiniplayerPatch {
private static final boolean HIDE_SUBTEXT_ENABLED =
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get();
private static final boolean HIDE_REWIND_FORWARD_ENABLED =
CURRENT_TYPE == MODERN_1 && Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get();
// 19.25 is last version that has forward/back buttons for phones,
// but buttons still show for tablets/foldable devices and they don't work well so always hide.
private static final boolean HIDE_REWIND_FORWARD_ENABLED = CURRENT_TYPE == MODERN_1
&& (VersionCheckPatch.IS_19_34_OR_GREATER || Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get());
private static final boolean MINIPLAYER_ROUNDED_CORNERS_ENABLED =
Settings.MINIPLAYER_ROUNDED_CORNERS.get();
@@ -145,6 +153,18 @@ public final class MiniplayerPatch {
private static final int OPACITY_LEVEL;
static {
int opacity = Settings.MINIPLAYER_OPACITY.get();
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
Settings.MINIPLAYER_OPACITY.resetToDefault();
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
}
OPACITY_LEVEL = (opacity * 255) / 100;
}
public static final class MiniplayerHorizontalDragAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
@@ -163,18 +183,6 @@ public final class MiniplayerPatch {
}
}
static {
int opacity = Settings.MINIPLAYER_OPACITY.get();
if (opacity < 0 || opacity > 100) {
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
Settings.MINIPLAYER_OPACITY.resetToDefault();
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
}
OPACITY_LEVEL = (opacity * 255) / 100;
}
/**
* Injection point.
*
@@ -182,7 +190,7 @@ public final class MiniplayerPatch {
* effectively disabling the miniplayer.
*/
public static boolean getMiniplayerOnCloseHandler(boolean original) {
return CURRENT_TYPE == ORIGINAL
return CURRENT_TYPE == DEFAULT
? original
: CURRENT_TYPE == DISABLED;
}
@@ -201,7 +209,7 @@ public final class MiniplayerPatch {
* Injection point.
*/
public static boolean getModernMiniplayerOverride(boolean original) {
return CURRENT_TYPE == ORIGINAL
return CURRENT_TYPE == DEFAULT
? original
: CURRENT_TYPE.isModern();
}
@@ -229,7 +237,7 @@ public final class MiniplayerPatch {
* Injection point.
*/
public static boolean getModernFeatureFlagsActiveOverride(boolean original) {
if (CURRENT_TYPE == ORIGINAL) {
if (CURRENT_TYPE == DEFAULT) {
return original;
}
@@ -240,7 +248,7 @@ public final class MiniplayerPatch {
* Injection point.
*/
public static boolean enableMiniplayerDoubleTapAction(boolean original) {
if (CURRENT_TYPE == ORIGINAL) {
if (CURRENT_TYPE == DEFAULT) {
return original;
}
@@ -251,7 +259,7 @@ public final class MiniplayerPatch {
* Injection point.
*/
public static boolean enableMiniplayerDragAndDrop(boolean original) {
if (CURRENT_TYPE == ORIGINAL) {
if (CURRENT_TYPE == DEFAULT) {
return original;
}

View File

@@ -3,12 +3,15 @@ package app.revanced.extension.youtube.patches;
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.os.Build;
import android.view.View;
import java.util.EnumMap;
import java.util.Map;
import android.widget.TextView;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
@@ -26,6 +29,15 @@ public final class NavigationButtonsPatch {
private static final boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON
= Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get();
private static final Boolean DISABLE_TRANSLUCENT_STATUS_BAR
= Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get();
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT.get();
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK.get();
/**
* Injection point.
*/
@@ -48,4 +60,42 @@ public final class NavigationButtonsPatch {
public static void hideNavigationButtonLabels(TextView navigationLabelsView) {
hideViewUnderCondition(Settings.HIDE_NAVIGATION_BUTTON_LABELS, navigationLabelsView);
}
/**
* Injection point.
*/
public static boolean useTranslucentNavigationStatusBar(boolean original) {
// Must check Android version, as forcing this on Android 11 or lower causes app hang and crash.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
return original;
}
if (DISABLE_TRANSLUCENT_STATUS_BAR) {
return false;
}
return original;
}
/**
* Injection point.
*/
public static boolean useTranslucentNavigationButtons(boolean original) {
// Feature requires Android 13+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return original;
}
if (!DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
return original;
}
if (DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
return false;
}
return Utils.isDarkModeEnabled(Utils.getContext())
? !DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
: !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT;
}
}

View File

@@ -0,0 +1,94 @@
package app.revanced.extension.youtube.patches;
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import java.lang.ref.WeakReference;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class OpenShortsInRegularPlayerPatch {
public enum ShortsPlayerType {
SHORTS_PLAYER,
REGULAR_PLAYER,
REGULAR_PLAYER_FULLSCREEN
}
static {
if (!VersionCheckPatch.IS_19_46_OR_GREATER
&& Settings.SHORTS_PLAYER_TYPE.get() == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN) {
// User imported newer settings to an older app target.
Logger.printInfo(() -> "Resetting " + Settings.SHORTS_PLAYER_TYPE);
Settings.SHORTS_PLAYER_TYPE.resetToDefault();
}
}
private static WeakReference<Activity> mainActivityRef = new WeakReference<>(null);
/**
* Injection point.
*/
public static void setMainActivity(Activity activity) {
mainActivityRef = new WeakReference<>(activity);
}
/**
* Injection point.
*/
public static boolean openShort(String videoID) {
try {
ShortsPlayerType type = Settings.SHORTS_PLAYER_TYPE.get();
if (type == ShortsPlayerType.SHORTS_PLAYER) {
return false; // Default unpatched behavior.
}
if (videoID.isEmpty()) {
// Shorts was opened using launcher app shortcut.
//
// This check will not detect if the Shorts app shortcut is used
// while the app is running in the background (instead the regular player is opened).
// To detect that the hooked method map parameter can be checked
// if integer key 'com.google.android.apps.youtube.app.endpoint.flags'
// has bitmask 16 set.
//
// This use case seems unlikely if the user has the Shorts
// set to open in the regular player, so it's ignored as
// checking the map makes the patch more complicated.
Logger.printDebug(() -> "Ignoring Short with no videoId");
return false;
}
if (NavigationButton.getSelectedNavigationButton() == NavigationButton.SHORTS) {
return false; // Always use Shorts player for the Shorts nav button.
}
final boolean forceFullScreen = (type == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN);
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(forceFullScreen);
// Can use the application context and add intent flags of
// FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TOP
// But the activity context seems to fix random app crashes
// if Shorts urls are opened outside the app.
var context = mainActivityRef.get();
Intent videoPlayerIntent = new Intent(
Intent.ACTION_VIEW,
Uri.parse("https://youtube.com/watch?v=" + videoID)
);
videoPlayerIntent.setPackage(context.getPackageName());
context.startActivity(videoPlayerIntent);
return true;
} catch (Exception ex) {
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(null);
Logger.printException(() -> "openShort failure", ex);
return false;
}
}
}

View File

@@ -0,0 +1,41 @@
package app.revanced.extension.youtube.patches;
import androidx.annotation.Nullable;
import app.revanced.extension.youtube.settings.Settings;
@SuppressWarnings("unused")
public class OpenVideosFullscreenHookPatch {
@Nullable
private static volatile Boolean openNextVideoFullscreen;
public static void setOpenNextVideoFullscreen(@Nullable Boolean forceFullScreen) {
openNextVideoFullscreen = forceFullScreen;
}
/**
* Changed during patching since this class is also
* used by {@link OpenVideosFullscreenHookPatch}.
*/
private static boolean isFullScreenPatchIncluded() {
return false;
}
/**
* Injection point.
*/
public static boolean openVideoFullscreenPortrait(boolean original) {
Boolean openFullscreen = openNextVideoFullscreen;
if (openFullscreen != null) {
openNextVideoFullscreen = null;
return openFullscreen;
}
if (!isFullScreenPatchIncluded()) {
return false;
}
return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get();
}
}

View File

@@ -9,4 +9,5 @@ public class VersionCheckPatch {
public static final boolean IS_19_26_OR_GREATER = Utils.getAppVersionName().compareTo("19.26.00") >= 0;
public static final boolean IS_19_29_OR_GREATER = Utils.getAppVersionName().compareTo("19.29.00") >= 0;
public static final boolean IS_19_34_OR_GREATER = Utils.getAppVersionName().compareTo("19.34.00") >= 0;
public static final boolean IS_19_46_OR_GREATER = Utils.getAppVersionName().compareTo("19.46.00") >= 0;
}

View File

@@ -85,7 +85,8 @@ public final class AdsFilter extends Filter {
"composite_concurrent_carousel_layout",
"carousel_headered_layout",
"full_width_portrait_image_layout",
"brand_video_shelf"
"brand_video_shelf",
"brand_video_singleton"
);
final var movieAds = new StringFilterGroup(

View File

@@ -14,6 +14,11 @@ final class CommentsFilter extends Filter {
private final ByteArrayFilterGroup emojiPickerBufferGroup;
public CommentsFilter() {
var chatSummary = new StringFilterGroup(
Settings.HIDE_COMMENTS_CHAT_SUMMARY,
"live_chat_summary_banner.eml"
);
var commentsByMembers = new StringFilterGroup(
Settings.HIDE_COMMENTS_BY_MEMBERS_HEADER,
"sponsorships_comments_header.eml",
@@ -54,6 +59,7 @@ final class CommentsFilter extends Filter {
);
addPathCallbacks(
chatSummary,
commentsByMembers,
comments,
createAShort,

View File

@@ -36,7 +36,6 @@ public final class LayoutComponentsFilter extends Filter {
);
private final StringTrieSearch exceptions = new StringTrieSearch();
private final StringFilterGroup searchResultShelfHeader;
private final StringFilterGroup inFeedSurvey;
private final StringFilterGroup notifyMe;
private final StringFilterGroup expandableMetadata;
@@ -74,13 +73,14 @@ public final class LayoutComponentsFilter extends Filter {
final var communityPosts = new StringFilterGroup(
Settings.HIDE_COMMUNITY_POSTS,
"post_base_wrapper",
"post_base_wrapper", // may be obsolete and no longer needed.
"text_post_root.eml",
"images_post_root.eml",
"images_post_slim.eml",
"images_post_slim.eml", // may be obsolete and no longer needed.
"images_post_root_slim.eml",
"text_post_root_slim.eml",
"post_base_wrapper_slim.eml"
"post_base_wrapper_slim.eml",
"poll_post_root.eml"
);
final var communityGuidelines = new StringFilterGroup(
@@ -194,11 +194,6 @@ public final class LayoutComponentsFilter extends Filter {
"timed_reaction"
);
searchResultShelfHeader = new StringFilterGroup(
Settings.HIDE_SEARCH_RESULT_SHELF_HEADER,
"shelf_header.eml"
);
notifyMe = new StringFilterGroup(
Settings.HIDE_NOTIFY_ME_BUTTON,
"set_reminder_button"
@@ -324,9 +319,6 @@ public final class LayoutComponentsFilter extends Filter {
return false;
}
// TODO: This also hides the feed Shorts shelf header
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
if (matchedGroup == horizontalShelves) {
if (contentIndex == 0 && hideShelves()) {
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);

View File

@@ -55,7 +55,7 @@ public class ThemePatch {
/**
* Injection point.
*/
public static boolean gradientLoadingScreenEnabled() {
public static boolean gradientLoadingScreenEnabled(boolean original) {
return GRADIENT_LOADING_SCREEN_ENABLED;
}
}

View File

@@ -16,7 +16,7 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_2;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_3;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.PHONE;
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_19_17_OR_GREATER;
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
@@ -52,6 +52,8 @@ public class Settings extends BaseSettings {
public static final FloatSetting PLAYBACK_SPEED_DEFAULT = new FloatSetting("revanced_playback_speed_default", -2.0f);
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
// Audio
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE);
// Ads
public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE);
@@ -91,7 +93,6 @@ 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_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
public static final BooleanSetting HIDE_SEARCH_RESULT_SHELF_HEADER = new BooleanSetting("revanced_hide_search_result_shelf_header", FALSE);
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
// Alternative thumbnails
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
@@ -140,15 +141,16 @@ public class Settings extends BaseSettings {
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("revanced_playback_speed_dialog_button", FALSE);
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("revanced_hide_player_popup_panels", FALSE);
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("revanced_player_overlay_opacity", 100, true);
public static final BooleanSetting OPEN_VIDEOS_FULLSCREEN_PORTRAIT = new BooleanSetting("revanced_open_videos_fullscreen_portrait", FALSE);
// Miniplayer
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.ORIGINAL, true);
public static final EnumSetting<MiniplayerType> MINIPLAYER_TYPE = new EnumSetting<>("revanced_miniplayer_type", MiniplayerType.DEFAULT, true);
private static final Availability MINIPLAYER_ANY_MODERN = MINIPLAYER_TYPE.availability(MODERN_1, MODERN_2, MODERN_3, MODERN_4);
public static final BooleanSetting MINIPLAYER_DOUBLE_TAP_ACTION = new BooleanSetting("revanced_miniplayer_double_tap_action", TRUE, true, MINIPLAYER_ANY_MODERN);
public static final BooleanSetting MINIPLAYER_DRAG_AND_DROP = new BooleanSetting("revanced_miniplayer_drag_and_drop", TRUE, true, MINIPLAYER_ANY_MODERN);
public static final BooleanSetting MINIPLAYER_HORIZONTAL_DRAG = new BooleanSetting("revanced_miniplayer_horizontal_drag", FALSE, true, new MiniplayerHorizontalDragAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, new MiniplayerHideExpandCloseAvailability());
public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3));
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1));
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", TRUE, true, MINIPLAYER_TYPE.availability(MODERN_1));
public static final BooleanSetting MINIPLAYER_ROUNDED_CORNERS = new BooleanSetting("revanced_miniplayer_rounded_corners", TRUE, true, MINIPLAYER_ANY_MODERN);
public static final IntegerSetting MINIPLAYER_WIDTH_DIP = new IntegerSetting("revanced_miniplayer_width_dip", 192, true, MINIPLAYER_ANY_MODERN);
public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, MINIPLAYER_TYPE.availability(MODERN_1));
@@ -158,6 +160,7 @@ public class Settings extends BaseSettings {
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));
// Comments
public static final BooleanSetting HIDE_COMMENTS_CHAT_SUMMARY = new BooleanSetting("revanced_hide_comments_chat_summary", FALSE);
public static final BooleanSetting HIDE_COMMENTS_BY_MEMBERS_HEADER = new BooleanSetting("revanced_hide_comments_by_members_header", FALSE);
public static final BooleanSetting HIDE_COMMENTS_CREATE_A_SHORT_BUTTON = new BooleanSetting("revanced_hide_comments_create_a_short_button", TRUE);
public static final BooleanSetting HIDE_COMMENTS_PREVIEW_COMMENT = new BooleanSetting("revanced_hide_comments_preview_comment", FALSE);
@@ -203,7 +206,7 @@ public class Settings extends BaseSettings {
public static final BooleanSetting SPOOF_APP_VERSION = new BooleanSetting("revanced_spoof_app_version", FALSE, true, "revanced_spoof_app_version_user_dialog_message");
public static final BooleanSetting TABLET_LAYOUT = new BooleanSetting("revanced_tablet_layout", FALSE, true, "revanced_tablet_layout_user_dialog_message");
public static final BooleanSetting WIDE_SEARCHBAR = new BooleanSetting("revanced_wide_searchbar", FALSE, true);
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.ORIGINAL, true);
public static final EnumSetting<StartPage> CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true);
public static final StringSetting SPOOF_APP_VERSION_TARGET = new StringSetting("revanced_spoof_app_version_target", IS_19_17_OR_GREATER ? "19.35.36" : "17.33.42", true, parent(SPOOF_APP_VERSION));
// Custom filter
public static final BooleanSetting CUSTOM_FILTER = new BooleanSetting("revanced_custom_filter", FALSE);
@@ -215,10 +218,14 @@ public class Settings extends BaseSettings {
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true);
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true);
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = new BooleanSetting("revanced_disable_translucent_navigation_bar_light", FALSE, true);
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = new BooleanSetting("revanced_disable_translucent_navigation_bar_dark", FALSE, true);
// Shorts
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_shorts_disable_background_playback", FALSE);
public static final EnumSetting<ShortsPlayerType> SHORTS_PLAYER_TYPE = new EnumSetting<>("revanced_shorts_player_type", ShortsPlayerType.SHORTS_PLAYER);
public static final BooleanSetting HIDE_SHORTS_CHANNEL_BAR = new BooleanSetting("revanced_hide_shorts_channel_bar", FALSE);
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);
@@ -386,7 +393,8 @@ public class Settings extends BaseSettings {
}
// Migrate renamed enum.
if (MINIPLAYER_TYPE.get() == PHONE) {
//noinspection deprecation
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {
MINIPLAYER_TYPE.save(MINIMAL);
}

View File

@@ -10,6 +10,7 @@ import android.os.Build;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.util.Pair;
import android.util.TypedValue;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -18,6 +19,10 @@ import android.widget.Toolbar;
import androidx.annotation.RequiresApi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment;
@@ -41,6 +46,55 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
return Utils.getContext().getResources().getDrawable(backButtonResource);
}
/**
* Sorts a preference list by menu entries, but preserves the first value as the first entry.
*
* @noinspection SameParameterValue
*/
private static void sortListPreferenceByValues(ListPreference listPreference, int firstEntriesToPreserve) {
CharSequence[] entries = listPreference.getEntries();
CharSequence[] entryValues = listPreference.getEntryValues();
final int entrySize = entries.length;
if (entrySize != entryValues.length) {
// Xml array declaration has a missing/extra entry.
throw new IllegalStateException();
}
List<Pair<String, String>> firstPairs = new ArrayList<>(firstEntriesToPreserve);
List<Pair<String, String>> pairsToSort = new ArrayList<>(entrySize);
for (int i = 0; i < entrySize; i++) {
Pair<String, String> pair = new Pair<>(entries[i].toString(), entryValues[i].toString());
if (i < firstEntriesToPreserve) {
firstPairs.add(pair);
} else {
pairsToSort.add(pair);
}
}
Collections.sort(pairsToSort, (pair1, pair2) -> pair1.first.compareToIgnoreCase(pair2.first));
CharSequence[] sortedEntries = new CharSequence[entrySize];
CharSequence[] sortedEntryValues = new CharSequence[entrySize];
int i = 0;
for (Pair<String, String> pair : firstPairs) {
sortedEntries[i] = pair.first;
sortedEntryValues[i] = pair.second;
i++;
}
for (Pair<String, String> pair : pairsToSort) {
sortedEntries[i] = pair.first;
sortedEntryValues[i] = pair.second;
i++;
}
listPreference.setEntries(sortedEntries);
listPreference.setEntryValues(sortedEntryValues);
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
protected void initialize() {
@@ -50,9 +104,14 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
setPreferenceScreenToolbar(getPreferenceScreen());
// If the preference was included, then initialize it based on the available playback speed.
Preference defaultSpeedPreference = findPreference(Settings.PLAYBACK_SPEED_DEFAULT.key);
if (defaultSpeedPreference instanceof ListPreference) {
CustomPlaybackSpeedPatch.initializeListPreference((ListPreference) defaultSpeedPreference);
Preference preference = findPreference(Settings.PLAYBACK_SPEED_DEFAULT.key);
if (preference instanceof ListPreference playbackPreference) {
CustomPlaybackSpeedPatch.initializeListPreference(playbackPreference);
}
preference = findPreference(Settings.SPOOF_VIDEO_STREAMS_LANGUAGE.key);
if (preference instanceof ListPreference languagePreference) {
sortListPreferenceByValues(languagePreference, 1);
}
} catch (Exception ex) {
Logger.printException(() -> "initialize failure", ex);

View File

@@ -507,7 +507,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
Utils.showToastLong(str("revanced_sb_stats_username_changed"));
} else {
preference.setText(userName); // revert to previous
Utils.showToastLong(errorMessage);
SponsorBlockUtils.showErrorDialog(errorMessage);
}
});
});

View File

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

View File

@@ -363,6 +363,16 @@ public class SponsorBlockUtils {
}
}
public static void showErrorDialog(String dialogMessage) {
Utils.runOnMainThreadNowOrLater(() ->
new AlertDialog.Builder(SponsorBlockViewController.getOverLaysViewGroupContext())
.setMessage(dialogMessage)
.setPositiveButton(android.R.string.ok, null)
.setCancelable(false)
.show()
);
}
public static void onEditByHandClicked() {
try {
Utils.verifyOnMainThread();

View File

@@ -19,6 +19,7 @@ import java.util.concurrent.TimeUnit;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
import app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment.SegmentVote;
@@ -142,6 +143,7 @@ public class SBRequester {
public static void submitSegments(@NonNull String videoId, @NonNull String category,
long startTime, long endTime, long videoLength) {
Utils.verifyOffMainThread();
try {
String privateUserId = SponsorBlockSettings.getSBPrivateUserID();
String start = String.format(Locale.US, TIME_TEMPLATE, startTime / 1000f);
@@ -151,35 +153,29 @@ public class SBRequester {
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, privateUserId, videoId, category, start, end, duration);
final int responseCode = connection.getResponseCode();
final String messageToToast;
switch (responseCode) {
case HTTP_STATUS_CODE_SUCCESS:
messageToToast = str("revanced_sb_submit_succeeded");
break;
case 409:
messageToToast = str("revanced_sb_submit_failed_duplicate");
break;
case 403:
messageToToast = str("revanced_sb_submit_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection));
break;
case 429:
messageToToast = str("revanced_sb_submit_failed_rate_limit");
break;
case 400:
messageToToast = str("revanced_sb_submit_failed_invalid", Requester.parseErrorStringAndDisconnect(connection));
break;
default:
messageToToast = str("revanced_sb_submit_failed_unknown_error", responseCode, connection.getResponseMessage());
break;
}
Utils.showToastLong(messageToToast);
String userMessage = switch (responseCode) {
case HTTP_STATUS_CODE_SUCCESS -> str("revanced_sb_submit_succeeded");
case 409 -> str("revanced_sb_submit_failed_duplicate");
case 403 -> str("revanced_sb_submit_failed_forbidden",
Requester.parseErrorStringAndDisconnect(connection));
case 429 -> str("revanced_sb_submit_failed_rate_limit");
case 400 -> str("revanced_sb_submit_failed_invalid",
Requester.parseErrorStringAndDisconnect(connection));
default -> str("revanced_sb_submit_failed_unknown_error",
responseCode, connection.getResponseMessage());
};
// Message might be about the users account or an error too large to show in a toast.
// Use a dialog instead.
SponsorBlockUtils.showErrorDialog(userMessage);
} catch (SocketTimeoutException ex) {
// Always show, even if show connection toasts is turned off
Logger.printDebug(() -> "Timeout", ex);
Utils.showToastLong(str("revanced_sb_submit_failed_timeout"));
} catch (IOException ex) {
Logger.printDebug(() -> "IOException", ex);
Utils.showToastLong(str("revanced_sb_submit_failed_unknown_error", 0, ex.getMessage()));
} catch (Exception ex) {
Logger.printException(() -> "failed to submit segments", ex);
Logger.printException(() -> "failed to submit segments", ex); // Should never happen.
}
}
@@ -218,19 +214,22 @@ public class SBRequester {
: getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_QUALITY, uuid, segmentUuid, String.valueOf(voteOption.apiVoteType));
final int responseCode = connection.getResponseCode();
String userMessage;
switch (responseCode) {
case HTTP_STATUS_CODE_SUCCESS:
Logger.printDebug(() -> "Vote success for segment: " + segment);
break;
return;
case 403:
Utils.showToastLong(
str("revanced_sb_vote_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection)));
userMessage = str("revanced_sb_vote_failed_forbidden",
Requester.parseErrorStringAndDisconnect(connection));
break;
default:
Utils.showToastLong(
str("revanced_sb_vote_failed_unknown_error", responseCode, connection.getResponseMessage()));
userMessage = str("revanced_sb_vote_failed_unknown_error",
responseCode, connection.getResponseMessage());
break;
}
SponsorBlockUtils.showErrorDialog(userMessage);
} catch (SocketTimeoutException ex) {
Utils.showToastShort(str("revanced_sb_vote_failed_timeout"));
} catch (IOException ex) {

View File

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

View File

@@ -60,6 +60,10 @@ public final class app/revanced/patches/all/misc/directory/ChangeDataDirectoryLo
public static final fun getChangeDataDirectoryLocationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/all/misc/directory/documentsprovider/ExportInternalDataDocumentsProviderPatchKt {
public static final fun getExportInternalDataDocumentsProviderPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
public final class app/revanced/patches/all/misc/hex/HexPatchKt {
public static final fun getHexPatch ()Lapp/revanced/patcher/patch/RawResourcePatch;
}
@@ -320,8 +324,12 @@ public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt {
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatchKt {
public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public final class app/revanced/patches/music/misc/spoof/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/music/misc/spoof/UserAgentClientSpoofPatchKt {
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
@@ -762,6 +770,10 @@ public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
public static final fun userAgentClientSpoofPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt {
public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1164,6 +1176,14 @@ public final class app/revanced/patches/youtube/layout/player/background/PlayerC
public static final fun getPlayerControlsBackgroundPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
}
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenKt {
public static final fun getOpenVideosFullscreen ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatchKt {
public static final fun getOpenVideosFullscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/player/overlay/CustomPlayerOverlayOpacityPatchKt {
public static final fun getCustomPlayerOverlayOpacityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1194,6 +1214,10 @@ public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAuto
public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatchKt {
public static final fun getOpenShortsInRegularPlayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt {
public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1384,6 +1408,10 @@ public final class app/revanced/patches/youtube/shared/FingerprintsKt {
public static final fun getRollingNumberTextViewAnimationUpdateFingerprint ()Lapp/revanced/patcher/Fingerprint;
}
public final class app/revanced/patches/youtube/video/audio/ForceOriginalAudioPatchKt {
public static final fun getForceOriginalAudioPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/youtube/video/information/VideoInformationPatchKt {
public static final fun getVideoInformationPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public static final fun userSelectedPlaybackSpeedHook (Ljava/lang/String;Ljava/lang/String;)V

View File

@@ -1,58 +1,19 @@
package app.revanced.patches.all.misc.directory
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
import com.android.tools.smali.dexlib2.util.MethodUtil
import app.revanced.patches.all.misc.directory.documentsprovider.exportInternalDataDocumentsProviderPatch
@Suppress("unused")
@Deprecated(
"Superseded by internalDataDocumentsProviderPatch",
ReplaceWith("internalDataDocumentsProviderPatch"),
)
val changeDataDirectoryLocationPatch = bytecodePatch(
name = "Change data directory location",
// name = "Change data directory location",
description = "Changes the data directory in the application from " +
"the app internal storage directory to /sdcard/android/data accessible by root-less devices." +
"Using this patch can cause unexpected issues with some apps.",
use = false,
) {
dependsOn(
transformInstructionsPatch(
filterMap = filter@{ _, _, instruction, instructionIndex ->
val reference = instruction.getReference<MethodReference>() ?: return@filter null
if (!MethodUtil.methodSignaturesMatch(reference, MethodCall.GetDir.reference)) {
return@filter null
}
return@filter instructionIndex
},
transform = { method, index ->
val getDirInstruction = method.getInstruction<Instruction35c>(index)
val contextRegister = getDirInstruction.registerC
val dataRegister = getDirInstruction.registerD
method.replaceInstruction(
index,
"invoke-virtual { v$contextRegister, v$dataRegister }, " +
"Landroid/content/Context;->getExternalFilesDir(Ljava/lang/String;)Ljava/io/File;",
)
},
),
)
}
private enum class MethodCall(
val reference: MethodReference,
) {
GetDir(
ImmutableMethodReference(
"Landroid/content/Context;",
"getDir",
listOf("Ljava/lang/String;", "I"),
"Ljava/io/File;",
),
),
dependsOn(exportInternalDataDocumentsProviderPatch)
}

View File

@@ -0,0 +1,58 @@
package app.revanced.patches.all.misc.directory.documentsprovider
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.asSequence
import app.revanced.util.getNode
@Suppress("unused")
val exportInternalDataDocumentsProviderPatch = resourcePatch(
name = "Export internal data documents provider",
description = "Exports a documents provider that grants access to the internal data directory of this app " +
"to file managers and other apps that support the Storage Access Framework.",
use = false,
) {
dependsOn(
bytecodePatch {
extendWith("extensions/all/misc/directory/export-internal-data-documents-provider.rve")
},
)
execute {
val documentsProviderClass =
"app.revanced.extension.all.misc.directory.documentsprovider.InternalDataDocumentsProvider"
document("AndroidManifest.xml").use { document ->
// Check if the provider is already declared
if (document.getElementsByTagName("provider")
.asSequence()
.any { it.attributes.getNamedItem("android:name")?.nodeValue == documentsProviderClass }
) {
return@execute
}
val authority =
document.getNode("manifest").attributes.getNamedItem("package").let {
// Select a URI authority name that is unique to the current app
"${it.nodeValue}.$documentsProviderClass"
}
// Register the documents provider
with(document.getNode("application")) {
document.createElement("provider").apply {
setAttribute("android:name", documentsProviderClass)
setAttribute("android:authorities", authority)
setAttribute("android:exported", "true")
setAttribute("android:grantUriPermissions", "true")
setAttribute("android:permission", "android.permission.MANAGE_DOCUMENTS")
document.createElement("intent-filter").apply {
document.createElement("action").apply {
setAttribute("android:name", "android.content.action.DOCUMENTS_PROVIDER")
}.let(this::appendChild)
}.let(this::appendChild)
}.let(this::appendChild)
}
}
}
}

View File

@@ -1,9 +1,10 @@
package app.revanced.patches.all.misc.packagename
import app.revanced.patcher.patch.Option
import app.revanced.patcher.patch.resourcePatch
import app.revanced.patcher.patch.stringOption
import app.revanced.patcher.patch.*
import app.revanced.util.asSequence
import app.revanced.util.getNode
import org.w3c.dom.Element
import java.util.logging.Logger
lateinit var packageNameOption: Option<String>
@@ -27,7 +28,8 @@ fun setOrGetFallbackPackageName(fallbackPackageName: String): String {
val changePackageNamePatch = resourcePatch(
name = "Change package name",
description = "Appends \".revanced\" to the package name by default. Changing the package name of the app can lead to unexpected issues.",
description = "Appends \".revanced\" to the package name by default. " +
"Changing the package name of the app can lead to unexpected issues.",
use = false,
) {
packageNameOption = stringOption(
@@ -41,20 +43,81 @@ val changePackageNamePatch = resourcePatch(
it == "Default" || it!!.matches(Regex("^[a-z]\\w*(\\.[a-z]\\w*)+\$"))
}
val updatePermissions by booleanOption(
key = "updatePermissions",
default = false,
title = "Update permissions",
description = "Update compatibility receiver permissions. " +
"Enabling this can fix installation errors, but this can also break features in certain apps.",
)
val updateProviders by booleanOption(
key = "updateProviders",
default = false,
title = "Update providers",
description = "Update provider names declared by the app. " +
"Enabling this can fix installation errors, but this can also break features in certain apps.",
)
finalize {
/**
* Apps that are confirmed to not work correctly with this patch.
* This is not an exhaustive list, and is only the apps with
* ReVanced specific patches and are confirmed incompatible with this patch.
*/
val incompatibleAppPackages = setOf(
// Cannot log in, settings menu is broken.
"com.reddit.frontpage",
// Patches and installs but crashes on launch.
"com.duolingo",
"com.twitter.android",
"tv.twitch.android.app",
)
document("AndroidManifest.xml").use { document ->
val manifest = document.getNode("manifest") as Element
val packageName = manifest.getAttribute("package")
if (incompatibleAppPackages.contains(packageName)) {
return@finalize Logger.getLogger(this::class.java.name).severe(
"'$packageName' does not work correctly with \"Change package name\"",
)
}
val replacementPackageName = packageNameOption.value
val newPackageName = if (replacementPackageName != packageNameOption.default) {
replacementPackageName!!
} else {
"$packageName.revanced"
}
val manifest = document.getElementsByTagName("manifest").item(0) as Element
manifest.setAttribute(
"package",
if (replacementPackageName != packageNameOption.default) {
replacementPackageName
} else {
"${manifest.getAttribute("package")}.revanced"
},
)
manifest.setAttribute("package", newPackageName)
if (updatePermissions == true) {
val permissions = manifest.getElementsByTagName("permission").asSequence()
val usesPermissions = manifest.getElementsByTagName("uses-permission").asSequence()
val receiverNotExported = "DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
(permissions + usesPermissions)
.map { it as Element }
.filter { it.getAttribute("android:name") == "$packageName.$receiverNotExported" }
.forEach { it.setAttribute("android:name", "$newPackageName.$receiverNotExported") }
}
if (updateProviders == true) {
val providers = manifest.getElementsByTagName("provider").asSequence()
for (node in providers) {
val provider = node as Element
val authorities = provider.getAttribute("android:authorities")
if (!authorities.startsWith("$packageName.")) continue
provider.setAttribute("android:authorities", authorities.replace(packageName, newPackageName))
}
}
}
}
}

View File

@@ -3,9 +3,9 @@ package app.revanced.patches.music.interaction.permanentshuffle
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.bytecodePatch
@Deprecated("This patch no longer works and will be removed in the future.")
@Suppress("unused")
val permanentShufflePatch = bytecodePatch(
name = "Permanent shuffle",
description = "Permanently remember your shuffle preference " +
"even if the playlist ends or another track is played.",
use = false,

View File

@@ -1,22 +1,16 @@
package app.revanced.patches.music.misc.androidauto
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
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")
compatibleWith("com.google.android.apps.youtube.music"("7.29.52"))
execute {
checkCertificateFingerprint.method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
)
checkCertificateFingerprint.method.returnEarly(true)
}
}

View File

@@ -4,5 +4,6 @@ import app.revanced.patches.music.misc.extension.hooks.applicationInitHook
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch(
"music",
applicationInitHook,
)

View File

@@ -4,7 +4,7 @@ import app.revanced.patcher.patch.Option
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch
import app.revanced.patches.music.misc.spoof.spoofClientPatch
import app.revanced.patches.shared.castContextFetchFingerprint
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
import app.revanced.patches.shared.primeMethodFingerprint
@@ -21,7 +21,7 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
extensionPatch = sharedExtensionPatch,
gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
) {
dependsOn(spoofVideoStreamsPatch)
dependsOn(spoofClientPatch)
compatibleWith(MUSIC_PACKAGE_NAME)
}

View File

@@ -0,0 +1,39 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val playerRequestConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
strings("player")
}
/**
* Matches using the class found in [playerRequestConstructorFingerprint].
*/
internal val createPlayerRequestBodyFingerprint = fingerprint {
parameters("L")
returns("V")
opcodes(
Opcode.CHECK_CAST,
Opcode.IGET,
Opcode.AND_INT_LIT16,
)
strings("ms")
}
/**
* Used to get a reference to other clientInfo fields.
*/
internal val setClientInfoFieldsFingerprint = fingerprint {
returns("L")
strings("Google Inc.")
}
/**
* Used to get a reference to the clientInfo and clientInfo.clientVersion field.
*/
internal val setClientInfoClientVersionFingerprint = fingerprint {
strings("10.29")
}

View File

@@ -0,0 +1,105 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
// TODO: Replace this patch with spoofVideoStreamsPatch once possible.
val spoofClientPatch = bytecodePatch(
name = "Spoof client",
description = "Spoofs the client to fix playback.",
) {
compatibleWith("com.google.android.apps.youtube.music")
dependsOn(
sharedExtensionPatch,
// TODO: Add settingsPatch
userAgentClientSpoofPatch,
)
execute {
val playerRequestClass = playerRequestConstructorFingerprint.classDef
val createPlayerRequestBodyMatch = createPlayerRequestBodyFingerprint.match(playerRequestClass)
val clientInfoContainerClass = createPlayerRequestBodyMatch.method
.getInstruction(createPlayerRequestBodyMatch.patternMatch!!.startIndex)
.getReference<TypeReference>()!!.type
val clientInfoField = setClientInfoClientVersionFingerprint.method.instructions.first {
it.opcode == Opcode.IPUT_OBJECT && it.getReference<FieldReference>()!!.definingClass == clientInfoContainerClass
}.getReference<FieldReference>()!!
val setClientInfoFieldInstructions = setClientInfoFieldsFingerprint.method.instructions.filter {
(it.opcode == Opcode.IPUT_OBJECT || it.opcode == Opcode.IPUT) &&
it.getReference<FieldReference>()!!.definingClass == clientInfoField.type
}.map { it.getReference<FieldReference>()!! }
// Offsets are known for the fields in the clientInfo object.
val clientIdField = setClientInfoFieldInstructions[0]
val clientModelField = setClientInfoFieldInstructions[5]
val osVersionField = setClientInfoFieldInstructions[7]
val clientVersionField = setClientInfoClientVersionFingerprint.method
.getInstruction(setClientInfoClientVersionFingerprint.stringMatches!!.first().index + 1)
.getReference<FieldReference>()
// Helper method to spoof the client info.
val spoofClientInfoMethod = ImmutableMethod(
playerRequestClass.type,
"spoofClientInfo",
listOf(ImmutableMethodParameter(clientInfoContainerClass, null, null)),
"V",
AccessFlags.PRIVATE.value or AccessFlags.STATIC.value,
null,
null,
MutableMethodImplementation(3),
).toMutable().also(playerRequestClass.methods::add).apply {
addInstructions(
"""
iget-object v0, p0, $clientInfoField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientId()I
move-result v1
iput v1, v0, $clientIdField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientModel()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientModelField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientVersion()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $clientVersionField
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getOsVersion()Ljava/lang/String;
move-result-object v1
iput-object v1, v0, $osVersionField
return-void
""",
)
}
createPlayerRequestBodyMatch.method.apply {
val checkCastIndex = createPlayerRequestBodyMatch.patternMatch!!.startIndex
val clientInfoContainerRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(checkCastIndex + 1, "invoke-static {v$clientInfoContainerRegister}, $spoofClientInfoMethod")
}
}
}

View File

@@ -1,7 +0,0 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
compatibleWith("com.google.android.apps.youtube.music")
})

View File

@@ -0,0 +1,5 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.apps.youtube.music")

View File

@@ -2,7 +2,7 @@ package app.revanced.patches.shared.misc.checks
import android.os.Build.*
import app.revanced.patcher.Fingerprint
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
@@ -82,7 +82,7 @@ fun checkEnvironmentPatch(
}
}
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstructions(
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstruction(
0,
"invoke-static/range { p0 .. p0 },$EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
)

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -110,3 +111,13 @@ internal val buildMediaDataSourceFingerprint = fingerprint {
"Ljava/lang/Object;",
)
}
internal const val HLS_CURRENT_TIME_FEATURE_FLAG = 45355374L
internal val hlsCurrentTimeFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("Z", "L")
literal {
HLS_CURRENT_TIME_FEATURE_FLAG
}
}

View File

@@ -12,6 +12,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertFeatureFlagBooleanOverride
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
@@ -23,7 +24,7 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
private const val EXTENSION_CLASS_DESCRIPTOR =
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/shared/spoof/SpoofVideoStreamsPatch;"
fun spoofVideoStreamsPatch(
@@ -201,6 +202,15 @@ fun spoofVideoStreamsPatch(
}
// endregion
// region Fix iOS livestream current time.
hlsCurrentTimeFingerprint.method.insertFeatureFlagBooleanOverride(
HLS_CURRENT_TIME_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->fixHLSCurrentTime(Z)Z"
)
// endregion
executeBlock()
}
}

View File

@@ -0,0 +1,81 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
fun userAgentClientSpoofPatch(originalPackageName: String) = transformInstructionsPatch(
filterMap = { classDef, _, instruction, instructionIndex ->
filterMapInstruction35c<MethodCall>(
"Lapp/revanced/extension",
classDef,
instruction,
instructionIndex,
)
},
transform = transform@{ mutableMethod, entry ->
val (_, _, instructionIndex) = entry
// Replace the result of context.getPackageName(), if it is used in a user agent string.
mutableMethod.apply {
// After context.getPackageName() the result is moved to a register.
val targetRegister = (
getInstruction(instructionIndex + 1)
as? OneRegisterInstruction ?: return@transform
).registerA
// IndexOutOfBoundsException is technically possible here,
// but no such occurrences are present in the app.
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
// Only replace string builder usage.
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
return@transform
}
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
// Changing these package names will result in playback limitations,
// particularly Android VR background audio only playback.
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
opcode == Opcode.CONST_STRING &&
(reference?.string == "android.resource://" || reference?.string == "gcore_")
}
if (resourceOrGmsStringInstructionIndex >= 0) {
return@transform
}
// Overwrite the result of context.getPackageName() with the original package name.
replaceInstruction(
instructionIndex + 1,
"const-string v$targetRegister, \"$originalPackageName\"",
)
}
},
)
@Suppress("unused")
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String,
) : IMethodCall {
GetPackageName(
"Landroid/content/Context;",
"getPackageName",
emptyArray(),
"Ljava/lang/String;",
),
}

View File

@@ -21,7 +21,7 @@ val audioAdsPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "ad.audio.audioAdsPatch")

View File

@@ -19,7 +19,7 @@ val embeddedAdsPatch = bytecodePatch(
settingsPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "ad.embedded.embeddedAdsPatch")

View File

@@ -4,6 +4,6 @@ import app.revanced.patcher.fingerprint
internal val createsUsherClientFingerprint = fingerprint {
custom { method, _ ->
method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;") && method.name == "buildOkHttpClient"
method.name == "buildOkHttpClient" && method.definingClass.endsWith("Ltv/twitch/android/network/OkHttpClientFactory;")
}
}

View File

@@ -141,24 +141,21 @@ val videoAdsPatch = bytecodePatch(
)
// Spoof showAds JSON field.
contentConfigShowAdsFingerprint.method.addInstructions(
// Late versions of the app don't have the method anymore.
contentConfigShowAdsFingerprint.methodOrNull?.addInstructions(
0,
"""
${createConditionInstructions("v0")}
const/4 v0, 0
:$skipLabelName
return v0
""",
${createConditionInstructions("v0")}
const/4 v0, 0
:$skipLabelName
return v0
""",
)
}
},
)
compatibleWith(
"tv.twitch.android.app"(
"15.4.1",
"16.1.0",
"16.9.1",
),
"tv.twitch.android.app",
)
}

View File

@@ -22,7 +22,7 @@ val showDeletedMessagesPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
fun createSpoilerConditionInstructions(register: String = "v0") = """
invoke-static {}, Lapp/revanced/extension/twitch/patches/ShowDeletedMessagesPatch;->shouldUseSpoiler()Z

View File

@@ -20,7 +20,7 @@ val autoClaimChannelPointsPatch = bytecodePatch(
addResourcesPatch,
)
compatibleWith("tv.twitch.android.app"("15.4.1", "16.1.0", "16.9.1"))
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "chat.autoclaim.autoClaimChannelPointsPatch")

View File

@@ -48,13 +48,7 @@ val settingsPatch = bytecodePatch(
settingsPatch(preferences = preferences),
)
compatibleWith(
"tv.twitch.android.app"(
"15.4.1",
"16.1.0",
"16.9.1",
),
)
compatibleWith("tv.twitch.android.app")
execute {
addResources("twitch", "misc.settings.settingsPatch")

View File

@@ -3,6 +3,7 @@ package app.revanced.patches.youtube.layout.buttons.navigation
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.AccessFlags
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
internal const val ANDROID_AUTOMOTIVE_STRING = "Android Automotive"
@@ -22,4 +23,31 @@ internal val createPivotBarFingerprint = fingerprint {
Opcode.INVOKE_VIRTUAL,
Opcode.RETURN_VOID,
)
}
internal const val TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG = 45400535L
internal val translucentNavigationStatusBarFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
literal { TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG }
}
internal const val TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG = 45630927L
internal val translucentNavigationButtonsFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
literal { TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG }
}
/**
* The device on screen back/home/recent buttons.
*/
internal const val TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG = 45632194L
internal val translucentNavigationButtonsSystemFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("Z")
literal { TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG }
}

View File

@@ -12,10 +12,13 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.navigation.hookNavigationButtonCreated
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertFeatureFlagBooleanOverride
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@@ -32,6 +35,7 @@ val navigationButtonsPatch = bytecodePatch(
settingsPatch,
addResourcesPatch,
navigationBarHookPatch,
versionCheckPatch
)
compatibleWith(
@@ -50,19 +54,27 @@ val navigationButtonsPatch = bytecodePatch(
execute {
addResources("youtube", "layout.buttons.navigation.navigationButtonsPatch")
val preferences = mutableSetOf(
SwitchPreference("revanced_hide_home_button"),
SwitchPreference("revanced_hide_shorts_button"),
SwitchPreference("revanced_hide_create_button"),
SwitchPreference("revanced_hide_subscriptions_button"),
SwitchPreference("revanced_switch_create_with_notifications_button"),
SwitchPreference("revanced_hide_navigation_button_labels"),
)
if (is_19_25_or_greater) {
preferences += SwitchPreference("revanced_disable_translucent_status_bar")
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_light")
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_dark")
}
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
PreferenceScreenPreference(
key = "revanced_navigation_buttons_screen",
sorting = Sorting.UNSORTED,
preferences = setOf(
SwitchPreference("revanced_hide_home_button"),
SwitchPreference("revanced_hide_shorts_button"),
SwitchPreference("revanced_hide_create_button"),
SwitchPreference("revanced_hide_subscriptions_button"),
SwitchPreference("revanced_switch_create_with_notifications_button"),
SwitchPreference("revanced_hide_navigation_button_labels"),
),
),
preferences = preferences
)
)
// Switch create with notifications button.
@@ -101,5 +113,24 @@ val navigationButtonsPatch = bytecodePatch(
// Hook navigation button created, in order to hide them.
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
// Force on/off translucent effect on status bar and navigation buttons.
if (is_19_25_or_greater) {
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
)
translucentNavigationButtonsFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
)
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
)
}
}
}

View File

@@ -154,6 +154,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
PreferenceScreenPreference(
"revanced_comments_screen",
preferences = setOf(
SwitchPreference("revanced_hide_comments_chat_summary"),
SwitchPreference("revanced_hide_comments_by_members_header"),
SwitchPreference("revanced_hide_comments_section"),
SwitchPreference("revanced_hide_comments_create_a_short_button"),
@@ -221,7 +222,6 @@ val hideLayoutComponentsPatch = bytecodePatch(
SwitchPreference("revanced_hide_notify_me_button"),
SwitchPreference("revanced_hide_playables"),
SwitchPreference("revanced_hide_search_result_recommendations"),
SwitchPreference("revanced_hide_search_result_shelf_header"),
SwitchPreference("revanced_hide_show_more_button"),
SwitchPreference("revanced_hide_doodles"),
)

View File

@@ -240,14 +240,14 @@ val miniplayerPatch = bytecodePatch(
),
)
fun MutableMethod.insertBooleanOverride(index: Int, methodName: String) {
fun MutableMethod.insertMiniplayerBooleanOverride(index: Int, methodName: String) {
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z
move-result v$register
""",
invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->$methodName(Z)Z
move-result v$register
"""
)
}
@@ -257,29 +257,25 @@ val miniplayerPatch = bytecodePatch(
* Adds an override to force legacy tablet miniplayer to be used or not used.
*/
fun MutableMethod.insertLegacyTabletMiniplayerOverride(index: Int) {
insertBooleanOverride(index, "getLegacyTabletMiniplayerOverride")
insertMiniplayerBooleanOverride(index, "getLegacyTabletMiniplayerOverride")
}
/**
* Adds an override to force modern miniplayer to be used or not used.
*/
fun MutableMethod.insertModernMiniplayerOverride(index: Int) {
insertBooleanOverride(index, "getModernMiniplayerOverride")
insertMiniplayerBooleanOverride(index, "getModernMiniplayerOverride")
}
fun Fingerprint.insertLiteralValueBooleanOverride(
fun Fingerprint.insertMiniplayerFeatureFlagBooleanOverride(
literal: Long,
extensionMethod: String,
) {
method.apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal)
val targetIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
) = method.insertFeatureFlagBooleanOverride(
literal,
"$EXTENSION_CLASS_DESCRIPTOR->$extensionMethod(Z)Z"
)
insertBooleanOverride(targetIndex + 1, extensionMethod)
}
}
fun Fingerprint.insertLiteralValueFloatOverride(
fun Fingerprint.insertMiniplayerFeatureFlagFloatOverride(
literal: Long,
extensionMethod: String,
) {
@@ -370,24 +366,24 @@ val miniplayerPatch = bytecodePatch(
}
if (is_19_23_or_greater) {
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
MINIPLAYER_DRAG_DROP_FEATURE_KEY,
"enableMiniplayerDragAndDrop",
)
}
if (is_19_25_or_greater) {
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
MINIPLAYER_MODERN_FEATURE_LEGACY_KEY,
"getModernMiniplayerOverride",
)
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
MINIPLAYER_MODERN_FEATURE_KEY,
"getModernFeatureFlagsActiveOverride",
)
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
MINIPLAYER_DOUBLE_TAP_FEATURE_KEY,
"enableMiniplayerDoubleTapAction",
)
@@ -426,19 +422,19 @@ val miniplayerPatch = bytecodePatch(
}
if (is_19_36_or_greater) {
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
MINIPLAYER_ROUNDED_CORNERS_FEATURE_KEY,
"setRoundedCorners",
)
}
if (is_19_43_or_greater) {
miniplayerOnCloseHandlerFingerprint.insertLiteralValueBooleanOverride(
miniplayerOnCloseHandlerFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
MINIPLAYER_DISABLED_FEATURE_KEY,
"getMiniplayerOnCloseHandler"
)
miniplayerModernConstructorFingerprint.insertLiteralValueBooleanOverride(
miniplayerModernConstructorFingerprint.insertMiniplayerFeatureFlagBooleanOverride(
MINIPLAYER_HORIZONTAL_DRAG_FEATURE_KEY,
"setHorizontalDrag",
)

View File

@@ -0,0 +1,28 @@
package app.revanced.patches.youtube.layout.player.fullscreen
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
internal const val OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG = 45666112L
internal val openVideosFullscreenPortraitFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("L", "Lj\$/util/Optional;")
literal {
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG
}
}
/**
* Used to enable opening regular videos fullscreen.
*/
internal val openVideosFullscreenHookPatchExtensionFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("Z")
parameters()
custom { methodDef, classDef ->
methodDef.name == "isFullScreenPatchIncluded" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.youtube.layout.player.fullscreen
import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
@Deprecated("Renamed to openVideosFullscreenPatch", ReplaceWith("openVideosFullscreenPatch"))
val openVideosFullscreen = bytecodePatch{
dependsOn(openVideosFullscreenPatch)
}

View File

@@ -0,0 +1,32 @@
package app.revanced.patches.youtube.layout.player.fullscreen
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.youtube.layout.shortsplayer.openShortsInRegularPlayerPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.util.insertFeatureFlagBooleanOverride
internal const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch;"
/**
* Used by both [openVideosFullscreenPatch] and [openShortsInRegularPlayerPatch].
*/
internal val openVideosFullscreenHookPatch = bytecodePatch {
dependsOn(
sharedExtensionPatch,
versionCheckPatch
)
execute {
if (!is_19_46_or_greater) {
return@execute
}
openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride(
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
)
}
}

View File

@@ -0,0 +1,46 @@
package app.revanced.patches.youtube.layout.player.fullscreen
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.playservice.is_19_46_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.returnEarly
@Suppress("unused")
val openVideosFullscreenPatch = bytecodePatch(
name = "Open videos fullscreen",
description = "Adds an option to open videos in full screen portrait mode.",
) {
dependsOn(
openVideosFullscreenHookPatch,
settingsPatch,
addResourcesPatch,
versionCheckPatch
)
compatibleWith(
"com.google.android.youtube"(
"19.46.42",
)
)
execute {
if (!is_19_46_or_greater) {
throw PatchException("'Open videos fullscreen' requires 19.46.42 or greater")
}
addResources("youtube", "layout.player.fullscreen.openVideosFullscreen")
PreferenceScreen.PLAYER.addPreferences(
SwitchPreference("revanced_open_videos_fullscreen_portrait")
)
// Enable the logic for the user Setting to open regular videos fullscreen.
openVideosFullscreenHookPatchExtensionFingerprint.method.returnEarly(true)
}
}

View File

@@ -24,6 +24,7 @@ import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import app.revanced.util.inputStreamFromBundledResource
import app.revanced.util.insertFeatureFlagBooleanOverride
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
@@ -228,19 +229,10 @@ val seekbarColorPatch = bytecodePatch(
// 19.25+ changes
playerSeekbarGradientConfigFingerprint.method.apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG)
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
addInstructions(
resultIndex + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z
move-result v$register
"""
)
}
playerSeekbarGradientConfigFingerprint.method.insertFeatureFlagBooleanOverride(
PLAYER_SEEKBAR_GRADIENT_FEATURE_FLAG,
"$EXTENSION_CLASS_DESCRIPTOR->playerSeekbarGradientEnabled(Z)Z"
)
lithoLinearGradientFingerprint.method.addInstruction(
0,
@@ -255,19 +247,10 @@ val seekbarColorPatch = bytecodePatch(
launchScreenLayoutTypeFingerprint,
mainActivityOnCreateFingerprint
).forEach { fingerprint ->
fingerprint.method.apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(launchScreenLayoutTypeLotteFeatureFlag)
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA
addInstructions(
resultIndex + 1,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z
move-result v$register
"""
)
}
fingerprint.method.insertFeatureFlagBooleanOverride(
launchScreenLayoutTypeLotteFeatureFlag,
"$EXTENSION_CLASS_DESCRIPTOR->useLotteLaunchSplashScreen(Z)Z"
)
}
// Hook the splash animation drawable to set the a seekbar color theme.

View File

@@ -1,5 +1,6 @@
package app.revanced.patches.youtube.layout.shortsautoplay
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.bytecodePatch
@@ -56,7 +57,7 @@ val shortsAutoplayPatch = bytecodePatch(
}
// Main activity is used to check if app is in pip mode.
mainActivityOnCreateFingerprint.method.addInstructions(
mainActivityOnCreateFingerprint.method.addInstruction(
1,
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
"setMainActivity(Landroid/app/Activity;)V",

View File

@@ -0,0 +1,56 @@
package app.revanced.patches.youtube.layout.shortsplayer
import app.revanced.patcher.fingerprint
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
/**
* Purpose of this method is not clear, and it's only used to identify
* the obfuscated name of the videoId() method in PlaybackStartDescriptor.
*/
internal val playbackStartFeatureFlagFingerprint = fingerprint {
returns("Z")
parameters(
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
)
literal {
45380134L
}
}
// Pre 19.25
internal val shortsPlaybackIntentLegacyFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters(
"L",
"Ljava/util/Map;",
"J",
"Ljava/lang/String;",
"Z",
"Ljava/util/Map;"
)
strings(
// None of these strings are unique.
"com.google.android.apps.youtube.app.endpoint.flags",
"ReelWatchFragmentArgs",
"reels_fragment_descriptor"
)
}
internal val shortsPlaybackIntentFingerprint = fingerprint {
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
returns("V")
parameters(
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
"Ljava/util/Map;",
"J",
"Ljava/lang/String;"
)
strings(
// None of these strings are unique.
"com.google.android.apps.youtube.app.endpoint.flags",
"ReelWatchFragmentArgs",
"reels_fragment_descriptor"
)
}

View File

@@ -0,0 +1,131 @@
package app.revanced.patches.youtube.layout.shortsplayer
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.youtube.layout.player.fullscreen.openVideosFullscreenHookPatch
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
import app.revanced.patches.youtube.misc.playservice.is_19_46_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.patches.youtube.shared.mainActivityOnCreateFingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/OpenShortsInRegularPlayerPatch;"
@Suppress("unused")
val openShortsInRegularPlayerPatch = bytecodePatch(
name = "Open Shorts in regular player",
description = "Adds options to open Shorts in the regular video player.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
openVideosFullscreenHookPatch,
navigationBarHookPatch,
versionCheckPatch
)
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
),
)
execute {
addResources("youtube", "layout.shortsplayer.shortsPlayerTypePatch")
PreferenceScreen.SHORTS.addPreferences(
if (is_19_46_or_greater) {
ListPreference(
key = "revanced_shorts_player_type",
summaryKey = null,
)
} else {
ListPreference(
key = "revanced_shorts_player_type",
summaryKey = null,
entriesKey = "revanced_shorts_player_type_legacy_entries",
entryValuesKey = "revanced_shorts_player_type_legacy_entry_values"
)
}
)
// Activity is used as the context to launch an Intent.
mainActivityOnCreateFingerprint.method.addInstruction(
1,
"invoke-static/range { p0 .. p0 }, ${EXTENSION_CLASS_DESCRIPTOR}->" +
"setMainActivity(Landroid/app/Activity;)V",
)
// Find the obfuscated method name for PlaybackStartDescriptor.videoId()
val playbackStartVideoIdMethodName = playbackStartFeatureFlagFingerprint.method.let {
val stringMethodIndex = it.indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.definingClass == "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
&& reference.returnType == "Ljava/lang/String;"
}
navigate(it).to(stringMethodIndex).stop().name
}
fun extensionInstructions(playbackStartRegister: Int, freeRegister: Int) =
"""
invoke-virtual { v$playbackStartRegister }, Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;->$playbackStartVideoIdMethodName()Ljava/lang/String;
move-result-object v$freeRegister
invoke-static { v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->openShort(Ljava/lang/String;)Z
move-result v$freeRegister
if-eqz v$freeRegister, :disabled
return-void
:disabled
nop
"""
if (!is_19_25_or_greater) {
shortsPlaybackIntentLegacyFingerprint.method.apply {
val index = indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.returnType ==
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
}
val freeRegister = getInstruction<FiveRegisterInstruction>(index).registerC
val playbackStartRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
addInstructionsWithLabels(
index + 2,
extensionInstructions(playbackStartRegister, freeRegister)
)
}
return@execute
}
shortsPlaybackIntentFingerprint.method.addInstructionsWithLabels(
0,
"""
move-object/from16 v0, p1
${extensionInstructions(0, 1)}
"""
)
}
}

View File

@@ -1,7 +1,6 @@
package app.revanced.patches.youtube.layout.theme
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.patch.resourcePatch
@@ -17,10 +16,7 @@ import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.forEachChildElement
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import app.revanced.util.insertFeatureFlagBooleanOverride
import org.w3c.dom.Element
private const val EXTENSION_CLASS_DESCRIPTOR =
@@ -212,19 +208,10 @@ val themePatch = bytecodePatch(
SwitchPreference("revanced_gradient_loading_screen"),
)
useGradientLoadingScreenFingerprint.method.apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(GRADIENT_LOADING_SCREEN_AB_CONSTANT)
val isEnabledIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val isEnabledRegister = getInstruction<OneRegisterInstruction>(isEnabledIndex).registerA
addInstructions(
isEnabledIndex + 1,
"""
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled()Z
move-result v$isEnabledRegister
""",
)
}
useGradientLoadingScreenFingerprint.method.insertFeatureFlagBooleanOverride(
GRADIENT_LOADING_SCREEN_AB_CONSTANT,
"$EXTENSION_CLASS_DESCRIPTOR->gradientLoadingScreenEnabled(Z)Z"
)
mapOf(
themeHelperLightColorFingerprint to lightThemeBackgroundColor,

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.youtube.misc.announcements
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -41,7 +41,7 @@ val announcementsPatch = bytecodePatch(
SwitchPreference("revanced_announcements"),
)
mainActivityOnCreateFingerprint.method.addInstructions(
mainActivityOnCreateFingerprint.method.addInstruction(
// Insert index must be greater than the insert index used by GmsCoreSupport,
// as both patch the same method and GmsCore check should be first.
1,

View File

@@ -110,22 +110,22 @@ val enableDebuggingPatch = bytecodePatch(
"""
)
}
}
experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
experimentalStringFeatureFlagFingerprint.match(
experimentalFeatureFlagParentFingerprint.originalClassDef
).method.apply {
val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.MOVE_RESULT_OBJECT)
addInstructions(
insertIndex,
"""
move-result-object v0
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
)
addInstructions(
insertIndex,
"""
move-result-object v0
invoke-static { v0, p1, p2, p3 }, $EXTENSION_CLASS_DESCRIPTOR->isStringFeatureFlagEnabled(Ljava/lang/String;JLjava/lang/String;)Ljava/lang/String;
move-result-object v0
return-object v0
"""
)
}
}
// There exists other experimental accessor methods for byte[]

View File

@@ -1,6 +1,6 @@
package app.revanced.patches.youtube.misc.dns
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.all.misc.resources.addResourcesPatch
@@ -31,7 +31,7 @@ val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch(
execute {
addResources("youtube", "misc.dns.checkWatchHistoryDomainNameResolutionPatch")
mainActivityOnCreateFingerprint.method.addInstructions(
mainActivityOnCreateFingerprint.method.addInstruction(
// FIXME: Insert index must be greater than the insert index used by GmsCoreSupport,
// as both patch the same method and GmsCoreSupport check should be first,
// but the patch does not depend on GmsCoreSupport, so it should not be possible to enforce this

View File

@@ -34,10 +34,7 @@ internal val disableCairoSettingsPatch = bytecodePatch(
* <a href="https://github.com/qnblackcat/uYouPlus/issues/1468">uYouPlus#1468</a>.
*/
cairoFragmentConfigFingerprint.method.apply {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(
CAIRO_CONFIG_LITERAL_VALUE,
)
val literalIndex = indexOfFirstLiteralInstructionOrThrow(CAIRO_CONFIG_LITERAL_VALUE)
val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(resultIndex).registerA

View File

@@ -40,9 +40,16 @@ val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
"revanced_spoof_video_streams_client",
summaryKey = null,
),
ListPreference(
"revanced_spoof_video_streams_language",
summaryKey = null
),
SwitchPreference("revanced_spoof_video_streams_ios_force_avc"),
NonInteractivePreference("revanced_spoof_video_streams_about_android_vr"),
NonInteractivePreference("revanced_spoof_video_streams_about_ios"),
// Preference requires a title but the actual text is chosen at runtime.
NonInteractivePreference(
key = "revanced_spoof_video_streams_about_android_vr",
tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference"
),
),
),
)

View File

@@ -1,82 +1,5 @@
package app.revanced.patches.youtube.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.all.misc.transformation.IMethodCall
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.StringReference
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
private const val ORIGINAL_PACKAGE_NAME = "com.google.android.youtube"
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
val userAgentClientSpoofPatch = transformInstructionsPatch(
filterMap = { classDef, _, instruction, instructionIndex ->
filterMapInstruction35c<MethodCall>(
"Lapp/revanced/extension",
classDef,
instruction,
instructionIndex,
)
},
transform = transform@{ mutableMethod, entry ->
val (_, _, instructionIndex) = entry
// Replace the result of context.getPackageName(), if it is used in a user agent string.
mutableMethod.apply {
// After context.getPackageName() the result is moved to a register.
val targetRegister = (
getInstruction(instructionIndex + 1)
as? OneRegisterInstruction ?: return@transform
).registerA
// IndexOutOfBoundsException is technically possible here,
// but no such occurrences are present in the app.
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
// Only replace string builder usage.
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
return@transform
}
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
// Changing these package names will result in playback limitations,
// particularly Android VR background audio only playback.
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
val reference = getReference<StringReference>()
opcode == Opcode.CONST_STRING &&
(reference?.string == "android.resource://" || reference?.string == "gcore_")
}
if (resourceOrGmsStringInstructionIndex >= 0) {
return@transform
}
// Overwrite the result of context.getPackageName() with the original package name.
replaceInstruction(
instructionIndex + 1,
"const-string v$targetRegister, \"$ORIGINAL_PACKAGE_NAME\"",
)
}
},
)
@Suppress("unused")
private enum class MethodCall(
override val definedClassName: String,
override val methodName: String,
override val methodParams: Array<String>,
override val returnType: String,
) : IMethodCall {
GetPackageName(
"Landroid/content/Context;",
"getPackageName",
emptyArray(),
"Ljava/lang/String;",
),
}
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.youtube")

View File

@@ -0,0 +1,23 @@
package app.revanced.patches.youtube.video.audio
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val streamingModelBuilderFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("L")
strings("vprng")
}
internal val menuItemAudioTrackFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters("L")
returns("V")
strings("menu_item_audio_track")
}
internal val audioStreamingTypeSelector = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
returns("L")
strings("raw") // String is not unique
}

View File

@@ -0,0 +1,159 @@
package app.revanced.patches.youtube.video.audio
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.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.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.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
private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/youtube/patches/ForceOriginalAudioPatch;"
@Suppress("unused")
val forceOriginalAudioPatch = bytecodePatch(
name = "Force original audio",
description = "Adds an option to always use the original audio track.",
) {
dependsOn(
sharedExtensionPatch,
settingsPatch,
addResourcesPatch,
)
compatibleWith(
"com.google.android.youtube"(
"18.38.44",
"18.49.37",
"19.16.39",
"19.25.37",
"19.34.42",
"19.43.41",
"19.45.38",
"19.46.42",
),
)
execute {
addResources("youtube", "video.audio.forceOriginalAudioPatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference("revanced_force_original_audio")
)
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()
}
// 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
formatStreamModelClass.apply {
// Add a new field to store the override.
val helperFieldName = "isDefaultAudioTrackOverride"
fields.add(
ImmutableField(
type,
helperFieldName,
"Ljava/lang/Boolean;",
// Boolean is a 100% immutable class (all fields are final)
// and safe to write to a shared field without volatile/synchronization,
// but without volatile the field can show stale data
// and the same field is calculated more than once by different threads.
AccessFlags.PRIVATE.value or AccessFlags.VOLATILE.value,
null,
null,
null
).toMutable()
)
// Add a helper method because the isDefaultAudioTrack() has only 2 registers and 3 are needed.
val helperMethodClass = type
val helperMethodName = "extension_isDefaultAudioTrack"
val helperMethod = ImmutableMethod(
helperMethodClass,
helperMethodName,
listOf(ImmutableMethodParameter("Z", null, null)),
"Z",
AccessFlags.PRIVATE.value,
null,
null,
MutableMethodImplementation(6),
).toMutable().apply {
addInstructionsWithLabels(
0,
"""
iget-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
if-eqz v0, :call_extension
invoke-virtual { v0 }, Ljava/lang/Boolean;->booleanValue()Z
move-result v3
return v3
:call_extension
invoke-virtual { p0 }, $audioTrackIdMethod
move-result-object v1
invoke-virtual { p0 }, $audioTrackDisplayNameMethod
move-result-object v2
invoke-static { p1, v1, v2 }, $EXTENSION_CLASS_DESCRIPTOR->isDefaultAudioStream(ZLjava/lang/String;Ljava/lang/String;)Z
move-result v3
invoke-static { v3 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
move-result-object v0
iput-object v0, p0, $helperMethodClass->$helperFieldName:Ljava/lang/Boolean;
return v3
"""
)
}
methods.add(helperMethod)
// Modify isDefaultAudioTrack() to call extension helper method.
isDefaultMethod.apply {
val index = indexOfFirstInstructionOrThrow(Opcode.RETURN)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index,
"""
invoke-direct { p0, v$register }, $helperMethodClass->$helperMethodName(Z)Z
move-result v$register
"""
)
}
}
}
}

View File

@@ -17,6 +17,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
import com.android.tools.smali.dexlib2.iface.reference.Reference
@@ -402,6 +403,20 @@ fun Method.findInstructionIndicesReversedOrThrow(opcode: Opcode): List<Int> {
return instructions
}
internal fun MutableMethod.insertFeatureFlagBooleanOverride(literal: Long, extensionsMethod: String) {
val literalIndex = indexOfFirstLiteralInstructionOrThrow(literal)
val index = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
val register = getInstruction<OneRegisterInstruction>(index).registerA
addInstructions(
index + 1,
"""
invoke-static { v$register }, $extensionsMethod
move-result v$register
"""
)
}
/**
* Called for _all_ instructions with the given literal value.
*/

View File

@@ -1,8 +1,8 @@
package app.revanced.util.resource
import app.revanced.patcher.patch.PatchException
import org.w3c.dom.Document
import org.w3c.dom.Node
import java.util.logging.Logger
/**
* A string value.
@@ -19,13 +19,36 @@ class StringResource(
) : BaseResource(name, "string") {
override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) =
super.serialize(ownerDocument, resourceCallback).apply {
fun String.validateAndroidStringEscaping() : String {
if (value.startsWith('"') && value.endsWith('"')) {
// Raw strings allow unescaped single quote but not double quote.
if (!value.substring(1, value.length - 1).contains(Regex("(?<!\\\\)[\"]"))) {
return this;
}
} else {
if (value.contains('\n')) {
// Don't throw an exception, otherwise unnoticed mistakes
// in Crowdin can cause patching failures.
// Incorrectly escaped strings still work but do not display as intended.
Logger.getLogger(StringResource.javaClass.name).severe(
"String $name is not raw but contains encoded new line characters: $value")
}
if (!value.contains(Regex("(?<!\\\\)['\"]"))) {
return this;
}
}
Logger.getLogger(StringResource.javaClass.name).severe(
"String $name cannot contain unescaped quotes in value: $value")
return this;
}
// if the string is un-formatted, explicitly add the formatted attribute
if (!formatted) setAttribute("formatted", "false")
if (value.contains(Regex("(?<!\\\\)['\"]")))
throw PatchException("String $name cannot contain unescaped quotes in value \"$value\".")
textContent = value
textContent = value.validateAndroidStringEscaping();
}
companion object {

View File

@@ -1,34 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
Raw strings are required because Crowdin AI translations regularly gets confused and
replace \n with an encoded new line character.
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Bad:
<string name="summary_key">First \'item\' text\nSecond \"item\" text</string>
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
Good:
<string name="summary_key">"First 'item' text
Second \"item\" text"</string>
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
@@ -147,6 +136,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.returnYouTubeDislikePatch">
@@ -209,6 +200,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
</patch>

View File

@@ -1,34 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
Raw strings are required because Crowdin AI translations regularly gets confused and
replace \n with an encoded new line character.
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Bad:
<string name="summary_key">First \'item\' text\nSecond \"item\" text</string>
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
Good:
<string name="summary_key">"First 'item' text
Second \"item\" text"</string>
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
@@ -147,6 +136,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.returnYouTubeDislikePatch">
@@ -209,6 +200,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
</patch>

View File

@@ -1,34 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
Raw strings are required because Crowdin AI translations regularly gets confused and
replace \n with an encoded new line character.
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Bad:
<string name="summary_key">First \'item\' text\nSecond \"item\" text</string>
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
Good:
<string name="summary_key">"First 'item' text
Second \"item\" text"</string>
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
@@ -44,6 +33,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_check_environment_not_near_patch_time_invalid">تاريخ إنشاء APK تالف</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_title">ReVanced</string>
<string name="revanced_settings_confirm_user_dialog_title">هل ترغب في المتابعة؟</string>
<string name="revanced_settings_reset">إعادة التعيين</string>
<string name="revanced_settings_restart_title">تحديث وإعادة تشغيل</string>
@@ -58,18 +48,26 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- Settings about dialog. -->
<string name="revanced_settings_about_links_body">أنت تستخدم إصدار ReVanced Patches &lt;i&gt;%s&lt;/i&gt;</string>
<string name="revanced_settings_about_links_dev_header">ملاحظة</string>
<string name="revanced_settings_about_links_dev_body">هذا الإصدار هو إصدار مسبق، وقد تواجه مشاكل غير متوقعة</string>
<string name="revanced_settings_about_links_dev_body">هذه النسخة هي إصدار مسبق وقد تواجه مشكلات غير متوقعة</string>
<string name="revanced_settings_about_links_header">الروابط الرسمية</string>
<!-- NOTE: the about strings above are duplicated in the TikTok about screen code,
and changes made here must also be made there. -->
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="gms_core_toast_not_installed_message">لم يتم تثبيت MicroG GmsCore . قم بتثبيته.</string>
<string name="gms_core_toast_not_installed_message">لم يتم تثبيت MicroG GmsCore. قم بتثبيته.</string>
<string name="gms_core_dialog_title">الإجراء مطلوب</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">لا يملك MicroG GmsCore الصلاحية للتشغيل في الخلفية.\n\nاتبع دليل \"لا تقتل تطبيقي\" للهاتف الخاص بك، وقم بتطبيق الإرشادات على تثبيت المايكروج.\n\nهذا مطلوب لكي يعمل التطبيق.</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"لا يملك MicroG GmsCore صلاحية التشغيل في الخلفية.
اتبع دليل \"لا تقتل تطبيقي\" لهاتفك، وطبّق التعليمات لتثبيت MicroG.
هذا مطلوب لعمل التطبيق."</string>
<string name="gms_core_dialog_open_website_text">فتح الموقع</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">يجب تعطيل تحسينات بطارية MicroG GmsCore لمنع حدوث مشكلات.\n\nاضغط على زر المتابعة وقم بتعطيل تحسينات البطارية.</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"يجب تعطيل تحسينات بطارية MicroG GmsCore لمنع حدوث مشكلات.
لن يؤثر تعطيل تحسينات البطارية لـ MicroG سلبًا على استخدام البطارية.
انقر فوق زر الاستمرار واسمح بتغييرات التحسين."</string>
<string name="gms_core_dialog_continue_text">متابعة</string>
</patch>
</app>
@@ -106,7 +104,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_debug_toast_on_error_title">عرض ملاحظة عند وجود خطأ في ReVanced</string>
<string name="revanced_debug_toast_on_error_summary_on">يتم عرض ملاحظة في حالة حدوث خطأ</string>
<string name="revanced_debug_toast_on_error_summary_off">لا يتم عرض ملاحظة في حالة حدوث خطأ</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">يؤدي إيقاف تشغيل ملاحظات الأخطاء إلى إخفاء كافة إشعارات خطأ ReVanced.\n\nلن يتم إعلامك بأي أخطاء غير متوقعة.</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"يؤدي إيقاف تشغيل ملاحظات الأخطاء إلى إخفاء كافة إشعارات أخطاء ReVanced.
لن يتم إعلامك بأي أخطاء غير متوقعة."</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_disable_like_subscribe_glow_title">تعطيل توهج زر أعجبني / اشتراك</string>
@@ -125,7 +125,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_channel_watermark_summary_on">تم إخفاء علامة الفيديو المائية</string>
<string name="revanced_hide_channel_watermark_summary_off">يتم عرض علامة الفيديو المائية</string>
<string name="revanced_hide_horizontal_shelves_title">إخفاء الرفوف الأفقية</string>
<string name="revanced_hide_horizontal_shelves_summary_on">تكون الرفوف مخفية مثل:\n• الأخبار العاجلة\n• متابعة المشاهدة\n• استكشاف المزيد من القنوات\n• التسوق\n• مشاهدة مرة أخرى</string>
<string name="revanced_hide_horizontal_shelves_summary_on">"تكون الرفوف مخفية مثل:
• الأخبار العاجلة
• متابعة المشاهدة
• استكشاف المزيد من القنوات
• التسوق
• مشاهدة مرة أخرى"</string>
<string name="revanced_hide_horizontal_shelves_summary_off">يتم عرض الرفوف</string>
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
@@ -153,9 +158,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_timed_reactions_title">إخفاء ردود الفعل المؤقتة</string>
<string name="revanced_hide_timed_reactions_summary_on">تم إخفاء ردود الفعل المؤقتة</string>
<string name="revanced_hide_timed_reactions_summary_off">يتم عرض ردود الفعل المؤقتة</string>
<string name="revanced_hide_search_result_shelf_header_title">إخفاء رأس رف نتائج البحث</string>
<string name="revanced_hide_search_result_shelf_header_summary_on">تم إخفاء رأس الرف</string>
<string name="revanced_hide_search_result_shelf_header_summary_off">يتم عرض رأس الرف</string>
<string name="revanced_hide_channel_guidelines_title">إخفاء إرشادات القناة</string>
<string name="revanced_hide_channel_guidelines_summary_on">تم إخفاء إرشادات القناة</string>
<string name="revanced_hide_channel_guidelines_summary_off">يتم عرض إرشادات القناة</string>
@@ -226,8 +228,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_chapters_section_summary_on">تم إخفاء قسم الفصول</string>
<string name="revanced_hide_chapters_section_summary_off">يتم عرض قسم الفصول</string>
<string name="revanced_hide_podcast_section_title">إخفاء قسم \'استكشاف البودكاست\'</string>
<string name="revanced_hide_podcast_section_summary_on">يتم عرض قسم \'استكشاف البودكاست\'</string>
<string name="revanced_hide_podcast_section_summary_off">يتم عرض قسم \'استكشاف البودكاست\'</string>
<string name="revanced_hide_podcast_section_summary_on">تم إخفاء قسم \"استكشاف البودكاست\"</string>
<string name="revanced_hide_podcast_section_summary_off">يتم عرض قسم \"استكشاف البودكاست\"</string>
<string name="revanced_hide_info_cards_section_title">إخفاء قسم بطاقات المعلومات</string>
<string name="revanced_hide_info_cards_section_summary_on">تم إخفاء قسم بطاقات المعلومات</string>
<string name="revanced_hide_info_cards_section_summary_off">يتم عرض قسم بطاقات المعلومات</string>
@@ -252,6 +254,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">يعرض في الفيديوهات ذات الصلة</string>
<string name="revanced_comments_screen_title">التعليقات</string>
<string name="revanced_comments_screen_summary">إخفاء أو عرض مكونات قسم التعليقات</string>
<string name="revanced_hide_comments_chat_summary_title">إخفاء \"ملخص الدردشة\" </string>
<string name="revanced_hide_comments_chat_summary_summary_on">تم إخفاء “ملخص الدردشة”</string>
<string name="revanced_hide_comments_chat_summary_summary_off">يتم عرض “ملخص الدردشة”</string>
<string name="revanced_hide_comments_by_members_header_title">إخفاء رأس \'تعليقات الأعضاء\'</string>
<string name="revanced_hide_comments_by_members_header_summary_on">تم إخفاء رأس \'تعليقات الأعضاء\'</string>
<string name="revanced_hide_comments_by_members_header_summary_off">يتم عرض رأس \'تعليقات الأعضاء\'</string>
@@ -274,7 +279,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_doodles_title">إخفاء رسومات YouTube</string>
<string name="revanced_hide_doodles_summary_on">تم إخفاء رسومات شريط البحث</string>
<string name="revanced_hide_doodles_summary_off">يتم عرض رسومات شريط البحث</string>
<string name="revanced_hide_doodles_user_dialog_message">تظهر رسومات YouTube Doodles لعدة أيام كل عام.\n\nإذا كانت الرسومات تظهر حاليًا في منطقتك وإعداد الإخفاء هذا قيد التشغيل، سيتم أيضًا إخفاء شريط الفلتر أسفل شريط البحث.</string>
<string name="revanced_hide_doodles_user_dialog_message">"تظهر \"\"Doodles\"\" على YouTube بضعة أيام كل عام.
إذا كان يتم عرض \"Doodle\" حاليًا في منطقتك وإذا كان إعداد الإخفاء هذا قيد التشغيل، فسيتم أيضًا إخفاء شريط التصفية أسفل شريط البحث."</string>
<string name="revanced_custom_filter_screen_title">فلتر مخصص</string>
<string name="revanced_custom_filter_screen_summary">إخفاء المكونات باستخدام فلاتر مخصصة</string>
<string name="revanced_custom_filter_title">تمكين الفلتر المخصص</string>
@@ -298,9 +305,18 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_content_phrases_title">الكلمات المفتاحية المراد إخفاؤها</string>
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<string name="revanced_hide_keyword_content_phrases_summary">الكلمات والعبارات الرئيسية التي يجب إخفاؤها، مفصولة بسطر جديد\n\nالكلمات الرئيسية يمكن أن تكون أسماء قنوات أو أي نص يظهر في عناوين الفيديو\n\nيجب إدخال الكلمات التي تحتوي على أحرف كبيرة في الوسط بإستخدام الأحرف الكبيرة (مثال: iPhone، TikTok، LeBlanc)</string>
<string name="revanced_hide_keyword_content_phrases_summary">"الكلمات والعبارات التي تريد إخفاءها، مفصولة بسطور جديدة
يمكن أن تكون الكلمات عبارة عن أسماء قنوات أو أي نص يظهر في عناوين الفيديوهات
يجب إدخال الكلمات التي تحتوي على أحرف كبيرة في المنتصف بنفس الحالة (على سبيل المثال: iPhone, TikTok, LeBlanc)"</string>
<string name="revanced_hide_keyword_content_about_title">حول تصفية الكلمات المفتاحية</string>
<string name="revanced_hide_keyword_content_about_summary">الصفحة الرئيسية/الاشتراكات/نتائج الإشتراك/يتم تصفية نتائج البحث لإخفاء المحتوى الذي يتطابق مع عبارات الكلمات الرئيسية\n\nالقيود\n• لا يمكن إخفاء فيديوهات Shorts بواسطة اسم القناة\n• قد لا تكون بعض مكونات واجهة المستخدم مخفية\n• قد لا تظهر نتائج بحث عن كلمة رئيسية</string>
<string name="revanced_hide_keyword_content_about_summary">"الصفحة الرئيسية/الاشتراكات/نتائج البحث يتم تصفيتها لإخفاء المحتوى الذي يتطابق مع كلمات البحث
القيود
• فيديوهات Shorts لا يمكن إخفاؤها بواسطة اسم القناة
• قد لا يتم إخفاء بعض مكونات واجهة المستخدم
• البحث عن كلمة ما قد لا يعطي نتائج"</string>
<string name="revanced_hide_keyword_content_about_whole_words_title">مطابقة الكلمات بأكملها</string>
<!-- Translations _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
<string name="revanced_hide_keyword_content_about_whole_words_summary">سيؤدي وضع علامة اقتباس مزدوجة حول كلمة رئيسية/عبارة إلى منع التطابقات الجزئية لعناوين الفيديو وأسماء القنوات.&lt;br&gt;&lt;br&gt;على سبيل المثال،&lt;br&gt;&lt;b&gt;\"ai\"&lt;/b&gt; سيخفي الفيديو: &lt;b&gt;How does AI work?&lt;/b&gt;&lt;br&gt;ولكن لن يخفي: &lt;b&gt;What does fair use mean?&lt;/b&gt;</string>
@@ -316,7 +332,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_general_ads_summary_on">تم إخفاء الإعلانات بشكل عام</string>
<string name="revanced_hide_general_ads_summary_off">يتم عرض الإعلانات العامة</string>
<string name="revanced_hide_fullscreen_ads_title">إخفاء إعلانات ملء الشاشة</string>
<string name="revanced_hide_fullscreen_ads_summary_on">يتم إخفاء إعلانات ملء الشاشة\n\nهذه الميزة متوفرة فقط للأجهزة القديمة</string>
<string name="revanced_hide_fullscreen_ads_summary_on">"يتم إخفاء إعلانات ملء الشاشة
هذه الميزة متاحة فقط للأجهزة القديمة"</string>
<string name="revanced_hide_fullscreen_ads_summary_off">يتم عرض إعلانات ملء الشاشة</string>
<string name="revanced_hide_buttoned_ads_title">إخفاء الإعلانات الزرية</string>
<string name="revanced_hide_buttoned_ads_summary_on">تم إخفاء الإعلانات الزرية</string>
@@ -416,7 +434,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_swipe_save_and_restore_brightness_summary_on">حفظ واستعادة السطوع عند الخروج أو الدخول إلى وضع ملء الشاشة</string>
<string name="revanced_swipe_save_and_restore_brightness_summary_off">لا تقم بحفظ السطوع واستعادته عند الخروج أو الدخول إلى وضع ملء الشاشة</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_title">تمكين إيماءة السطوع التلقائي</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">يؤدي التمرير لأسفل إلى أدنى قيمة لإيماءة السطوع إلى تمكين السطوع التلقائي</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_on">التمرير لأسفل إلى أدنى قيمة للسطوع يمكّن السطوع التلقائي</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_summary_off">لا يؤدي التمرير لأسفل إلى أدنى قيمة إلى تمكين السطوع التلقائي</string>
<string name="revanced_swipe_lowest_value_enable_auto_brightness_overlay_text">تلقائي</string>
<string name="revanced_swipe_overlay_timeout_title">مهلة واجهة التمرير</string>
@@ -435,7 +453,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.buttons.action.hideButtonsPatch">
<string name="revanced_hide_buttons_screen_title">أزرار الإجراء</string>
<string name="revanced_hide_buttons_screen_summary">إخفاء أو عرض الأزرار تحت مقاطع الفيديو</string>
<string name="revanced_hide_buttons_screen_summary">إخفاء أو عرض الأزرار تحت الفيديوهات</string>
<string name="revanced_hide_like_dislike_button_title">إخفاء أعجبني ولم يعجبني</string>
<string name="revanced_hide_like_dislike_button_summary_on">تم إخفاء أزرار أعجبني ولم يعجبني</string>
<string name="revanced_hide_like_dislike_button_summary_off">يتم عرض أزرار أعجبني ولم يعجبني</string>
@@ -490,11 +508,22 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_subscriptions_button_summary_off">يتم عرض زر الاشتراكات</string>
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_switch_create_with_notifications_button_title">تبديل الإنشاء مع الإشعارات</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">تم تبديل زر الإنشاء بـزر الإشعارات\n\nملاحظة: يؤدي تمكين هذا أيضًا إلى إخفاء إعلانات الفيديو بالقوة</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"تم تبديل زر الإنشاء بـزر الإشعارات
ملاحظة: يؤدي تمكين هذا أيضًا إلى إخفاء إعلانات الفيديو بالقوة"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">لا يتم تبديل زر الإنشاء بزر الإشعارات</string>
<string name="revanced_hide_navigation_button_labels_title">إخفاء تسميات زر التنقل</string>
<string name="revanced_hide_navigation_button_labels_summary_on">تم إخفاء التسميات</string>
<string name="revanced_hide_navigation_button_labels_summary_off">يتم عرض التسميات</string>
<string name="revanced_disable_translucent_status_bar_title">تعطيل شريط الحالة الشفاف</string>
<string name="revanced_disable_translucent_status_bar_summary_on">شريط الحالة غير معتمة</string>
<string name="revanced_disable_translucent_status_bar_summary_off">شريط الحالة غير شفافة أو عميقة</string>
<string name="revanced_disable_translucent_navigation_bar_light_title">تعطيل شريط التنقل الشفاف الفاتح</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">شريط التنقل في الوضع الفاتح معتم</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">يكون شريط التنقل في الوضع الفاتح معتمًا أو نصف شفاف</string>
<string name="revanced_disable_translucent_navigation_bar_dark_title">تعطيل الشريط الداكنة</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_on">شريط التنقل في الوضع الداكن معتم</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_off">شريط التطبيق المصوري الشفاف غير عميقة أو عميقة</string>
</patch>
<patch id="layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch">
<string name="revanced_hide_player_flyout_title">القائمة المنبثقة</string>
@@ -617,8 +646,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_shorts_subscribe_button_summary_on">تم إخفاء زر الاشتراك</string>
<string name="revanced_hide_shorts_subscribe_button_summary_off">يتم عرض زر الاشتراك</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_title">إخفاء أزرار واجهة التوقف</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_on">تم إخفاء أزرار تراكب التوقف</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_off">يتم عرض أزرار تراكب التوقف</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_on">تم إخفاء أزرار واجهة التوقف</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_off">يتم عرض أزرار واجهة التوقف</string>
<string name="revanced_hide_shorts_shop_button_title">إخفاء زر المتجر</string>
<string name="revanced_hide_shorts_shop_button_summary_on">تم إخفاء زر المتجر</string>
<string name="revanced_hide_shorts_shop_button_summary_off">يتم عرض زر المتجر</string>
@@ -652,9 +681,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_shorts_stickers_title">إخفاء الملصقات</string>
<string name="revanced_hide_shorts_stickers_summary_on">تم إخفاء الملصقات</string>
<string name="revanced_hide_shorts_stickers_summary_off">يتم عرض الملصقات</string>
<string name="revanced_hide_shorts_like_fountain_title">إخفاء نافورة أعجبني</string>
<string name="revanced_hide_shorts_like_fountain_summary_on">تم إخفاء تأثير النافورة لـزر أعجبني</string>
<string name="revanced_hide_shorts_like_fountain_summary_off">يتم عرض تأثير النافورة لـزر أعجبني</string>
<string name="revanced_hide_shorts_like_fountain_title">إخفاء فوران أعجبني</string>
<string name="revanced_hide_shorts_like_fountain_summary_on">تم إخفاء التأثير الفوّار لـزر أعجبني</string>
<string name="revanced_hide_shorts_like_fountain_summary_off">يتم عرض التأثير الفوّار لـزر أعجبني</string>
<string name="revanced_hide_shorts_like_button_title">إخفاء زر أعجبني</string>
<string name="revanced_hide_shorts_like_button_summary_on">تم إخفاء زر أعجبني</string>
<string name="revanced_hide_shorts_like_button_summary_off">يتم عرض زر أعجبني</string>
@@ -683,7 +712,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_shorts_video_title_summary_off">يتم عرض العنوان</string>
<string name="revanced_hide_shorts_sound_metadata_label_title">إخفاء تسمية بيانات التعريف الصوتية</string>
<string name="revanced_hide_shorts_sound_metadata_label_summary_on">تم إخفاء تسمية بيانات التعريف</string>
<string name="revanced_hide_shorts_sound_metadata_label_summary_off">يتم عرض تسمية البيانات الوصفية</string>
<string name="revanced_hide_shorts_sound_metadata_label_summary_off">يتم عرض تسمية بيانات التعريف</string>
<string name="revanced_hide_shorts_full_video_link_label_title">إخفاء تسمية رابط الفيديو الكامل</string>
<string name="revanced_hide_shorts_full_video_link_label_summary_on">تم إخفاء تسمية رابط الفيديو</string>
<string name="revanced_hide_shorts_full_video_link_label_summary_off">يتم عرض تسمية رابط الفيديو</string>
@@ -709,8 +738,13 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_player_popup_panels_summary_on">تم إخفاء لوحات المشغل المنبثقة</string>
<string name="revanced_hide_player_popup_panels_summary_off">يتم عرض لوحات المشغل المنبثقة</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">فتح مقاطع الفيديو في ملء الشاشة</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">يتم فتح الفيديوهات في وضع ملء الشاشة</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_off">لا يتم فتح الفيديوهات في وضع ملء الشاشة</string>
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
<string name="revanced_player_overlay_opacity_title">شفافية تراكب المشغل</string>
<string name="revanced_player_overlay_opacity_title">شفافية واجهة المشغل</string>
<string name="revanced_player_overlay_opacity_summary">قيمة الشفافية بين 0-100، حيث يكون 0 شفاف</string>
<string name="revanced_player_overlay_opacity_invalid_toast">شفافية واجهة المشغل يجب أن تكون بين 0-100</string>
</patch>
@@ -726,7 +760,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_ryd_enable_summary_off">لا يتم عرض لم يعجبني</string>
<string name="revanced_ryd_shorts_title">عرض لم يعجني في مقاطع Shorts</string>
<string name="revanced_ryd_shorts_summary_on">يتم عرض لم يعجني في مقاطع Shorts</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">إبداءات لم يعجبني التي تظهر على فيديوهات Shorts\n\nالتقييد: قد لا تظهر إبداءات لم يعجبني في وضع التصفح المتخفي</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">"إبداءات لم يعجبني التي تظهر على فيديوهات Shorts
التقييد: قد لا تظهر إبداءات لم يعجبني في وضع التصفح المتخفي"</string>
<string name="revanced_ryd_shorts_summary_off">تم إخفاء لم يعجني في مقاطع Shorts</string>
<string name="revanced_ryd_dislike_percentage_title">لم يعجبني كــ نسبة مئوية</string>
<string name="revanced_ryd_dislike_percentage_summary_on">يعرض عدد لم يعجبني كـ نسبة مئوية</string>
@@ -751,7 +787,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_ryd_statistics_getFetchCallCount_zero_summary">لم يتم إجراء اتصالات الشبكة</string>
<string name="revanced_ryd_statistics_getFetchCallCount_non_zero_summary">%d اتصالات شبكية أجريت</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_title">API جلب الأصوات، عدد المهلات</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_zero_summary">لم تنتهي مهلة المكالمات الشبكية</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_zero_summary">لم تنتهي مهلة الاتصالات الشبكية</string>
<string name="revanced_ryd_statistics_getFetchCallNumberOfFailures_non_zero_summary">اتصالات شبكية انتهت مهلتها %d</string>
<string name="revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_title">حدود معدل عميل API</string>
<string name="revanced_ryd_statistics_getNumberOfRateLimitRequestsEncountered_zero_summary">لا توجد حدود لمعدل العميل</string>
@@ -769,13 +805,17 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_thumbnails_high_quality_summary_off">مصغرات شريط التقدم متوسطة الجودة</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_on">مصغرات شريط التقدم بملء الشاشة عالية الجودة</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_off">مصغرات شريط التقدم بملء الشاشة متوسطة الجودة</string>
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">سيؤدي هذا أيضا إلى استعادة المصغرات على البث المباشر الذي لا يحتوي على مصغرات شريط التقدم.\n\nمصغرات شريط التقدم سوف تستخدم نفس جودة الفيديو الحالي.\n\nتعمل هذه الميزة بشكل أفضل مع جودة فيديو 720p أو أقل وعند استخدام اتصال إنترنت سريع جداً.</string>
<string name="revanced_restore_old_seekbar_thumbnails_title">استعادة مصغرات شريط التقدم القديم</string>
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"سيؤدي هذا أيضا إلى استعادة المصغرات على البث المباشر الذي لا يحتوي على مصغرات شريط التقدم.
مصغرات شريط التقدم سوف تستخدم نفس جودة الفيديو الحالي.
تعمل هذه الميزة بشكل أفضل مع جودة فيديو 720p أو أقل وعند استخدام اتصال إنترنت سريع جداً."</string>
<string name="revanced_restore_old_seekbar_thumbnails_title">استعادة مصغرات شريط التقدم القديمة</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_on">مصغرات شريط التقدم ستظهر فوق شريط تقدم الفيديو</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_off">مصغرات شريط التقدم ستظهر في ملء الشاشة</string>
</patch>
<patch id="layout.sponsorblock.sponsorBlockResourcePatch">
<string name="revanced_sb_enable_sb">تمكين مانِع الرُعَاة SponsorBlock</string>
<string name="revanced_sb_enable_sb">تمكين SponsorBlock</string>
<string name="revanced_sb_enable_sb_sum">مانِع الرُعَاة هو نظام جماعي لتخطي الأجزاء المُمِلَّة في مقاطع YouTube</string>
<string name="revanced_sb_appearance_category">المظهر</string>
<string name="revanced_sb_enable_voting">عرض زر التصويت</string>
@@ -832,12 +872,15 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_settings_import_successful">تم استيراد الإعدادات بنجاح</string>
<string name="revanced_sb_settings_import_failed">فشل استيراد: %s</string>
<string name="revanced_sb_settings_export_failed">فشل تصدير: %s</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning">تحتوي إعداداتك على معرف مستخدم خاص لـ SponsorBlock.\n\n معرف المستخدم الخاص بك يشبه كلمة المرور ويجب عدم مشاركته أبدًا.\n</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning">"تحتوي إعداداتك على معرف مستخدم خاص لـ SponsorBlock.
معرف المستخدم الخاص بك يشبه كلمة المرور ويجب عدم مشاركته أبدًا.
"</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning_dismiss">لا تعرض مرة أخرى</string>
<string name="revanced_sb_diff_segments">تغيير سلوك المقطع</string>
<string name="revanced_sb_segments_sponsor">الراعي</string>
<string name="revanced_sb_segments_sponsor_sum">الترويج المدفوع الأجر، والإحالات المدفوعة الأجر والإعلانات المباشرة. ليس للترويج الذاتي أو لصراعات مجانية للقضايا/المبدعين/المواقع الإلكترونية/المنتجات التي يحبون الحصول عليها</string>
<string name="revanced_sb_segments_selfpromo">ترويج شخصي / غير مدفوع الأجر</string>
<string name="revanced_sb_segments_selfpromo">ترويج شخصي/غير مدفوع الأجر</string>
<string name="revanced_sb_segments_selfpromo_sum">شبيهة بـ \"الراعي\" باستثناء ما يتعلق بالإعلانات غير المدفوعة الأجر أو الذاتية. ويشمل ذلك أقسام عن السلع أو التبرعات أو المعلومات المتعلقة بمن تعاونوا مع ناشر المحتوى</string>
<string name="revanced_sb_segments_interaction">تذكير بالتفاعل (اشتراك)</string>
<string name="revanced_sb_segments_interaction_sum">تذكير قصير للإعجاب أو الاشتراك أو المتابعة في منتصف المحتوى. إذا كانت طويلة أو تتعلق بشيء محدد، فيجب أن تكون خاضعة للترويج الشخصي بدلاً من ذلك</string>
@@ -892,9 +935,10 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_submit_failed_invalid">غير قادر على إرسال المقطع: الحالة: %s</string>
<string name="revanced_sb_submit_failed_timeout">SponsorBlock متوقف مؤقتًا</string>
<string name="revanced_sb_submit_failed_unknown_error">غير قادر على إرسال المقطع (الحالة: %1$d %2$s)</string>
<string name="revanced_sb_submit_failed_rate_limit">لا يمكن إرسال المقطع.\n جارٍ الحد من معدل إرسالك (عدد كبير جدا من نفس المستخدم أو IP)</string>
<string name="revanced_sb_submit_failed_rate_limit">لا يمكن إرسال المقطع. جارٍ الحد من معدل إرسالك (عدد كبير جدًا من نفس المستخدم أو IP)</string>
<string name="revanced_sb_submit_failed_forbidden">لا يمكن إرسال المقطع: %s</string>
<string name="revanced_sb_submit_failed_duplicate">لا يمكن إرسال هذا المقطع.\nموجود بالفعل</string>
<string name="revanced_sb_submit_failed_duplicate">"لا يمكن إرسال هذا المقطع.
موجود بالفعل"</string>
<string name="revanced_sb_submit_succeeded">تم إرسال المقطع بنجاح</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock غير متاح مؤقتًا (انتهت مهلة API)</string>
@@ -909,7 +953,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_vote_no_segments">لا توجد مقاطع للتصويت عليها</string>
<string name="revanced_sb_new_segment_choose_category">اختيار فئة المقطع</string>
<string name="revanced_sb_new_segment_disabled_category">الفئة معطلة في الإعدادات. تمكين الفئة للإرسال.</string>
<string name="revanced_sb_new_segment_title">مقطع مانِع رُعَاة جديد</string>
<string name="revanced_sb_new_segment_title">مقطع SponsorBlock جديد</string>
<string name="revanced_sb_new_segment_mark_time_as_question">تعيين %s كبداية أو نهاية لمقطع جديد؟</string>
<string name="revanced_sb_new_segment_mark_start">البداية</string>
<string name="revanced_sb_new_segment_mark_end">النهاية</string>
@@ -917,7 +961,15 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_time_start">الوقت الذي يبدأ عنده المقطع</string>
<string name="revanced_sb_new_segment_time_end">الوقت الذي ينتهي عنده المقطع</string>
<string name="revanced_sb_new_segment_confirm_title">هل الأوقات صحيحة؟</string>
<string name="revanced_sb_new_segment_confirm_content">المقطع من\n\n%1$s\nto\n%2$s\n\n(%3$s)\n\nReady to جاهز للإرسال؟</string>
<string name="revanced_sb_new_segment_confirm_content">"المقطع من
%1$s
إلى
%2$s
(%3$s)
هل أنت مستعد للإرسال؟"</string>
<string name="revanced_sb_new_segment_start_is_before_end">يجب أن تكون البداية قبل النهاية</string>
<string name="revanced_sb_new_segment_mark_locations_first">ضع علامة على موقعين في شريط الوقت أولًا</string>
<string name="revanced_sb_new_segment_preview_segment_first">معاينة المقطع، والتأكد من تخطيه بسلاسة</string>
@@ -959,7 +1011,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_app_version_title">خِداع إصدار التطبيق</string>
<string name="revanced_spoof_app_version_summary_on">تم تغيير اصدار التطبيق</string>
<string name="revanced_spoof_app_version_summary_off">لم يتم تغيير اصدار التطبيق</string>
<string name="revanced_spoof_app_version_user_dialog_message">سيتم تغيير إصدار التطبيق إلى إصدار قديم من YouTube.\n\nسيؤدي هذا إلى تغيير مظهر ومميزات التطبيق، ولكن قد تحدث تأثيرات جانبية غير معروفة.\n\nإذا تم إيقاف تشغيله لاحقا، من المستحسن مسح بيانات التطبيق لمنع حدوث أخطاء في واجهة المستخدم.</string>
<string name="revanced_spoof_app_version_user_dialog_message">"سيتم تغيير إصدار التطبيق إلى إصدار قديم من YouTube.
سيؤدي هذا إلى تغيير مظهر ومميزات التطبيق، ولكن قد تحدث تأثيرات جانبية غير معروفة.
إذا تم إيقاف تشغيله لاحقًا، من المستحسن مسح بيانات التطبيق لمنع حدوث أخطاء في واجهة المستخدم."</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>
@@ -1012,7 +1068,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_miniplayer_screen_summary">تغيير نمط المشغل المصغر داخل التطبيق</string>
<string name="revanced_miniplayer_type_title">نوع المشغل المصغر</string>
<string name="revanced_miniplayer_type_entry_0">معطّل</string>
<string name="revanced_miniplayer_type_entry_1">الأصلي</string>
<string name="revanced_miniplayer_type_entry_1">إفتراضي</string>
<string name="revanced_miniplayer_type_entry_2">الحد الأدنى</string>
<string name="revanced_miniplayer_type_entry_3">الجهاز اللوحي</string>
<string name="revanced_miniplayer_type_entry_4">حديث 1</string>
@@ -1022,19 +1078,28 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_miniplayer_rounded_corners_summary_on">الزوايا مستديرة</string>
<string name="revanced_miniplayer_rounded_corners_summary_off">الزوايا مربعة</string>
<string name="revanced_miniplayer_double_tap_action_title">تمكين النقر المزدوج والضغط لتغيير الحجم</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">تم تمكين إجراء الضغط المزدوج والضغط لتغيير الحجم\n\n• النقر المزدوج لزيادة حجم المشغل المصغر\n• النقر المزدوج مرة أخرى لاستعادة الحجم الأصلي</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">"تم تمكين إجراء الضغط المزدوج والضغط لتغيير الحجم
• النقر المزدوج لزيادة حجم المشغل المصغر
• النقر المزدوج مرة أخرى لاستعادة الحجم الأصلي"</string>
<string name="revanced_miniplayer_double_tap_action_summary_off">إجراء النقر المزدوج والضغط لتغيير الحجم معطل</string>
<string name="revanced_miniplayer_drag_and_drop_title">تمكين السحب والإفلات</string>
<string name="revanced_miniplayer_drag_and_drop_summary_on">السحب والإفلات مفعلان\n\nيمكن سحب المشغل المصغر إلى أي زاوية من الشاشة</string>
<string name="revanced_miniplayer_drag_and_drop_summary_on">"السحب والإفلات مفعلان
يمكن سحب المشغل المصغر إلى أي زاوية من الشاشة"</string>
<string name="revanced_miniplayer_drag_and_drop_summary_off">تم تعطيل السحب والإفلات</string>
<string name="revanced_miniplayer_horizontal_drag_title">تمكين إيماءة السحب الأفقية</string>
<string name="revanced_miniplayer_horizontal_drag_summary_on">تم تمكين إيماءة السحب الأفقية\n\nيمكن سحب المشغل المصغر خارج الشاشة إلى اليسار أو اليمين</string>
<string name="revanced_miniplayer_horizontal_drag_summary_on">"تم تمكين إيماءة السحب الأفقية
يمكن سحب المشغل المصغر خارج الشاشة إلى اليسار أو اليمين"</string>
<string name="revanced_miniplayer_horizontal_drag_summary_off">تم تعطيل إيماءة السحب الأفقية</string>
<string name="revanced_miniplayer_hide_expand_close_title">إخفاء زر الإغلاق</string>
<string name="revanced_miniplayer_hide_expand_close_summary_on">تم إخفاء زر الإغلاق</string>
<string name="revanced_miniplayer_hide_expand_close_summary_off">يتم عرض زر الإغلاق</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_title">إخفاء أزرار التوسيع والإغلاق</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_on">تم إخفاء الأزرار\n\nمرر للتوسيع أو الإغلاق</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_on">"تم إخفاء الأزرار
مرر للتوسيع أو الإغلاق"</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_off">يتم عرض أزرار التوسيع والإغلاق</string>
<string name="revanced_miniplayer_hide_subtext_title">إخفاء النصوص الفرعية</string>
<string name="revanced_miniplayer_hide_subtext_summary_on">تم إخفاء النصوص الفرعية</string>
@@ -1065,7 +1130,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
<string name="revanced_bypass_image_region_restrictions_title">تجاوز قيود منطقة الصورة</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">استخدام مضيف الصورة yt4.ggpht.com</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">استخدام مضيف الصور الأصلي\n\nتمكين هذا يمكن إصلاح الصور المفقودة التي يتم حظرها في بعض المناطق</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">"استخدام مضيف الصور الأصلي
تمكين هذا يمكن إصلاح الصور المفقودة التي يتم حظرها في بعض المناطق"</string>
</patch>
<patch id="layout.thumbnails.alternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
@@ -1080,12 +1147,16 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow &amp; المصّغرات الأصلية</string>
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow &amp; اللقطات الثابتة</string>
<string name="revanced_alt_thumbnail_options_entry_4">اللقطات الثابتة</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">يوف DeArrow مصغرات فيديو من مصادر جماعية لفيديوهات YouTube. هذه المصغرات غالبا ما تكون أكثر صلة من تلك المقدمة من YouTube\n\nإذا تم تفعيلها، سيتم إرسال روابط الفيديو إلى خادم API ولن يتم إرسال أي بيانات أخرى. إذا كان الفيديو لا يحتوي على مصغرات DArrow ، سيتم عرض المقاطع الأصلية أو اللقطات الثابتة\n\nاضغط هنا لمعرفة المزيد عن DArrow</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">"يوفر DeArrow مصغرات فيديو من مصادر جماعية لفيديوهات YouTube. هذه المصغرات غالبا ما تكون أكثر صلة من تلك المقدمة من YouTube
إذا تم تفعيلها، سيتم إرسال روابط الفيديو إلى خادم API ولن يتم إرسال أي بيانات أخرى. إذا كان الفيديو لا يحتوي على مصغرات DArrow، سيتم عرض المقاطع الأصلية أو اللقطات الثابتة
اضغط هنا لمعرفة المزيد عن DArrow"</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">عرض ملاحظة إذا كان API غير متاح</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">يتم عرض ملاحظة إذا كان DeArrow غير متوفر</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">لا يتم عرض ملاحظة إذا كان DeArrow غير متوفر</string>
<string name="revanced_alt_thumbnail_dearrow_api_url_title">DeArrow API endpoint</string>
<string name="revanced_alt_thumbnail_dearrow_api_url_summary">The URL of the DeArrow thumbnail cache endpointThe URL of the DeArrow thumbnail cache endpoint</string>
<string name="revanced_alt_thumbnail_dearrow_api_url_summary">The URL of the DeArrow thumbnail cache endpoint</string>
<string name="revanced_alt_thumbnail_stills_about_title">لقطات الفيديو الثابتة</string>
<string name="revanced_alt_thumbnail_stills_about_summary">يتم التقاط اللقطات الثابتة من بداية/وسط/نهاية كل فيديو. هذه الصور مدمجة في YouTube ولا يتم استخدام أي واجهة برمجة تطبيقات خارجية</string>
<string name="revanced_alt_thumbnail_stills_fast_title">استخدم اللقطات الثابتة السريعة</string>
@@ -1119,8 +1190,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
<string name="revanced_spoof_device_dimensions_title">محاكاة أبعاد الجهاز</string>
<string name="revanced_spoof_device_dimensions_summary_on">تم محاكاة أبعاد الجهاز\n\nقد يتم فتح قفل جودة الفيديو العالية ولكن قد تواجه تقطعًا في تشغيل الفيديو وعمر بطارية أسوأ وتأثيرات جانبية غير معروفة</string>
<string name="revanced_spoof_device_dimensions_summary_off">أبعاد الجهاز غير محاكاة\n\nيمكن أن يؤدي تفعيل هذا إلى فتح جودة أعلى للفيديو</string>
<string name="revanced_spoof_device_dimensions_summary_on">"تم محاكاة أبعاد الجهاز
قد يتم فتح قفل جودة الفيديو العالية ولكن قد تواجه تقطعًا في تشغيل الفيديو وعمر بطارية أسوأ وتأثيرات جانبية غير معروفة"</string>
<string name="revanced_spoof_device_dimensions_summary_off">"أبعاد الجهاز غير محاكاة
يمكن أن يؤدي تفعيل هذا إلى فتح جودة أعلى للفيديو"</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">قد يؤدي تمكين هذا إلى تباطؤ تشغيل الفيديو وتدهور عمر البطارية وآثار جانبية غير معروفة.</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
@@ -1147,6 +1222,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_disable_zoom_haptics_summary_on">تم تعطيل الاهتزاز</string>
<string name="revanced_disable_zoom_haptics_summary_off">تم تمكين الاهتزاز</string>
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">فرض الصوت الأصلي</string>
<string name="revanced_force_original_audio_summary_on">استخدام الصوت الأصلي</string>
<string name="revanced_force_original_audio_summary_off">استخدام الصوت الافتراضي</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
<string name="revanced_video_quality_default_entry_1">تلقائي</string>
@@ -1170,13 +1250,13 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_custom_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>
<string name="revanced_custom_playback_speeds_parse_exception">سرعة تشغيل مخصصة غير صالحة. استخدام القيم الافتراضية.</string>
<string name="revanced_custom_playback_speeds_invalid">يجب أن تكون سرعات التشغيل المخصصة أقل من %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">سرعة التشغيل المخصصة غير صالحة</string>
<string name="revanced_custom_playback_speeds_auto">تلقائي</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
<string name="revanced_remember_playback_speed_last_selected_title">تذكر التغيرات في سرعة التشغيل</string>
<string name="revanced_remember_playback_speed_last_selected_summary_on">تطبيق تغييرات سرعة التشغيل على جميع مقاطع الفيديو</string>
<string name="revanced_remember_playback_speed_last_selected_summary_on">تطبيق تغييرات سرعة التشغيل على جميع الفيديوهات</string>
<string name="revanced_remember_playback_speed_last_selected_summary_off">تطبيق تغييرات سرعة التشغيل فقط على الفيديو الحالي</string>
<string name="revanced_playback_speed_default_title">سرعة التشغيل الافتراضية</string>
<string name="revanced_remember_playback_speed_toast">تغيير السرعة الافتراضية إلى: %s</string>
@@ -1187,27 +1267,87 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_restore_old_video_quality_menu_summary_off">لا يتم عرض قائمة جودة الفيديو القديمة</string>
</patch>
<patch id="interaction.seekbar.enableSlideToSeekPatch">
<string name="revanced_slide_to_seek_title">تمكين Slide to Seek</string>
<string name="revanced_slide_to_seek_summary_on">تم تمكين Slide to Seek</string>
<string name="revanced_slide_to_seek_summary_off">تم تعطيل Slide to Seek</string>
<string name="revanced_slide_to_seek_title">تمكين التمرير للتقديم أو الترجيع</string>
<string name="revanced_slide_to_seek_summary_on">تم تمكين التمرير للتقديم أو الترجيع</string>
<string name="revanced_slide_to_seek_summary_off">تم تعطيل التمرير للتقديم أو الترجيع</string>
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
<string name="revanced_spoof_video_streams_screen_title">Spoof Video Streams</string>
<string name="revanced_spoof_video_streams_screen_summary">تزييف تدفقات الفيديو الخاصة بالعميل لمنع حدوث مشكلات أثناء التشغيل</string>
<string name="revanced_spoof_video_streams_title">Spoof Video Streams</string>
<string name="revanced_spoof_video_streams_summary_on">يتم تزييف تدفقات الفيديو</string>
<string name="revanced_spoof_video_streams_summary_off">لا يتم تزييف تدفقات الفيديو\n\nقد لا يعمل تشغيل الفيديو</string>
<string name="revanced_spoof_video_streams_summary_off">"لا يتم تزييف تدفقات الفيديو
قد لا يعمل تشغيل الفيديو"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">إيقاف تشغيل هذا الإعداد قد يسبب مشاكل في تشغيل الفيديو.</string>
<string name="revanced_spoof_video_streams_client_title">العميل الافتراضي</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">فرض AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">ترميز الفيديو هو AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">ترميز الفيديو هو VP9 أو AV1</string>
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">لا يحتوي جهازك على فك تشفير الأجهزة VP9، وهذا الإعداد يعمل دائما عند تمكين تزييف العميل</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">قد يؤدي تمكين هذا إلى تحسين عمر البطارية وإصلاح مشكلة تقطيع التشغيل.\n\nيتمتع تنسيق AVC بدقة قصوى تبلغ 1080P، وسيستخدم تشغيل الفيديو المزيد من بيانات الإنترنت مقارنةً بتنسيق VP9 أو AV1.</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">تم فرض ترميز الفيديو على AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">يتم تحديد ترميز الفيديو تلقائيًا</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"قد يؤدي تمكين هذا إلى تحسين عمر البطارية وإصلاح مشكلة تقطيع التشغيل.
يتمتع تنسيق AVC بدقة قصوى تبلغ 1080P، برنامج ترميز الصوت Opus غير متوفر، وسيستخدم تشغيل الفيديو المزيد من بيانات الإنترنت مقارنةً بتنسيق VP9 أو AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_title">التأثيرات الجانبية لمحاكاة iOS</string>
<string name="revanced_spoof_video_streams_about_ios_summary"> قد لا يتم تشغيل فيديوهات الأطفال الخاصة\n• تبدأ البثوث المباشر من البداية\n• قد تنتهي الفيديوهات قبل النهاية بثانية واحدة\n• لا يوجد ترميز الصوت opus</string>
<string name="revanced_spoof_video_streams_about_ios_summary">"• لا يمكن تشغيل مقاطع فيديو الأطفال الخاصة.
• تنتهي مقاطع الفيديو مبكرًا بمقدار 1 ثانية."</string>
<string name="revanced_spoof_video_streams_about_android_vr_title">التأثيرات الجانبية لمحاكاة Android VR</string>
<string name="revanced_spoof_video_streams_about_android_vr_summary">• قد لا يتم تشغيل فيديوهات الأطفال\n• قائمة المقطع الصوتي مفقودة\n• مستوى الصوت الثابت غير متوفر</string>
<string name="revanced_spoof_video_streams_about_android_vr_summary">"• قد لا يتم تشغيل فيديوهات الأطفال
• تبدأ البثوث المباشرة من البداية
• قد تنتهي الفيديوهات قبل النهاية بثانية واحدة"</string>
<string name="revanced_spoof_video_streams_language_title">لغة البث الصوتي الافتراضية</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">لغة التطبيق</string>
<string name="revanced_spoof_video_streams_language_AR">العربية</string>
<string name="revanced_spoof_video_streams_language_AZ">Azerbaijani</string>
<string name="revanced_spoof_video_streams_language_BG">Bulgarian</string>
<string name="revanced_spoof_video_streams_language_BN">Bengali</string>
<string name="revanced_spoof_video_streams_language_CA">Catalan</string>
<string name="revanced_spoof_video_streams_language_CS">Czech</string>
<string name="revanced_spoof_video_streams_language_DA">Danish</string>
<string name="revanced_spoof_video_streams_language_DE">German</string>
<string name="revanced_spoof_video_streams_language_EL">Greek</string>
<string name="revanced_spoof_video_streams_language_EN">English</string>
<string name="revanced_spoof_video_streams_language_ES">Spanish</string>
<string name="revanced_spoof_video_streams_language_ET">Estonian</string>
<string name="revanced_spoof_video_streams_language_FA">فارسى</string>
<string name="revanced_spoof_video_streams_language_FI">Finnish</string>
<string name="revanced_spoof_video_streams_language_FR">French</string>
<string name="revanced_spoof_video_streams_language_GU">Gujarati</string>
<string name="revanced_spoof_video_streams_language_HI">Hindi</string>
<string name="revanced_spoof_video_streams_language_HR">Croatian</string>
<string name="revanced_spoof_video_streams_language_HU">Hungarian</string>
<string name="revanced_spoof_video_streams_language_ID"> Indonesian</string>
<string name="revanced_spoof_video_streams_language_IT">Italian</string>
<string name="revanced_spoof_video_streams_language_JA">Japanese</string>
<string name="revanced_spoof_video_streams_language_KK">Kazakh</string>
<string name="revanced_spoof_video_streams_language_KO">Korean</string>
<string name="revanced_spoof_video_streams_language_LT">Lithuanian</string>
<string name="revanced_spoof_video_streams_language_LV">Latvian</string>
<string name="revanced_spoof_video_streams_language_MK">Macedonian</string>
<string name="revanced_spoof_video_streams_language_MN">Mongolian</string>
<string name="revanced_spoof_video_streams_language_MR">Marathi</string>
<string name="revanced_spoof_video_streams_language_MS">Malay</string>
<string name="revanced_spoof_video_streams_language_MY">Burmese</string>
<string name="revanced_spoof_video_streams_language_NL">Dutch</string>
<string name="revanced_spoof_video_streams_language_OR">Odia</string>
<string name="revanced_spoof_video_streams_language_PA">Punjabi</string>
<string name="revanced_spoof_video_streams_language_PL">Polish</string>
<string name="revanced_spoof_video_streams_language_PT_BR">Portuguese (Brazil)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">Portuguese (Portugal)</string>
<string name="revanced_spoof_video_streams_language_RO">Romanian</string>
<string name="revanced_spoof_video_streams_language_RU">Russian</string>
<string name="revanced_spoof_video_streams_language_SK">Slovak</string>
<string name="revanced_spoof_video_streams_language_SL">Slovene</string>
<string name="revanced_spoof_video_streams_language_SR">Serbian</string>
<string name="revanced_spoof_video_streams_language_SV">Swedish</string>
<string name="revanced_spoof_video_streams_language_SW">Swahili</string>
<string name="revanced_spoof_video_streams_language_TA">Tamil</string>
<string name="revanced_spoof_video_streams_language_TE">Telugu</string>
<string name="revanced_spoof_video_streams_language_TH">Thai</string>
<string name="revanced_spoof_video_streams_language_TR">Turkish</string>
<string name="revanced_spoof_video_streams_language_UK">Ukrainian</string>
<string name="revanced_spoof_video_streams_language_UR">Urdu</string>
<string name="revanced_spoof_video_streams_language_VI">Vietnamese</string>
<string name="revanced_spoof_video_streams_language_ZH">Chinese</string>
</patch>
</app>
<app id="twitch">

View File

@@ -1,34 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
Raw strings are required because Crowdin AI translations regularly gets confused and
replace \n with an encoded new line character.
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Bad:
<string name="summary_key">First \'item\' text\nSecond \"item\" text</string>
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
Good:
<string name="summary_key">"First 'item' text
Second \"item\" text"</string>
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
@@ -147,6 +136,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="layout.panels.popup.playerPopupPanelsPatch">
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
</patch>
<patch id="layout.returnyoutubedislike.returnYouTubeDislikePatch">
@@ -192,8 +183,10 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
</patch>
<patch id="misc.announcements.announcementsPatch">
<string name="revanced_announcements_dialog_dismiss">খাৰিজ কৰক</string>
</patch>
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
<string name="revanced_check_watch_history_domain_name_dialog_title">সকীয়নি</string>
</patch>
<patch id="misc.autorepeat.autoRepeatPatch">
</patch>
@@ -209,6 +202,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="misc.zoomhaptics.zoomHapticsPatch">
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
</patch>

View File

@@ -1,34 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
Raw strings are required because Crowdin AI translations regularly gets confused and
replace \n with an encoded new line character.
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Bad:
<string name="summary_key">First \'item\' text\nSecond \"item\" text</string>
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
Good:
<string name="summary_key">"First 'item' text
Second \"item\" text"</string>
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
@@ -44,6 +33,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_check_environment_not_near_patch_time_invalid">APK quruluş tarixi pozulub</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_title">\"ReVanced\"</string>
<string name="revanced_settings_confirm_user_dialog_title">Davam etmək istəyirsiniz?</string>
<string name="revanced_settings_reset">Sıfırla</string>
<string name="revanced_settings_restart_title">Yenilə və yenidən başlat</string>
@@ -67,9 +57,17 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="gms_core_toast_not_installed_message">MicroG GmsCore quraşdırılmayıb. Bunu quraşdır.</string>
<string name="gms_core_dialog_title">Fəaliyyət lazımdır</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">MicroG GmsCore arxa planda işləməyə icazə almayıb.\n\nTelefonunuz üçün \"Tətbiqim bağlanılmasın\" bələdçisin izləyin və təlimatları MicroG quraşdırmasına tətbiq edin.\n\nBu tətbiq işləyən müddətdə tələb olunur.</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"MicroG GmsCore-un arxaplanda işləməsi üçün icazəsi yoxdur.
Telefonunuz üçün \"Tətbiqimi öldürmə\" bələdçisinə əməl edin və təlimatları MicroG quraşdırmanıza tətbiq edin.
Bu, tətbiqin işləməsi üçün tələb olunur."</string>
<string name="gms_core_dialog_open_website_text">Veb saytı</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">Problemlərin olmaması üçün MicroG GmsCore batareya optimallaşdırması qeyri-aktiv edilməlidir.\n\nDavam düyməsinə toxun və batareya optimallaşdırmasını qeyri-aktiv et.</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"Problemsiz işləməsi üçün MicroG GmsCore batareya optimallaşdırmaları sıradan çıxarılmalıdır.
MicroG üçün batareya optimallaşdırmasını sıradan çıxartmaq, batareya istifadəsinə mənfi təsir etməyəcək.
Davam düyməsinə toxunun və optimallaşdırma dəyişikliklərinə icazə verin."</string>
<string name="gms_core_dialog_continue_text">Davam et</string>
</patch>
</app>
@@ -84,7 +82,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_settings_screen_07_seekbar_title">Axtarış çubuğu</string>
<string name="revanced_settings_screen_08_swipe_controls_title">Sürüşdürmə nəzarətçiləri</string>
<string name="revanced_settings_screen_11_misc_title">Müxtəlif</string>
<string name="revanced_settings_screen_12_video_title">Video</string>
<string name="revanced_settings_screen_12_video_title">\"Video\"</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">Shorts arxa plan oynatmasın bağla</string>
@@ -106,7 +104,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_debug_toast_on_error_title">ReVanced xətasında ani bildiriş göstər</string>
<string name="revanced_debug_toast_on_error_summary_on">Xəta baş verərsə bildiriş göstər</string>
<string name="revanced_debug_toast_on_error_summary_off">Xəta baş verərsə bildiriş göstərmə</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">Ani bildirişləri söndürəndə, bütün ReVanced xəta bildirişləri gizlənir.\n\nGözlənilməz hallardan xəbərdar olmayacaqsınız.</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"Xəta bildirişlərini söndürmək, bütün ReVanced xəta bildirişlərini gizlədir.
Gözlənilməz tədbirlər barədə bildiriş almayacaqsınız."</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_disable_like_subscribe_glow_title">Bəyən/abunə ol düymə parıltısın söndür</string>
@@ -125,7 +125,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_channel_watermark_summary_on">Su nişanı gizlidir</string>
<string name="revanced_hide_channel_watermark_summary_off">Su nişanı göstərilir</string>
<string name="revanced_hide_horizontal_shelves_title">Üfüqi hissələri gizlət</string>
<string name="revanced_hide_horizontal_shelves_summary_on">Belə hissələr gizlidir:\n• Son xəbərlər\n• Baxmağa davam et\n• Daha çox kanal kəşf et\n• Bazarlıq\n• Təkrar izlə</string>
<string name="revanced_hide_horizontal_shelves_summary_on">"Rəflər gizlidir, məsələn:
• Son xəbərlər
• İzləməyə davam et
• Daha çox kanallar kəşf edin
• Alış-veriş
• Yenidən izlə"</string>
<string name="revanced_hide_horizontal_shelves_summary_off">Hissələr göstərilir</string>
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
@@ -153,9 +158,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_timed_reactions_title">Vaxtlı reaksiyaları gizlət</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_search_result_shelf_header_title">Axtarış nəticəsi bölmə başlığını gizlət</string>
<string name="revanced_hide_search_result_shelf_header_summary_on">Bölmə başlığı gizlidir</string>
<string name="revanced_hide_search_result_shelf_header_summary_off">Bölmə başlığı göstərilir</string>
<string name="revanced_hide_channel_guidelines_title">Kanal təlimatlarını gizlət</string>
<string name="revanced_hide_channel_guidelines_summary_on">Kanal təlimatları gizlidir</string>
<string name="revanced_hide_channel_guidelines_summary_off">Kanal təlimatları göstərilir</string>
@@ -252,6 +254,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">Əlaqəli videolarda 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_chat_summary_title">\'Söhbət yekunun\' gizlət </string>
<string name="revanced_hide_comments_chat_summary_summary_on">\"Söhbət yekunu\" gizlədilir</string>
<string name="revanced_hide_comments_chat_summary_summary_off">\'Söhbət yekunu\' göstərilir</string>
<string name="revanced_hide_comments_by_members_header_title">\'Üzvlərin şərhləri\' başlığını gizlət</string>
<string name="revanced_hide_comments_by_members_header_summary_on">\"Üzvlərin şərhləri\" başlığı gizlədilib</string>
<string name="revanced_hide_comments_by_members_header_summary_off">\"Üzvlərin şərhləri\" başlığı göstərilir</string>
@@ -274,7 +279,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_doodles_title">YouTube Doodle-ları gizlət</string>
<string name="revanced_hide_doodles_summary_on">Axtarış çubuğu Doodle-ları gizlidir</string>
<string name="revanced_hide_doodles_summary_off">Axtarış çubuğu Doodle-ları göstərilir</string>
<string name="revanced_hide_doodles_user_dialog_message">YouTube Doodle-lar hər il bir neçə gün göstərilir.\n\nDoodle hazırda bölgənizdə görünür və bu gizlətmə seçimi aktivdirsə, axtarış çubuğu altındakı filtr paneli də gizlədiləcək.</string>
<string name="revanced_hide_doodles_user_dialog_message">"YouTube Doodles ilinin bir neçə günü göstərilir.
Əgər Doodle hazırda sizin bölgənizdə göstərilirsə və bu gizlətmə parametr açıqdırsa, axtarış çubuğunun altındakı filtr çubuğu da gizlənəcək."</string>
<string name="revanced_custom_filter_screen_title">Şəxsi filtr</string>
<string name="revanced_custom_filter_screen_summary">Fərdi filtrlər ilə elementləri gizlət</string>
<string name="revanced_custom_filter_title">Fərdi filtri aktivləşdir</string>
@@ -298,9 +305,18 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_content_phrases_title">Gizlədiləcək açar sözlər</string>
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<string name="revanced_hide_keyword_content_phrases_summary">Yeni sətirlərlə ayrılmış gizlədiləcək açar sözlər və frazalar\n\nAçar sözlər kanal adları və ya video adlarında göstərilən istənilən mətn ola bilər\n\nOrtada böyük hərf olan sözlər korpusla birlikdə qeyd edilməlidir (yəni: iPhone, TikTok, LeBlanc)</string>
<string name="revanced_hide_keyword_content_phrases_summary">"Kənarda qoyulacaq açar sözlər və cümlələr, yeni sətirlərlə ayrılmışdır
Açar sözlər kanal adları və ya video başlıqlarında göstərilən hər hansı mətn ola bilər.
Orta hərfləri böyük hərflərlə olan sözlər böyük hərflərlə yazılmalıdır (məsələn: iPhone, TikTok, LeBlanc)"</string>
<string name="revanced_hide_keyword_content_about_title">Açar söz filtrləməsi haqqında</string>
<string name="revanced_hide_keyword_content_about_summary">Əsas səhifə/Abunəlik/Axtarış nəticələri açar söz ifadələrinə uyğunlaşan məzmunu gizlətmək üçün filtrlənir\n\nMəhdudiyyətlər\n• Shorts-lar kanal adına görə gizlənə bilməz\n• Bəzi UI hissəcikləri gizlədilə bilməz\n• Açar söz axtarışında nəticə olmaya bilər</string>
<string name="revanced_hide_keyword_content_about_summary">"Ana səhifə/Abunəlik/Axtarış nəticələri açar söz ifadələrinə uyğun olan məzmunu gizlətmək üçün süzülür
Məhdudiyyətlər
• Şortlar kanal adı ilə gizlənə bilməz
• Bəzi UI komponentləri gizlənə bilməz
• Açar söz axtarmaq heç bir nəticə verməyə bilər"</string>
<string name="revanced_hide_keyword_content_about_whole_words_title">Bütün sözləri uyğunlaşdır</string>
<!-- Translations _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
<string name="revanced_hide_keyword_content_about_whole_words_summary">Açar söz/frazanın qoşa dırnaqlarla əhatə olunması video adları və kanal adlarının qismən uyğunlaşmasına mane olacaq &lt;br&gt;&lt;br&gt;Məsələn,&lt;br&gt;&lt;b&gt;\"ai\"&lt;/b&gt; videonu gizlədəcək:&lt;b&gt;How does AI work?&lt;/b&gt;&lt;br&gt; lakin gizlətməyəcək: Düzgün;&lt;b&gt;What does fair use mean?&lt;/b&gt;</string>
@@ -316,7 +332,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_general_ads_summary_on">Ümumi reklamlar gizlidir</string>
<string name="revanced_hide_general_ads_summary_off">Ümumi reklamlar göstərilir</string>
<string name="revanced_hide_fullscreen_ads_title">Tam ekran reklamlarını gizlət</string>
<string name="revanced_hide_fullscreen_ads_summary_on">Tam ekran reklamları gizlidir\n\nBu funksiya yalnız köhnə cihazlar üçün əlçatandır</string>
<string name="revanced_hide_fullscreen_ads_summary_on">"Tam ekran reklam gizlidir
Bu xüsusiyyət yalnız köhnə cihazlar üçün mövcuddur"</string>
<string name="revanced_hide_fullscreen_ads_summary_off">Tam ekran reklamları göstərilir</string>
<string name="revanced_hide_buttoned_ads_title">Düyməli reklamları gizlət</string>
<string name="revanced_hide_buttoned_ads_summary_on">Düyməli reklamlar gizlədilir</string>
@@ -490,11 +508,22 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_subscriptions_button_summary_off">Abunəliklər düyməsi göstərilir</string>
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_switch_create_with_notifications_button_title">\"Yarat\"ı \"Bildirişlər\" ilə dəyişdir</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">Yarat düyməsi Bildirişlər düyməsi ilə dəyişdirilir\n\nQeyd: Bunu aktivləşdirmə video reklamları da məcburən gizlədir</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"Yarat düyməsi Bildirişlər düyməsi ilə dəyişdirilir
Qeyd: Bunu aktivləşdirmək video reklamları da məcburi olaraq gizlədir"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">\"Yarat\" düyməsi, \"Bildirişlər\" düyməsi ilə dəyişdirilmir</string>
<string name="revanced_hide_navigation_button_labels_title">Fəaliyyət düymə etiketlərini gizlət</string>
<string name="revanced_hide_navigation_button_labels_summary_on">Etiketlər gizlidir</string>
<string name="revanced_hide_navigation_button_labels_summary_off">Etiketlər göstərilir</string>
<string name="revanced_disable_translucent_status_bar_title">Yarımşəffaf status barı deaktiv et</string>
<string name="revanced_disable_translucent_status_bar_summary_on">Status çubuğu şəffaf deyil</string>
<string name="revanced_disable_translucent_status_bar_summary_off">Status çubuğu şəffaf və ya yarı şəffaf</string>
<string name="revanced_disable_translucent_navigation_bar_light_title">ıq yarımşəffaf barı deaktiv et</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">ıq rejim navigasiya barı qeyri-şəffafdır</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">ıq rəngli naviqasiya çubuğu şəffaf və ya yarı şəffaf</string>
<string name="revanced_disable_translucent_navigation_bar_dark_title">Tünd şəffaf çubuğu deaktiv edin</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_on">Tünd rejim navigasiya barı qeyri-şəffafdır</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_off">Tünd rəngli naviqasiya çubuğu şəffaf və ya yarı şəffaf</string>
</patch>
<patch id="layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch">
<string name="revanced_hide_player_flyout_title">ılan menyu</string>
@@ -709,6 +738,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_player_popup_panels_summary_on">Oynadıcıılan pəncərə panelləri gizlidir</string>
<string name="revanced_hide_player_popup_panels_summary_off">Oynadıcıılan pəncərə panelləri göstərilir</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">Videoları tam ekran təsvirində aç</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">Videolar tam ekranda açılır</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_off">Videolar tam ekranda açılmır</string>
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
<string name="revanced_player_overlay_opacity_title">Oynadıcı örtüyünün qeyri-şəffaflığı</string>
<string name="revanced_player_overlay_opacity_summary">0-100 arasında qeyri-şəffaflıq dəyəri, burada 0 şəffafdır</string>
@@ -726,7 +760,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_ryd_enable_summary_off">Bəyənməmələr göstərilmir</string>
<string name="revanced_ryd_shorts_title">\"Shorts\"da bəyənməmə sayını göstər</string>
<string name="revanced_ryd_shorts_summary_on">Bəyənməmə sayı \"Shorts\"da göstərilir</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">\"Bəyənməmə\"lər \"Shorts\"da göstərilir\n\nMəhdudiyyət: \"Bəyənməmə\"lər gizli rejimdə görünməyə bilər</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">"Şortlarda görünən bəyənməmələr
Məhdudiyyət: Bəyənməmələr gizli rejimdə görünə bilməz"</string>
<string name="revanced_ryd_shorts_summary_off">\"Bəyənməmə\"lər \"Shorts\"da gizlidir</string>
<string name="revanced_ryd_dislike_percentage_title">\"Bəyənməmə\"lər faiz olaraq</string>
<string name="revanced_ryd_dislike_percentage_summary_on">\"Bəyənməmə\"lər faiz olaraq göstərilir</string>
@@ -769,7 +805,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_thumbnails_high_quality_summary_off">Axtarış çubuğu miniatürləri orta keyfiyyətlidir</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_on">Tam ekran axtarış çubuğu miniatürləri yüksək keyfiyyətlidir</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_off">Tam ekran axtarış çubuğu miniatürləri orta keyfiyyətlidir</string>
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">Bu, həm axtarış cizgisi üzrə miniarləri olmayan canlı yayımlarda miniatürləri qaytaracaq.\n\nAxtarış cizgisi miniatürləri, cari video kimi eyni keyfiyyəti işlədəcək.\n\nBu xüsusiyyət, 720p və ya daha aşağı video keyfiyyəti və çox sürətli internet bağlantısı istifadə edərkən daha yaxşı işləyir.</string>
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"Bu, həmçinin axtarış çubuğu kiçik görüntüləri olmayan canlı yayımlarda kiçik şəkilləri bərpa edəcək.
Axtarış çubuğu kiçik şəkilləri cari videonun keyfiyyəti ilə eyni keyfiyyətdən istifadə edəcək.
Bu xüsusiyyət ən yaxşı 720p və ya daha aşağı video keyfiyyəti və çox sürətli internet bağlantısından istifadə edərkən işləyir."</string>
<string name="revanced_restore_old_seekbar_thumbnails_title">Köhnə axtarış çubuğu miniatürlərin al</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_on">Axtarış çubuğu miniatürləri axtarış çubuğu üstündə görünəcək</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_off">Axtarış çubuğu miniatürləri tam ekranda görünəcək</string>
@@ -832,7 +872,10 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_settings_import_successful">Tənzimləmələr uğurla idxal edildi</string>
<string name="revanced_sb_settings_import_failed">%s idxal uğursuz</string>
<string name="revanced_sb_settings_export_failed">%s ixrac uğursuz</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning">Tənzimləmələr şəxsi SponsorBlock istifadəçi kimliyi ehtiva edir.\n\nİstifadəçi kimliyiniz parol kimidir və bu paylaşılmamalıdır.\n</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning">"Parametrlərdə özəl SponsorBlock istifadəçi IDniz var.
Sizə istifadəçi IDniz parol kimidir və heç vaxt başqasına verilməməlidir.
"</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning_dismiss">Təkrar göstərmə</string>
<string name="revanced_sb_diff_segments">Bölüm davranışını dəyişdir</string>
<string name="revanced_sb_segments_sponsor">Sponsor</string>
@@ -892,9 +935,10 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_submit_failed_invalid">Bölüm göndərilmir: %s</string>
<string name="revanced_sb_submit_failed_timeout">SponsorBlock müvəqqəti olaraq cavab vermir</string>
<string name="revanced_sb_submit_failed_unknown_error">Bölüm göndərilmir (status: %1$d %2$s)</string>
<string name="revanced_sb_submit_failed_rate_limit">Bölüm göndərilmir.\nSürət məhdudlaşıb (eyni istifadəçi və ya IP-dən pik sayda)</string>
<string name="revanced_sb_submit_failed_rate_limit">Segment təqdim edə bilmədiniz. Həddən artıq istifadə (eyni istifadəçi və ya IP ünvanından çox)</string>
<string name="revanced_sb_submit_failed_forbidden">Bölüm göndərilmir: %s</string>
<string name="revanced_sb_submit_failed_duplicate">Bölüm göndərilmir.\nArtıq var</string>
<string name="revanced_sb_submit_failed_duplicate">"Segment təqdim edə bilmirsiniz.
Artıq mövcuddur"</string>
<string name="revanced_sb_submit_succeeded">Bölüm uğurla göndərildi</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock müvəqqəti əlçatmazdır (API vaxtı bitdi)</string>
@@ -917,7 +961,15 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_time_start">Bölüm başladığı vaxt</string>
<string name="revanced_sb_new_segment_time_end">Bölümün bitmə vaxtı</string>
<string name="revanced_sb_new_segment_confirm_title">Vaxtlar düzgündür?</string>
<string name="revanced_sb_new_segment_confirm_content">Bölüm \n\n%1$s\n\n%2$s\n\n(%3$s)\n\nTəqdim etməyə hazırsınız?</string>
<string name="revanced_sb_new_segment_confirm_content">"Bölüm
%1$s
ilə
%2$s arasıdır
(%3$s)
Təqdim etməyə hazırdır?"</string>
<string name="revanced_sb_new_segment_start_is_before_end">Başlanğıc sondan əvvəl olmalıdır</string>
<string name="revanced_sb_new_segment_mark_locations_first">Əvvəlcə vaxt çubuğunda iki yeri doldur</string>
<string name="revanced_sb_new_segment_preview_segment_first">Bölümü izləyin, rahatlıqla ötürdüyünə əmin olun</string>
@@ -959,7 +1011,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_app_version_title">Tətbiq versiyasını saxtalaşdır</string>
<string name="revanced_spoof_app_version_summary_on">Versiya saxtalaşdırıldı</string>
<string name="revanced_spoof_app_version_summary_off">Versiya saxtalaşdırılmadı</string>
<string name="revanced_spoof_app_version_user_dialog_message">Tətbiq versiyası, YouTube-un daha köhnə versiyası şəklində saxtalaşdırılacaq.\n\nBu, tətbiqin görünüşünü və funksiyalarını dəyişdirəcək, ancaq bilinməyən yan təsirlər ola bilər.\n\nDaha sonra söndürsəniz, UI səhvlərini önləmək üçün tətbiq məlumatını təmizləmək tövsiyə olunur.</string>
<string name="revanced_spoof_app_version_user_dialog_message">"Tətbiq versiyası köhnə bir YouTube versiyasına oxşayacaq.
Bu tətbiqin görünüşünü və xüsusiyyətlərini dəyişdirəcək, lakin bilinməyən yan təsirlər yarana bilər.
Sonradan söndürülərsə, UI səhvlərini qarşısını almaq üçün tətbiqin 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>
@@ -1012,7 +1068,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_miniplayer_screen_summary">Tətbiqdə kiçildilən oynadıcı üslubunu dəyişdir</string>
<string name="revanced_miniplayer_type_title">Kiçik oynadıcı növü</string>
<string name="revanced_miniplayer_type_entry_0">Qeyri-aktivdir</string>
<string name="revanced_miniplayer_type_entry_1">Orijinal</string>
<string name="revanced_miniplayer_type_entry_1">İlkin</string>
<string name="revanced_miniplayer_type_entry_2">Ən kiçik</string>
<string name="revanced_miniplayer_type_entry_3">Planşet</string>
<string name="revanced_miniplayer_type_entry_4">Müasir 1</string>
@@ -1022,19 +1078,28 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_miniplayer_rounded_corners_summary_on">Künclər dairəvidir</string>
<string name="revanced_miniplayer_rounded_corners_summary_off">Künclər kvadratdır</string>
<string name="revanced_miniplayer_double_tap_action_title">Ölçüsünü dəyişmək üçün cüt toxunmanı və çimdikləməni aktivləşdir</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">Ölçüsünü dəyişdirmək üçün cüt toxunma fəaliyyəti və çimdikləmə aktivləşdirildi\n\n• Mini oynadıcı ölçüsün artırmaq üçün cüt toxunun\n• Orijinal ölçünü bərpa etmək üçün təkrar cüt toxun</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">"İkiqat vuruş hərəkəti və çimdikleyərək yenidən ölçmək aktivdir
• Miniplayer ölçüsünü artırmaq üçün iki dəfə vurun
• Orijinal ölçüsünə qayıtmaq üçün yenidən iki dəfə vurun"</string>
<string name="revanced_miniplayer_double_tap_action_summary_off">Ölçüsünü dəyişdirmək üçün cüt toxunma fəaliyyəti və çimdikləmə yoxdur</string>
<string name="revanced_miniplayer_drag_and_drop_title">\"Sürüklə və burax\"ı aktivləşdir</string>
<string name="revanced_miniplayer_drag_and_drop_summary_on">\"Sürüklə və burax\" aktivdir\n\nMini oynadıcı, ekranın istənilən küncünə sürüklənə bilər</string>
<string name="revanced_miniplayer_drag_and_drop_summary_on">"Sürüklə və burax aktivdir
Miniplayer ekranın istənilən küncünə sürüklənə bilər"</string>
<string name="revanced_miniplayer_drag_and_drop_summary_off">\"Sürüklə və burax\" aktiv deyil</string>
<string name="revanced_miniplayer_horizontal_drag_title">Üfüqi sürükləmə jestini aktivləşdir</string>
<string name="revanced_miniplayer_horizontal_drag_summary_on">Üfüqi sürükləmə jesti aktivdir\n\nKiçik Oynadıcı ekranın soluna və ya sağına sürüklənə bilər</string>
<string name="revanced_miniplayer_horizontal_drag_summary_on">"Üfüqi sürükləmə jesti aktivdir
Miniplayer ekrandan sola və ya sağa sürüklənə bilər"</string>
<string name="revanced_miniplayer_horizontal_drag_summary_off">Üfüqi sürükləmə jesti qapatıldı</string>
<string name="revanced_miniplayer_hide_expand_close_title">\"Bağla\" düyməsini gizlət</string>
<string name="revanced_miniplayer_hide_expand_close_summary_on">\"Bağla\" düyməsi gizlidir</string>
<string name="revanced_miniplayer_hide_expand_close_summary_off">\"Bağla\" düyməsi göstərilir</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_title">Genişləndir və bağla düymələrini gizlət</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_on">Düymələr gizlədilib\n\nGenişləndirmək və ya bağlamaq üçün sürüşdür</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_on">"Düymələr gizlidir
Açmaq və ya bağlamaq barmaqla sürüşdürün"</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_off">Genişləndir və bağla düymələri göstərilir</string>
<string name="revanced_miniplayer_hide_subtext_title">Alt mətnləri gizlət</string>
<string name="revanced_miniplayer_hide_subtext_summary_on">Alt mətnlər gizlədilir</string>
@@ -1065,7 +1130,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
<string name="revanced_bypass_image_region_restrictions_title">Təsvir bölgə məhdudiyyətlərini ötür</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">yt4.ggpht.com təsvir host-u istifadə edilir</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">Orijinal təsvir host-u istifadə edilir\n\nBunu aktivləşdirmə, bəzi bölgələrdə əngəllənən, ağ təsvirləri düzəldə bilər</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">"Orijinal şəkil hostundan istifadə
Bunu aktivləşdirmək, bəzi regionlarda bloklanmışdırılmış itkin şəkilləri düzəldə bilər"</string>
</patch>
<patch id="layout.thumbnails.alternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
@@ -1080,7 +1147,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow &amp; Orijinal miniatürlər</string>
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow &amp; Kadr çəkilişlər</string>
<string name="revanced_alt_thumbnail_options_entry_4">Kadr çəkilişləri</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">DeArrow YouTube videoları üçün bölük mənbəli miniatürlər təchiz edir. Bu miniatürlər hər zaman YouTube tərəfindən təmin edilənlərdən daha uyğun olur\n\nƏgər aktivləşdirilərsə, video URL-lər API serverinə göndəriləcək və başqa heç bir məlumat göndərilməyəcək. Videoda DeArrow miniatürləri yoxdursa, orijinal və ya hələ də kadr çəkilişləri göstərilir\n\nDeArrow haqqında ətraflı öyrənmək üçün bura toxun</string>
<string name="revanced_alt_thumbnail_dearrow_about_title">DeArrow</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow, YouTube videoları üçün kütlədən alınan kiçik görüntülər təqdim edir. Bu kiçik görüntülər YouTube tərəfindən təqdim edilənlərdən daha uyğun ola bilər
Aktivləşdirilərsə, video URLləri API serverinə göndəriləcək və başqa heç bir məlumat göndərilməyəcək. Bir videoda DeArrow kiçik şəkilləri yoxdursa, onda orijinal və ya hərəkətsiz çəkilişlər göstərilir
DeArrow haqqında ətraflı məlumat üçün buraya vurun"</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">API əlçatan deyilsə ani bildiriş göstər</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">DeArrow əlçatan deyilsə bildiriş göstərilir</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">DeArrow əlçatan deyilsə bildiriş göstərilmir</string>
@@ -1119,8 +1191,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
<string name="revanced_spoof_device_dimensions_title">Cihaz ölçülərini saxtalaşdır</string>
<string name="revanced_spoof_device_dimensions_summary_on">Cihaz ölçüləri saxtalaşdı\n\nDaha yüksək video keyfiyyətləri göstərilə bilər, ancaq video oynatma donmaları, daha pis batareya istismarı və bilinməyən yan təsirləri görə bilərsiniz</string>
<string name="revanced_spoof_device_dimensions_summary_off">Cihaz ölçüləri saxtalaşmır\n\nBunu aktivləşdirmə, daha yüksək video keyfiyyətlərinin olmasın təmin edə bilir</string>
<string name="revanced_spoof_device_dimensions_summary_on">"Cihaz ölçüləri saxtalaşdırıldı
Yüksək video keyfiyyətlər görünə bilər, ancaq videonun oynadılmasında qırılma, daha pis batareya ömrü və bilinməyən yan təsirləri sezə bilərsiniz"</string>
<string name="revanced_spoof_device_dimensions_summary_off">"Cihaz ölçüləri saxtalaşdırılmayıb
Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər"</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">Bunu aktivləşdirmə, video oynatma donmalarına, daha pis batareya istismarına və bilinməyən yan təsirlərə səbəb ola bilər.</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
@@ -1147,6 +1223,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_disable_zoom_haptics_summary_on">Toxunuş əks-əlaqəsi bağlandı</string>
<string name="revanced_disable_zoom_haptics_summary_off">Toxunuş əks-əlaqəsi aktivdir</string>
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">Orijinal səsi tələb et</string>
<string name="revanced_force_original_audio_summary_on">Orijinal səs istifadəsi</string>
<string name="revanced_force_original_audio_summary_off">İlkin səs istifadəsi</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
<string name="revanced_video_quality_default_entry_1">Avtomatik</string>
@@ -1170,8 +1251,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_custom_speed_menu_summary_off">Fərdi sürət menyusu göstərilmir</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ən az olmalıdır. Standart dəyərlər istifadəsi.</string>
<string name="revanced_custom_playback_speeds_parse_exception">Etibarsız oynatma sürətləri. Standartlar istifadədədir.</string>
<string name="revanced_custom_playback_speeds_invalid">Fərdi sürətlər %s dəyərindən az olmalıdır</string>
<string name="revanced_custom_playback_speeds_parse_exception">Fərdi oynatma sürətləri etibarsızdır</string>
<string name="revanced_custom_playback_speeds_auto">Avtomatik</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
@@ -1196,18 +1277,78 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_video_streams_screen_summary">Oynatma problemlərin önləmək üçün qəbuledici video yayımların saxtalaşdır</string>
<string name="revanced_spoof_video_streams_title">Video yayımları saxtalaşdır</string>
<string name="revanced_spoof_video_streams_summary_on">Video yayımları saxtalaşdırılır</string>
<string name="revanced_spoof_video_streams_summary_off">Video yayımları saxtalaşmır\n\nVideo oynatma işləməyə bilər</string>
<string name="revanced_spoof_video_streams_summary_off">"Video yayımlar saxtalaşdırılmır
Video oynatma işləməyə bilər"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">Bu seçimi bağlamaq, video oynatma problemlərinə səbəb olar.</string>
<string name="revanced_spoof_video_streams_client_title">İlkin qəbuledici</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">Məcburi AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Video kodlaşdırma: AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Video kodlaşdırma / VP9 və ya AV1</string>
<string name="revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on">Cihazınızın VP9 hardware decoding\'i yoxdur və bu seçim, \"Qəbuledicini saxtalaşdırma\" aktivləşdikdə həmişəlikdir</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">Bunu aktivləşdirmə batareya ömrünü yaxşılaşdıra və oynatma donmasını düzəldə bilər.\n\nAVC maksimum 1080p görüntü imkanına malikdir və video oynadılması VP9 və ya AV1-dən daha çox internet məlumatı istifadə edəcək.</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Video kodlama AVC (H.264) -yə məcbur edilir</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Video kodlama birbaşa yoxlanılır</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Bunu fəallaşdırma, batareya ömrünü yaxşılaşdıra və oxutma ilişmələrini düzəldə bilər.
AVC-nin maksimum dəqiqliyi 1080p-dir, Opus səs kodek əlçatan deyil və video oxutma, VP9 və ya AV1-dən daha çox internet datası istifadə edəcək."</string>
<string name="revanced_spoof_video_streams_about_ios_title">iOS saxtakarlığı yan təsirləri</string>
<string name="revanced_spoof_video_streams_about_ios_summary">• Şəxsi uşaq videoları oynadılmaya bilər\n• Canlı yayımlar başdan başlayır\n• Videolar 1 saniyə tez bitə bilər\n• Opus səs kodlama yoxdur</string>
<string name="revanced_spoof_video_streams_about_ios_summary">"◦ Xüsusi uşaq videoları bəlkə də oynanılmaya bilər
◦ Videolar 1 saniyə tez bitir"</string>
<string name="revanced_spoof_video_streams_about_android_vr_title">Android VR saxtakarlığı yan təsirləri</string>
<string name="revanced_spoof_video_streams_about_android_vr_summary">• Uşaq videoları oynadılmaya bilər\n• Səs treki menyusu əskikdir\n• Sabit səs həcmi əlçatan deyil</string>
<string name="revanced_spoof_video_streams_about_android_vr_summary">"• Uşaq videoları oxudulmaya bilər
• Canlı yayımlar başdan başlayır
• Videolar 1 saniyə tez bitir"</string>
<string name="revanced_spoof_video_streams_language_title">İlkin səs yayımı dili</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">Tətbiq dili</string>
<string name="revanced_spoof_video_streams_language_AR">Ərəbcə</string>
<string name="revanced_spoof_video_streams_language_AZ">Azərbaycanca</string>
<string name="revanced_spoof_video_streams_language_BG">Bolqarca</string>
<string name="revanced_spoof_video_streams_language_BN">Benqalca</string>
<string name="revanced_spoof_video_streams_language_CA">Katalanca</string>
<string name="revanced_spoof_video_streams_language_CS">Çexcə</string>
<string name="revanced_spoof_video_streams_language_DA">Danimarka dili</string>
<string name="revanced_spoof_video_streams_language_DE">Almanca</string>
<string name="revanced_spoof_video_streams_language_EL">Yunanca</string>
<string name="revanced_spoof_video_streams_language_EN">İngiliscə</string>
<string name="revanced_spoof_video_streams_language_ES">İspanca</string>
<string name="revanced_spoof_video_streams_language_ET">Estonca</string>
<string name="revanced_spoof_video_streams_language_FA">Farsca</string>
<string name="revanced_spoof_video_streams_language_FI">Fincə</string>
<string name="revanced_spoof_video_streams_language_FR">Fransızca</string>
<string name="revanced_spoof_video_streams_language_GU">Qücərat dili</string>
<string name="revanced_spoof_video_streams_language_HI">Hindcə</string>
<string name="revanced_spoof_video_streams_language_HR">Xorvatca</string>
<string name="revanced_spoof_video_streams_language_HU">Macarca</string>
<string name="revanced_spoof_video_streams_language_ID">İndoneziya dili</string>
<string name="revanced_spoof_video_streams_language_IT">İtalyanca</string>
<string name="revanced_spoof_video_streams_language_JA">Yaponca</string>
<string name="revanced_spoof_video_streams_language_KK">Qazaxca</string>
<string name="revanced_spoof_video_streams_language_KO">Koreyaca</string>
<string name="revanced_spoof_video_streams_language_LT">Litvaca</string>
<string name="revanced_spoof_video_streams_language_LV">Letonca</string>
<string name="revanced_spoof_video_streams_language_MK">Makedon Dili</string>
<string name="revanced_spoof_video_streams_language_MN">Monqolca</string>
<string name="revanced_spoof_video_streams_language_MR">Marathi dili</string>
<string name="revanced_spoof_video_streams_language_MS">Malay dili</string>
<string name="revanced_spoof_video_streams_language_MY">Birman dili</string>
<string name="revanced_spoof_video_streams_language_NL">Hollandca</string>
<string name="revanced_spoof_video_streams_language_OR">Oriya dili</string>
<string name="revanced_spoof_video_streams_language_PA">Pəncabca</string>
<string name="revanced_spoof_video_streams_language_PL">Polyak dili</string>
<string name="revanced_spoof_video_streams_language_PT_BR">Portuqalca (Braziliya)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">Portuqalca (Portəgiz)</string>
<string name="revanced_spoof_video_streams_language_RO">Rumınca</string>
<string name="revanced_spoof_video_streams_language_RU">Rusca</string>
<string name="revanced_spoof_video_streams_language_SK">Slovak dili</string>
<string name="revanced_spoof_video_streams_language_SL">Sloven dili</string>
<string name="revanced_spoof_video_streams_language_SR">Serbcə</string>
<string name="revanced_spoof_video_streams_language_SV">İsveçcə</string>
<string name="revanced_spoof_video_streams_language_SW">Suahili dili</string>
<string name="revanced_spoof_video_streams_language_TA">Tamilcə</string>
<string name="revanced_spoof_video_streams_language_TE">Teluquca</string>
<string name="revanced_spoof_video_streams_language_TH">Tay dili</string>
<string name="revanced_spoof_video_streams_language_TR">Türkcə</string>
<string name="revanced_spoof_video_streams_language_UK">Ukrayna dili</string>
<string name="revanced_spoof_video_streams_language_UR">Urduca</string>
<string name="revanced_spoof_video_streams_language_VI">Vyetnamca</string>
<string name="revanced_spoof_video_streams_language_ZH">Çincə</string>
</patch>
</app>
<app id="twitch">
@@ -1217,8 +1358,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_block_audio_ads_summary_off">Səsli reklamlar bloklanmayıb</string>
</patch>
<patch id="ad.embedded.embeddedAdsPatch">
<string name="revanced_embedded_ads_service_unavailable">%s əlçatmazdır. Reklamlar görünə bilər. Seçimlərdə başqa reklam bloku xidmətinə keçirməyə cəhd et.</string>
<string name="revanced_embedded_ads_service_failed">%s serveri xəta sorğusu verdi. Reklam görünə bilər. Seçimlərdə başqa reklam bloku xidmətinə keçir.</string>
<string name="revanced_embedded_ads_service_unavailable">%s əlçatmazdır, reklamlar göstərilə bilər. Ayarlarda reklam əngəlləmə xidmətini dəyişdirməyə çalışın.</string>
<string name="revanced_embedded_ads_service_failed">%s bir xəta qaytardı, reklamlar göstərilə bilər. Ayarlarda reklam əngəlləmə xidmətini dəyişdirməyə çalışın.</string>
<string name="revanced_block_embedded_ads_title">Yerləşdirilən video reklamlarını əngəllə</string>
<string name="revanced_block_embedded_ads_entry_1">Qeyri-aktiv edildi</string>
<string name="revanced_block_embedded_ads_entry_2">Dəqiq proksi</string>

View File

@@ -1,40 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Note: All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
All strings must have a unique path, even if the same string is declared in two different apps.
This is because Crowdin requires temporarily flattening this file and removing the <app> and <patch> elements.
# General guidelines and information for translating
## Strings parameters can be reordered to allow more flexible translations if the grammar should be changed.
Strings with new lines must be raw strings where they're wrapped in quotes and new lines are not encoded.
Raw strings still requires escaping embedded double quotes but escaping embedded single quotes is optional.
For example, the patches string:
<string name="revanced_patches_string">You will arrive at %1$s in %2$s hours from now</string>
Could be translated to another language using a rearranged grammar:
<string name="revanced_patches_string">You will arrive %2$s hours from now at %1$s</string>
Raw strings are required because Crowdin AI translations regularly gets confused and
replace \n with an encoded new line character.
For Manager strings:
You will arrive at ${destination} in ${count} hours from now
Could be rearranged by changing the order of the ${} parameters:
You will arrive ${count} hours from now at ${destination}
Bad:
<string name="summary_key">First \'item\' text\nSecond \"item\" text</string>
Reordering is particularly relevant when translating into right to left languages, or for any language with grammar that is noticeably different from English.
Good:
<string name="summary_key">"First 'item' text
Second \"item\" text"</string>
## Single and double quotation marks must be escaped for patch strings (Manager does not require escaping any quotes).
All _patches_ single and double quotation marks must be escaped as \" or \'
Forgetting to do this will cause that string to appear in app with no quotation characters.
Correct:
<string name="revanced_string">You\'re correct. This is the \"correct\" way and this text will appear as expected in the app</string>
Not correct:
<string name="revanced_string">You're not correct. This is not the "correct" way and this text will not appear as expected the in app</string>
-->
<resources>
<app id="shared">
<patch id="misc.checks.checkEnvironmentPatch">
<string name="revanced_check_environment_failed_title">\"Правядзенне праверкі асяроддзя не ўдалося\"</string>
<string name="revanced_check_environment_dialog_open_official_source_button">\"Адкрыць афіцыйны вэб-сайт\"</string>
<string name="revanced_check_environment_dialog_ignore_button">Ігнараваць</string>
<string name="revanced_check_environment_failed_message">&lt;h5&gt;Гэта дадатак відавочна не з\'яўляецца патчам.&lt;/h5&gt;&lt;br&gt;Гэта дадатак можа працаваць няправільна, а таксама можа быць &lt;b&gt;небяспечным або нават небяспечным у выкарыстанні&lt;/b&gt;.&lt;br&gt;&lt;br&gt;Гэтыя праверкі азначаюць, што гэта дадатак было загаддзя перароблена або атрымана ад кагосьці іншага:&lt;br&gt;&lt;br&gt;&lt;small&gt;%1$s&lt;/small&gt;&lt;br&gt;Настойліва рэкамендуецца &lt;b&gt;выдаліць гэты дадатак і перарабіць яго самастойна&lt;/b&gt;, каб пераканацца, што вы выкарыстоўваеце правераны і бяспечны дадатак.&lt;p&gt;&lt;br&gt;Калі ігнараваць, гэта папярэджанне будзе паказана толькі два разы.</string>
<string name="revanced_check_environment_not_same_patching_device">Адкарэктавана на іншай прыладзе</string>
<string name="revanced_check_environment_manager_not_expected_installer">Не ўстаноўлена ReVanced Manager</string>
<string name="revanced_check_environment_not_near_patch_time">Адкарэктавана больш чым 10 хвілін таму</string>
<string name="revanced_check_environment_not_near_patch_time_days">Адкарэктавана %s дзён таму</string>
<string name="revanced_check_environment_not_near_patch_time_invalid">Дата стварэння APK пашкоджана</string>
</patch>
<patch id="misc.settings.settingsResourcePatch">
<string name="revanced_settings_title">ReVanced</string>
<string name="revanced_settings_confirm_user_dialog_title">Вы хочаце працягнуць?</string>
<string name="revanced_settings_reset">Скінуць</string>
<string name="revanced_settings_restart_title">Абнавіце і перазагрузіце</string>
@@ -58,9 +57,17 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="gms_core_toast_not_installed_message">MicroG GmsCore не ўсталяваны. Усталюйце яго.</string>
<string name="gms_core_dialog_title">Патрабуецца дзеянне</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">MicroG GmsCore не мае дазволу на працу ў фонавым рэжыме.\n\nВыконвайце інструкцыі \"Не забівайце маю праграму\" для вашага тэлефона і прымяніце інструкцыі да ўстаноўкі MicroG.\n\nГэта патрабуецца для прыкладанне для працы.</string>
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"MicroG GmsCore не мае дазволу на працу ў фонавым рэжыме.
Выконвайце інструкцыі \"Не забівай маё прыкладанне\" для вашага тэлефона і прымяніце іх да вашай ўстаноўкі MicroG.
Гэта неабходна для працы прыкладання."</string>
<string name="gms_core_dialog_open_website_text">Адкрыць сайт</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">Аптымізацыя батарэі MicroG GmsCore павінна быць адключана, каб прадухіліць праблемы.\n\nНацісніце кнопку \"Працягнуць\" і адключыце аптымізацыю батарэі.</string>
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"Неабходна адключыць аптымізацыю батарэі для MicroG GmsCore, каб пазбегнуць праблем.
Адключэнне аптымізацыі батарэі для MicroG не паўплывае на выкарыстанне батарэі.
Націсніце кнопку \"Працягнуць\" і дазвольце змяніць аптымізацыю."</string>
<string name="gms_core_dialog_continue_text">Працягнуць</string>
</patch>
</app>
@@ -72,12 +79,16 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_settings_screen_03_feed_title">Карміць</string>
<string name="revanced_settings_screen_04_player_title">Гулец</string>
<string name="revanced_settings_screen_05_general_title">Генеральная планіроўка</string>
<string name="revanced_settings_screen_06_shorts_title">Shorts</string>
<string name="revanced_settings_screen_07_seekbar_title">Панэль пошуку</string>
<string name="revanced_settings_screen_08_swipe_controls_title">Элементы кіравання пальцам</string>
<string name="revanced_settings_screen_11_misc_title">Рознае</string>
<string name="revanced_settings_screen_12_video_title">Відэа</string>
</patch>
<patch id="misc.backgroundplayback.backgroundPlaybackPatch">
<string name="revanced_shorts_disable_background_playback_title">Адключыць прайграванне Shorts у фонавым</string>
<string name="revanced_shorts_disable_background_playback_summary_on">Прайграванне Shorts у фонавым адключана</string>
<string name="revanced_shorts_disable_background_playback_summary_off">Фоновый плейлист Shorts включен</string>
</patch>
<patch id="misc.debugging.enableDebuggingPatch">
<string name="revanced_debug_screen_title">Адладка</string>
@@ -94,10 +105,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_debug_toast_on_error_title">Паказаць тост пры памылцы ReVanced</string>
<string name="revanced_debug_toast_on_error_summary_on">Тост паказваецца, калі ўзнікае памылка</string>
<string name="revanced_debug_toast_on_error_summary_off">Тост не паказваецца, калі ўзнікае памылка</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">Адключэнне апавяшчэнняў пра памылкі схавае ўсе апавяшчэнні аб памылках ReVanced.\n\nВы не будзеце атрымліваць апавяшчэнні аб непрадбачаных падзеях.</string>
<string name="revanced_debug_toast_on_error_user_dialog_message">"Адключэнне паведамленняў пра памылкі схавае ўсе апавяшчэнні ReVanced пра памылкі.
Вы не будзеце атрымліваць апавяшчэнні пра нечаканыя падзеі."</string>
</patch>
<patch id="layout.hide.general.hideLayoutComponentsPatch">
<string name="revanced_disable_like_subscribe_glow_title">Адключыць свячэнне кнопкі \"Падабаецца\" / \"Падпісацца\".</string>
<string name="revanced_disable_like_subscribe_glow_title">Адключыць свячэнне кнопкі \"Падабаецца\" / \"Падпісацца\"</string>
<string name="revanced_disable_like_subscribe_glow_summary_on">Кнопка \"Падабаецца\" і \"Падпісацца\" не будуць свяціцца пры згадванні</string>
<string name="revanced_disable_like_subscribe_glow_summary_off">Кнопка \"Падабаецца\" і \"Падпісацца\" будуць свяціцца пры згадванні</string>
<string name="revanced_hide_album_cards_title">Схаваць карты альбома</string>
@@ -113,7 +126,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_channel_watermark_summary_on">Вадзяны знак схаваны</string>
<string name="revanced_hide_channel_watermark_summary_off">Паказаны вадзяны знак</string>
<string name="revanced_hide_horizontal_shelves_title">Схавайце гарызантальныя паліцы</string>
<string name="revanced_hide_horizontal_shelves_summary_on">Паліцы схаваны, напрыклад:\n• Апошнія навіны\n• Працягвайце прагляд\n• Даследуйце іншыя каналы\n• Пакупкі\n• Глядзіце яшчэ раз</string>
<string name="revanced_hide_horizontal_shelves_summary_on">"Схаваны паліцы, такія як:
• Навіны
• Працяг прагляду
• Даведайцеся больш пра каналы
• Пакупкі
• Паглядзець яшчэ раз"</string>
<string name="revanced_hide_horizontal_shelves_summary_off">Паказаны паліцы</string>
<!-- 'Join' should be translated using the same localized wording YouTube displays.
This appears in the video player for certain videos. -->
@@ -141,9 +159,6 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_timed_reactions_title">Схаваць рэакцыі па часе</string>
<string name="revanced_hide_timed_reactions_summary_on">Часовыя рэакцыі схаваныя</string>
<string name="revanced_hide_timed_reactions_summary_off">Паказваюцца рэакцыі па часе</string>
<string name="revanced_hide_search_result_shelf_header_title">Схаваць загаловак паліцы з вынікамі пошуку</string>
<string name="revanced_hide_search_result_shelf_header_summary_on">Загаловак паліцы схаваны</string>
<string name="revanced_hide_search_result_shelf_header_summary_off">Паказаны загаловак паліцы</string>
<string name="revanced_hide_channel_guidelines_title">Схаваць рэкамендацыі канала</string>
<string name="revanced_hide_channel_guidelines_summary_on">Правілы канала схаваны</string>
<string name="revanced_hide_channel_guidelines_summary_off">Паказваюцца інструкцыі па каналах</string>
@@ -210,7 +225,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_attributes_section_title">Схаваць раздзел атрыбутаў</string>
<string name="revanced_hide_attributes_section_summary_on">Раздзелы \"Выбраныя месцы\", гульні і музыка схаваны</string>
<string name="revanced_hide_attributes_section_summary_off">Паказваюцца раздзелы \"Выбраныя месцы\", Гульні і Музыка</string>
<string name="revanced_hide_chapters_section_title">Схаваць раздзел \"Раздзелы\".</string>
<string name="revanced_hide_chapters_section_title">Схаваць раздзел \"Раздзелы\"</string>
<string name="revanced_hide_chapters_section_summary_on">Раздзел раздзелаў схаваны</string>
<string name="revanced_hide_chapters_section_summary_off">Паказваецца раздзел раздзелаў</string>
<string name="revanced_hide_podcast_section_title">Схаваць раздзел \"Даследаваць падкаст\"</string>
@@ -240,12 +255,18 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_filter_bar_feed_in_related_videos_summary_off">Паказана ў звязаных відэа</string>
<string name="revanced_comments_screen_title">Каментарыі</string>
<string name="revanced_comments_screen_summary">Схаваць або паказаць кампаненты раздзела каментарыяў</string>
<string name="revanced_hide_comments_chat_summary_title">Схаваць \"Зводка чата\" </string>
<string name="revanced_hide_comments_chat_summary_summary_on">«Агляд чата» схаваны</string>
<string name="revanced_hide_comments_chat_summary_summary_off">«Агляд чата» паказаны</string>
<string name="revanced_hide_comments_by_members_header_title">Схаваць загаловак \"Каментарыі ўдзельнікаў\"</string>
<string name="revanced_hide_comments_by_members_header_summary_on">Загаловак \"Каментарыі ўдзельнікаў\" схаваны</string>
<string name="revanced_hide_comments_by_members_header_summary_off">Паказаны загаловак \"Каментарыі ўдзельнікаў\"</string>
<string name="revanced_hide_comments_section_title">Схаваць раздзел каментарыяў</string>
<string name="revanced_hide_comments_section_summary_on">Раздзел каментарыяў схаваны</string>
<string name="revanced_hide_comments_section_summary_off">Паказваецца раздзел каментарыяў</string>
<string name="revanced_hide_comments_create_a_short_button_title">Схаваць кнопку \"Створиць Short\"</string>
<string name="revanced_hide_comments_create_a_short_button_summary_on">Кнопка \"Створиць Short\" схавана</string>
<string name="revanced_hide_comments_create_a_short_button_summary_off">Кнопка \"Створиць Short\" паказана</string>
<string name="revanced_hide_comments_preview_comment_title">Схаваць каментарый для папярэдняга прагляду</string>
<string name="revanced_hide_comments_preview_comment_summary_on">Каментарый перад праглядам схаваны</string>
<string name="revanced_hide_comments_preview_comment_summary_off">Паказваецца папярэдні прагляд каментарыя</string>
@@ -256,6 +277,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_comments_timestamp_and_emoji_buttons_summary_on">Кнопкі меткі часу і эмодзі схаваны</string>
<string name="revanced_hide_comments_timestamp_and_emoji_buttons_summary_off">Паказваюцца кнопкі меткі часу і эмодзі</string>
<!-- https://logos.fandom.com/wiki/YouTube/Yoodles -->
<string name="revanced_hide_doodles_title">Схаваць YouTube Doodles</string>
<string name="revanced_hide_doodles_summary_on">Doodles у панэлі пошуку схаваны</string>
<string name="revanced_hide_doodles_summary_off">Doodles у панэлі пошуку паказаны</string>
<string name="revanced_hide_doodles_user_dialog_message">"Doodles па YouTube паказваюцца некалькі дзён у год.
Калі Doodle зараз паказваецца ў вашым рэгіёне і гэты параметр схаваны, панэль фільтраў ніжэй радка пошуку таксама будзе схавана."</string>
<string name="revanced_custom_filter_screen_title">Карыстальніцкі фільтр</string>
<string name="revanced_custom_filter_screen_summary">Схавайце кампаненты з дапамогай карыстацкіх фільтраў</string>
<string name="revanced_custom_filter_title">Уключыць карыстальніцкі фільтр</string>
@@ -279,16 +306,36 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_keyword_content_phrases_title">Ключавыя словы, якія трэба схаваць</string>
<!-- For localization it is preferred, but not required, if 'LeBlanc' is replaced with a localized name or a familiar word that has upper case letters in the middle of the word.
This is because keywords can be in any language, and showing an example in the localized script helps convey this. -->
<string name="revanced_hide_keyword_content_phrases_summary">"Ключавыя словы і фразы для схавання, падзеленыя пераходамі на новую радок
Ключавыя словы могуць быць назвамі каналаў або любым тэкстам, які паказаны ў назвах відэа
Словы з вялікімі літарамі пасярэдзіне павінны ўводзіцца з вялікай літары (напрыклад: iPhone, TikTok, LeBlanc)"</string>
<string name="revanced_hide_keyword_content_about_title">Аб фільтрацыі ключавых слоў</string>
<string name="revanced_hide_keyword_content_about_summary">"Вынікі дома/падпіскі/пошуку фільтруюцца, каб схаваць змест, які адпавядае ключавым словам і фразам
Абмежаванні
• Shorts нельга схаваць па назве канала
• Некаторыя элементы інтэрфейсу могуць не быць схаваны
• Пошук па ключавым слове можа не паказаць вынікі"</string>
<string name="revanced_hide_keyword_content_about_whole_words_title">Супадзенне цэлых слоў</string>
<!-- Translations _must_ use a localized example. For languages that do not use spaces between words (Chinese, Japanese, etc) the English AI example should be used since no localized examples exist. Or if using machine translations, or if nobody wants to think of a localized example, then the English 'ai' example should be left as-is. -->
<string name="revanced_hide_keyword_content_about_whole_words_summary">Калі вы ахінеце ключавое слова або фразу ў двухразовыя лапкі, гэта перашкодзіць частковаму супадзенню назваў відэа і каналаў&lt;br&gt;&lt;br&gt;Напрыклад,&lt;br&gt;&lt;b&gt;\"ai\"&lt;/b&gt; схавае відэа: &lt;b&gt;How does AI work?&lt;/b&gt;&lt;br&gt;але не схавае: &lt;b&gt;What does fair use mean?&lt;/b&gt;</string>
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
<string name="revanced_hide_keyword_toast_invalid_common">Нельга выкарыстоўваць ключавое слова: %s</string>
<string name="revanced_hide_keyword_toast_invalid_common_whole_word_required">Дадайце цытаты да ключавога слова: %s</string>
<string name="revanced_hide_keyword_toast_invalid_conflicting">Ключавое слова мае супярэчлівыя дэкларацыі: %s</string>
<string name="revanced_hide_keyword_toast_invalid_length">Ключавое слова занадта кароткае і патрабуе цытат: %s</string>
<string name="revanced_hide_keyword_toast_invalid_broad">Ключавое слова схавае ўсе відэа: %s</string>
</patch>
<patch id="ad.general.hideAdsResourcePatch">
<string name="revanced_hide_general_ads_title">Схаваць агульную рэкламу</string>
<string name="revanced_hide_general_ads_summary_on">Агульныя аб\"явы схаваныя</string>
<string name="revanced_hide_general_ads_summary_off">Паказваюцца агульныя аб\"явы</string>
<string name="revanced_hide_fullscreen_ads_title">Схаваць поўнаэкранную рэкламу</string>
<string name="revanced_hide_fullscreen_ads_summary_on">Поўнаэкранная рэклама схавана\n\nГэта функцыя даступная толькі для старых прылад</string>
<string name="revanced_hide_fullscreen_ads_summary_on">"Схаваны поўнаэкранныя рэкламныя ролікі
Гэтая функцыя даступная толькі для старых прылад"</string>
<string name="revanced_hide_fullscreen_ads_summary_off">Адлюстроўваецца поўнаэкранная рэклама</string>
<string name="revanced_hide_buttoned_ads_title">Схаваць рэкламу на кнопках</string>
<string name="revanced_hide_buttoned_ads_summary_on">Аб\"явы на кнопках схаваныя</string>
@@ -302,6 +349,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_products_banner_title">Схаваць банер для прагляду прадуктаў</string>
<string name="revanced_hide_products_banner_summary_on">Банэр схаваны</string>
<string name="revanced_hide_products_banner_summary_off">Паказваецца банэр</string>
<string name="revanced_hide_player_store_shelf_title">Схаваць полку крамы прайгравальніка</string>
<string name="revanced_hide_player_store_shelf_summary_on">Паліца крамы схавана</string>
<string name="revanced_hide_player_store_shelf_summary_off">Паліца крамы паказана</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>
@@ -375,7 +425,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_swipe_volume_title">Уключыць жэст гучнасці</string>
<string name="revanced_swipe_volume_summary_on">Правядзенне пальцам па гучнасці ўключана</string>
<string name="revanced_swipe_volume_summary_off">Правядзенне пальцам па гучнасці адключана</string>
<string name="revanced_swipe_press_to_engage_title">Уключыць жэст \"націсканне для правядзення пальцам\".</string>
<string name="revanced_swipe_press_to_engage_title">Уключыць жэст \"націсканне для правядзення пальцам\"</string>
<string name="revanced_swipe_press_to_engage_summary_on">Правядзенне пальцам уключана</string>
<string name="revanced_swipe_press_to_engage_summary_off">Правядзенне пальцам адключана</string>
<string name="revanced_swipe_haptic_feedback_title">Уключыць тактыльную зваротную сувязь</string>
@@ -405,22 +455,22 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.buttons.action.hideButtonsPatch">
<string name="revanced_hide_buttons_screen_title">Кнопкі дзеянняў</string>
<string name="revanced_hide_buttons_screen_summary">Схаваць або паказаць кнопкі пад відэа</string>
<string name="revanced_hide_like_dislike_button_title">Схаваць \"Падабаецца\" і \"Не падабаецца\".</string>
<string name="revanced_hide_like_dislike_button_title">Схаваць \"Падабаецца\" і \"Не падабаецца\"</string>
<string name="revanced_hide_like_dislike_button_summary_on">Кнопкі \"Падабаецца\" і \"Не падабаецца\" схаваны</string>
<string name="revanced_hide_like_dislike_button_summary_off">Паказваюцца кнопкі \"Падабаецца\" і \"Не падабаецца\".</string>
<string name="revanced_hide_like_dislike_button_summary_off">Паказваюцца кнопкі \"Падабаецца\" і \"Не падабаецца\"</string>
<!-- 'Share' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_share_button_title">Схаваць Share</string>
<string name="revanced_hide_share_button_summary_on">Кнопка \"Падзяліцца\" схавана</string>
<string name="revanced_hide_share_button_summary_off">Паказана кнопка \"Падзяліцца\".</string>
<string name="revanced_hide_share_button_summary_off">Паказана кнопка \"Падзяліцца\"</string>
<!-- 'Report' should be translated with the same localized wording that YouTube displays.
This button usually appears only on live streams. -->
<string name="revanced_hide_report_button_title">Схаваць справаздачу</string>
<string name="revanced_hide_report_button_summary_on">Кнопка \"Паведаміць\" схавана</string>
<string name="revanced_hide_report_button_summary_off">Паказана кнопка \"Паведаміць\".</string>
<string name="revanced_hide_report_button_summary_off">Паказана кнопка \"Паведаміць\"</string>
<!-- 'Remix' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_remix_button_title">Схаваць рэмікс</string>
<string name="revanced_hide_remix_button_summary_on">Кнопка \"Рэмікс\" схавана</string>
<string name="revanced_hide_remix_button_summary_off">Паказана кнопка \"Рэмікс\".</string>
<string name="revanced_hide_remix_button_summary_off">Паказана кнопка \"Рэмікс\"</string>
<!-- 'Download' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_download_button_title">Схаваць загрузку</string>
<string name="revanced_hide_download_button_summary_on">Кнопка загрузкі схавана</string>
@@ -436,7 +486,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- 'Save' should be translated with the same localized wording that YouTube displays. -->
<string name="revanced_hide_playlist_button_title">Схаваць Захаваць у плэйліст</string>
<string name="revanced_hide_playlist_button_summary_on">Кнопка \"Захаваць у спіс прайгравання\" схавана</string>
<string name="revanced_hide_playlist_button_summary_off">Паказана кнопка \"Захаваць у спіс прайгравання\".</string>
<string name="revanced_hide_playlist_button_summary_off">Паказана кнопка \"Захаваць у спіс прайгравання\"</string>
</patch>
<patch id="layout.buttons.navigation.navigationButtonsPatch">
<string name="revanced_navigation_buttons_screen_title">Кнопкі навігацыі</string>
@@ -444,23 +494,37 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- 'Home' should be translated using the same localized wording YouTube displays for the tab. -->
<string name="revanced_hide_home_button_title">Схаваць галоўную</string>
<string name="revanced_hide_home_button_summary_on">Кнопка \"Дадому\" схавана</string>
<string name="revanced_hide_home_button_summary_off">Паказана кнопка \"Дадому\".</string>
<string name="revanced_hide_home_button_summary_off">Паказана кнопка \"Дадому\"</string>
<!-- 'Shorts' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_hide_shorts_button_title">Схаваць Shorts</string>
<string name="revanced_hide_shorts_button_summary_on">Кнопка Shorts схавана</string>
<string name="revanced_hide_shorts_button_summary_off">Кнопка shorts паказваецца</string>
<!-- The Create button has no display name. Translate normally. -->
<string name="revanced_hide_create_button_title">Схаваць Стварыць</string>
<string name="revanced_hide_create_button_summary_on">Кнопка \"Стварыць\" схавана</string>
<string name="revanced_hide_create_button_summary_off">Паказана кнопка \"Стварыць\".</string>
<string name="revanced_hide_create_button_summary_off">Паказана кнопка \"Стварыць\"</string>
<!-- 'Subscriptions' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_hide_subscriptions_button_title">Схаваць падпіскі</string>
<string name="revanced_hide_subscriptions_button_summary_on">Кнопка \"Падпіскі\" схавана</string>
<string name="revanced_hide_subscriptions_button_summary_off">Паказана кнопка \"Падпіскі\".</string>
<string name="revanced_hide_subscriptions_button_summary_off">Паказана кнопка \"Падпіскі\"</string>
<!-- 'Notifications' should be translated using the same localized wording YouTube displays the tab. -->
<string name="revanced_switch_create_with_notifications_button_title">Пераключальнік \"Стварыць з апавяшчэннямі\".</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">Кнопка \"Стварыць\" замяняецца кнопкай \"Апавяшчэнні\"\n\nЗаўвага: пры ўключэнні гэтага таксама прымусова хаваецца відэарэклама</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">Кнопка \"Стварыць\" не ўзаемадзейнічае з кнопкай \"Апавяшчэнні\".</string>
<string name="revanced_switch_create_with_notifications_button_title">Пераключальнік \"Стварыць з апавяшчэннямі\"</string>
<string name="revanced_switch_create_with_notifications_button_summary_on">"Кнопка «Стварыць» заменена кнопкай «Апавяшчэнні»
Заўвага: Уключэнне гэтага таксама прымусова схавае відэарэкламу"</string>
<string name="revanced_switch_create_with_notifications_button_summary_off">Кнопка \"Стварыць\" не ўзаемадзейнічае з кнопкай \"Апавяшчэнні\"</string>
<string name="revanced_hide_navigation_button_labels_title">Схаваць меткі кнопак навігацыі</string>
<string name="revanced_hide_navigation_button_labels_summary_on">Цэтлікі схаваныя</string>
<string name="revanced_hide_navigation_button_labels_summary_off">Этыкеткі паказаны</string>
<string name="revanced_disable_translucent_status_bar_title">Адключыць празрыстую панэль стану</string>
<string name="revanced_disable_translucent_status_bar_summary_on">Панэль стану непразрыстая.</string>
<string name="revanced_disable_translucent_status_bar_summary_off">Панэль стану няпразрыстая ці празрыстая.</string>
<string name="revanced_disable_translucent_navigation_bar_light_title">Адключыць светлую празрыстую панэль</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_on">Панэль навігацыі ў светлым рэжыме непразрыстая</string>
<string name="revanced_disable_translucent_navigation_bar_light_summary_off">Панэль навігацыі ў светлай тэме няпразрыстая ці празрыстая.</string>
<string name="revanced_disable_translucent_navigation_bar_dark_title">Адключыць цёмную непразрыстую панэль.</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_on">Панэль навігацыі ў цёмным рэжыме непразрыстая</string>
<string name="revanced_disable_translucent_navigation_bar_dark_summary_off">Панэль навігацыі ў цёмнай тэме няпразрыстая.</string>
</patch>
<patch id="layout.hide.player.flyoutmenupanel.hidePlayerFlyoutMenuPatch">
<string name="revanced_hide_player_flyout_title">Выпадаючае меню</string>
@@ -474,6 +538,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_player_flyout_additional_settings_summary_on">Меню дадатковых налад схавана</string>
<string name="revanced_hide_player_flyout_additional_settings_summary_off">Адлюструецца меню дадатковых налад</string>
<!-- 'Sleep timer' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_sleep_timer_title">Схаваць таймер сну</string>
<string name="revanced_hide_player_flyout_sleep_timer_summary_on">Меню таймера сну схавана</string>
<string name="revanced_hide_player_flyout_sleep_timer_summary_off">Меню таймера сну паказана</string>
<!-- 'Loop video' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_loop_video_title">Схаваць цыкл відэа</string>
<string name="revanced_hide_player_flyout_loop_video_summary_on">Меню цыклічнага відэа схавана</string>
@@ -482,6 +549,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_player_flyout_ambient_mode_title">Схаваць неактыўны рэжым</string>
<string name="revanced_hide_player_flyout_ambient_mode_summary_on">Меню неактыўнага рэжыму схавана</string>
<string name="revanced_hide_player_flyout_ambient_mode_summary_off">Адлюстроўваецца меню рэжыму навакольнага асяроддзя</string>
<string name="revanced_hide_player_flyout_stable_volume_title">Схаваць стабільны гук</string>
<string name="revanced_hide_player_flyout_stable_volume_summary_off">Меню стабільнага гуку паказана</string>
<string name="revanced_hide_player_flyout_stable_volume_summary_on">Меню стабільнага гуку схавана</string>
<!-- 'Help & feedback' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_help_title">Схаваць Даведку &amp; зваротная сувязь</string>
<string name="revanced_hide_player_flyout_help_summary_on">Дапамога &amp; меню зваротнай сувязі схавана</string>
@@ -506,7 +576,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<!-- 'Watch in VR' should be translated using the same localized wording YouTube displays for the menu item. -->
<string name="revanced_hide_player_flyout_watch_in_vr_title">Схаваць гадзіннік у VR</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">Меню прагляду ў VR схавана</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_off">Паказана меню \"Глядзець у VR\".</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_off">Паказана меню \"Глядзець у VR\"</string>
<string name="revanced_hide_player_flyout_video_quality_footer_title">Схаваць калонтытул меню якасці відэа</string>
<string name="revanced_hide_player_flyout_video_quality_footer_summary_on">Ніжні калонтытул меню якасці відэа схаваны</string>
<string name="revanced_hide_player_flyout_video_quality_footer_summary_off">Паказваецца ніжні калонтытул меню якасці відэа</string>
@@ -519,7 +589,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_cast_button_summary_on">Кнопка Cast схавана</string>
<string name="revanced_hide_cast_button_summary_off">Паказана кнопка Cast</string>
<!-- This button does not display any text, but 'captions' should be translated using the same wording used as the translation of 'revanced_hide_player_flyout_captions_title' -->
<string name="revanced_hide_captions_button_title">Кнопка «Схаваць цітры».</string>
<string name="revanced_hide_captions_button_title">Кнопка «Схаваць цітры»</string>
<string name="revanced_hide_captions_button_summary_on">Кнопка субцітраў схавана</string>
<string name="revanced_hide_captions_button_summary_off">Паказана кнопка субцітраў</string>
<string name="revanced_hide_autoplay_button_title">Схаваць кнопку аўтазапуску</string>
@@ -555,16 +625,27 @@ This is because Crowdin requires temporarily flattening this file and removing t
<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>
<!-- 'home' should be translated using the same localized wording YouTube displays for the home tab. -->
<string name="revanced_hide_shorts_home_title">Схаваць шорты ў хатняй стужцы</string>
<string name="revanced_hide_shorts_home_summary_on">Shorts у стужцы хатняй старонкі схаваны</string>
<string name="revanced_hide_shorts_home_summary_off">Shorts у стужцы хатняй старонкі паказаны</string>
<!-- 'subscription' should be translated using the same localized wording YouTube displays for the subscription tab. -->
<string name="revanced_hide_shorts_subscriptions_title">Схаваць Shorts у стужцы падпіскі</string>
<string name="revanced_hide_shorts_subscriptions_summary_on">Shorts у стужцы падпіскі схаваны</string>
<string name="revanced_hide_shorts_subscriptions_summary_off">Shorts у стужцы падпіскі паказаны</string>
<string name="revanced_hide_shorts_search_title">Схаваць Shorts у выніках пошуку</string>
<string name="revanced_hide_shorts_search_summary_on">Shorts у выніках пошуку схаваны</string>
<string name="revanced_hide_shorts_search_summary_off">Shorts у выніках пошуку паказаны</string>
<!-- 'join' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_join_button_title">Схаваць кнопку далучыцца</string>
<string name="revanced_hide_shorts_join_button_summary_on">Кнопка «Далучыцца» схавана</string>
<string name="revanced_hide_shorts_join_button_summary_off">Паказана кнопка «Далучыцца».</string>
<string name="revanced_hide_shorts_join_button_summary_off">Паказана кнопка «Далучыцца»</string>
<!-- 'subscribe' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_subscribe_button_title">Схаваць кнопку падпісацца</string>
<string name="revanced_hide_shorts_subscribe_button_summary_on">Кнопка \"Падпісацца\" схавана</string>
<string name="revanced_hide_shorts_subscribe_button_summary_off">Паказана кнопка «Падпісацца».</string>
<string name="revanced_hide_shorts_subscribe_button_summary_off">Паказана кнопка «Падпісацца»</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_title">Схаваць прыпыненыя кнопкі накладання</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_on">Прыпыненыя кнопкі накладання схаваны</string>
<string name="revanced_hide_shorts_paused_overlay_buttons_summary_off">Паказваюцца прыпыненыя кнопкі накладання</string>
@@ -573,33 +654,54 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_shorts_shop_button_summary_off">Паказваецца кнопка крамы</string>
<string name="revanced_hide_shorts_super_thanks_button_title">Схаваць кнопку супердзякуй</string>
<string name="revanced_hide_shorts_super_thanks_button_summary_on">Кнопка супердзякуй схавана</string>
<string name="revanced_hide_shorts_super_thanks_button_summary_off">Паказана кнопка \"Супер дзякуй\".</string>
<string name="revanced_hide_shorts_super_thanks_button_summary_off">Паказана кнопка \"Супер дзякуй\"</string>
<string name="revanced_hide_shorts_tagged_products_title">Схаваць пазначаныя прадукты</string>
<string name="revanced_hide_shorts_tagged_products_summary_on">Пазначаныя прадукты схаваны</string>
<string name="revanced_hide_shorts_tagged_products_summary_off">Прадукты з тэгамі паказаны</string>
<string name="revanced_hide_shorts_location_label_title">Схаваць метку месцазнаходжання</string>
<string name="revanced_hide_shorts_location_label_summary_on">Метка месцазнаходжання схавана</string>
<string name="revanced_hide_shorts_location_label_summary_off">Паказана метка месцазнаходжання</string>
<string name="revanced_hide_shorts_save_sound_button_title">Схаваць кнопку захавання музыкі</string>
<string name="revanced_hide_shorts_save_sound_button_summary_on">Кнопка захавання музыкі схавана</string>
<string name="revanced_hide_shorts_save_sound_button_summary_off">Кнопка захавання музыкі паказана</string>
<string name="revanced_hide_shorts_use_template_button_title">Схаваць кнопку выкарыстання шаблону</string>
<string name="revanced_hide_shorts_use_template_button_summary_on">Кнопка выкарыстання шаблону схавана</string>
<string name="revanced_hide_shorts_use_template_button_summary_off">Кнопка выкарыстання шаблону паказана</string>
<string name="revanced_hide_shorts_upcoming_button_title">Схаваць кнопку будучых</string>
<string name="revanced_hide_shorts_upcoming_button_summary_on">Кнопка ⬆️Будущие ролики⬆️ скрыта</string>
<string name="revanced_hide_shorts_upcoming_button_summary_off">Кнопка ⬆️Будущие ролики⬆️ отображается</string>
<string name="revanced_hide_shorts_green_screen_button_title">Скрыть кнопку с зелёным экраном Shorts</string>
<string name="revanced_hide_shorts_green_screen_button_summary_on">Кнопка с зелёным экраном Shorts скрыта</string>
<string name="revanced_hide_shorts_green_screen_button_summary_off">Кнопка с зелёным экраном Shorts отображается</string>
<string name="revanced_hide_shorts_hashtag_button_title">Скрыть хештег-кнопку Shorts</string>
<string name="revanced_hide_shorts_hashtag_button_summary_on">Хештег-кнопка Shorts скрыта</string>
<string name="revanced_hide_shorts_hashtag_button_summary_off">Хештег-кнопка Shorts отображается</string>
<string name="revanced_hide_shorts_search_suggestions_title">Схаваць прапановы пошуку</string>
<string name="revanced_hide_shorts_search_suggestions_summary_on">Пошукавыя прапановы схаваны</string>
<string name="revanced_hide_shorts_search_suggestions_summary_off">Паказваюцца прапановы пошуку</string>
<string name="revanced_hide_shorts_like_button_title">Схаваць кнопку \"Падабаецца\".</string>
<string name="revanced_hide_shorts_stickers_title">Скрыть стикеры</string>
<string name="revanced_hide_shorts_stickers_summary_on">Стикеры Shorts скрыты</string>
<string name="revanced_hide_shorts_stickers_summary_off">Стикеры Shorts отображаются</string>
<string name="revanced_hide_shorts_like_fountain_title">Скрыть всплывающее окно с лайками Shorts</string>
<string name="revanced_hide_shorts_like_fountain_summary_on">Анимация всплывающего окна с лайками Shorts скрыта</string>
<string name="revanced_hide_shorts_like_fountain_summary_off">Анимация всплывающего окна с лайками Shorts отображается</string>
<string name="revanced_hide_shorts_like_button_title">Схаваць кнопку \"Падабаецца\"</string>
<string name="revanced_hide_shorts_like_button_summary_on">Кнопка \"Падабаецца\" схавана</string>
<string name="revanced_hide_shorts_like_button_summary_off">Паказана кнопка \"Падабаецца\".</string>
<string name="revanced_hide_shorts_dislike_button_title">Схаваць кнопку \"не падабаецца\".</string>
<string name="revanced_hide_shorts_like_button_summary_off">Паказана кнопка \"Падабаецца\"</string>
<string name="revanced_hide_shorts_dislike_button_title">Схаваць кнопку \"не падабаецца\"</string>
<string name="revanced_hide_shorts_dislike_button_summary_on">Кнопка \"Не падабаецца\" схавана</string>
<string name="revanced_hide_shorts_dislike_button_summary_off">Паказана кнопка \"Не падабаецца\".</string>
<string name="revanced_hide_shorts_comments_button_title">Кнопка \"Схаваць каментарыі\".</string>
<string name="revanced_hide_shorts_dislike_button_summary_off">Паказана кнопка \"Не падабаецца\"</string>
<string name="revanced_hide_shorts_comments_button_title">Кнопка \"Схаваць каментарыі\"</string>
<string name="revanced_hide_shorts_comments_button_summary_on">Кнопка каментарыяў схавана</string>
<string name="revanced_hide_shorts_comments_button_summary_off">Паказана кнопка каментарыяў</string>
<!-- 'remix' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_remix_button_title">Схаваць кнопку рэмікса</string>
<string name="revanced_hide_shorts_remix_button_summary_on">Кнопка \"Рэмікс\" схавана</string>
<string name="revanced_hide_shorts_remix_button_summary_off">Паказана кнопка \"Рэмікс\".</string>
<string name="revanced_hide_shorts_remix_button_summary_off">Паказана кнопка \"Рэмікс\"</string>
<!-- 'share' should be translated using the same localized wording YouTube displays for the button. -->
<string name="revanced_hide_shorts_share_button_title">Схаваць кнопку абагульвання</string>
<string name="revanced_hide_shorts_share_button_summary_on">Кнопка \"Падзяліцца\" схавана</string>
<string name="revanced_hide_shorts_share_button_summary_off">Паказана кнопка \"Падзяліцца\".</string>
<string name="revanced_hide_shorts_share_button_summary_off">Паказана кнопка \"Падзяліцца\"</string>
<string name="revanced_hide_shorts_info_panel_title">Схаваць інфармацыйную панэль</string>
<string name="revanced_hide_shorts_info_panel_summary_on">Інфармацыйная панэль схавана</string>
<string name="revanced_hide_shorts_info_panel_summary_off">Паказана інфармацыйная панэль</string>
@@ -615,7 +717,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_shorts_full_video_link_label_title">Схаваць поўную метку спасылкі на відэа</string>
<string name="revanced_hide_shorts_full_video_link_label_summary_on">Метка спасылкі на відэа схавана</string>
<string name="revanced_hide_shorts_full_video_link_label_summary_off">Адлюструецца метка спасылкі на відэа</string>
<string name="revanced_hide_shorts_sound_button_title">Кнопка \"Схаваць гук\".</string>
<string name="revanced_hide_shorts_sound_button_title">Кнопка \"Схаваць гук\"</string>
<string name="revanced_hide_shorts_sound_button_summary_on">Кнопка гуку схавана</string>
<string name="revanced_hide_shorts_sound_button_summary_off">Паказана кнопка гуку</string>
<string name="revanced_hide_shorts_navigation_bar_title">Схаваць панэль навігацыі</string>
@@ -637,6 +739,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_hide_player_popup_panels_summary_on">Усплывальныя панэлі прайгравальніка схаваныя</string>
<string name="revanced_hide_player_popup_panels_summary_off">Паказваюцца ўсплывальныя панэлі прайгравальніка</string>
</patch>
<patch id="layout.player.fullscreen.openVideosFullscreen">
<string name="revanced_open_videos_fullscreen_portrait_title">Открывать видео на весь экран в портретном режиме</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_on">Видео открываются на весь экран</string>
<string name="revanced_open_videos_fullscreen_portrait_summary_off">Видео не открываются на весь экран</string>
</patch>
<patch id="layout.player.overlay.customPlayerOverlayOpacityResourcePatch">
<string name="revanced_player_overlay_opacity_title">Непразрыстасць накладання прайгравальніка</string>
<string name="revanced_player_overlay_opacity_summary">Значэнне непразрыстасці паміж 0-100, дзе 0 - празрысты</string>
@@ -649,12 +756,14 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_ryd_failure_client_rate_limit_requested">Дызлайкі недаступныя (дасягнуты ліміт кліенцкага API)</string>
<string name="revanced_ryd_failure_generic">Не падабаецца (%s)</string>
<!-- Toast shown if the user enables RYD while a video is opened, and then tries to vote for the video. -->
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">Перазагрузіце відэа, каб прагаласаваць з дапамогай функцыі \"Вярнуць не падабаецца YouTube\".</string>
<string name="revanced_ryd_failure_ryd_enabled_while_playing_video_then_user_voted">Перазагрузіце відэа, каб прагаласаваць з дапамогай функцыі \"Вярнуць не падабаецца YouTube\"</string>
<string name="revanced_ryd_enable_summary_on">Дызлайкі паказаны</string>
<string name="revanced_ryd_enable_summary_off">Дызлайкі не паказваюцца</string>
<string name="revanced_ryd_shorts_title">Паказвайце \"не падабаецца\" на Shorts</string>
<string name="revanced_ryd_shorts_summary_on">Адзнакі \"не падабаецца\" ў Shorts</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">Адзнакі \"Не падабаецца\" ў Shorts\n\nАбмежаванне: адзнакі \"Не падабаецца\" могуць не з\"яўляцца ў рэжыме інкогніта</string>
<string name="revanced_ryd_shorts_summary_on_disclaimer">"Непападання паказаны на Shorts
Абмежаванне: Непападання могуць не з'яўляцца ў рэжыме інкогніта"</string>
<string name="revanced_ryd_shorts_summary_off">Дызлакі схаваныя ў Shorts</string>
<string name="revanced_ryd_dislike_percentage_title">Дызлайкі ў працэнтах</string>
<string name="revanced_ryd_dislike_percentage_summary_on">Неўпадабанні паказаны ў працэнтах</string>
@@ -692,6 +801,16 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_wide_searchbar_summary_off">Шырокая панэль пошуку адключана</string>
</patch>
<patch id="layout.seekbar.seekbarThumbnailsPatch">
<string name="revanced_seekbar_thumbnails_high_quality_title">Включить миниатюры высокого качества</string>
<string name="revanced_seekbar_thumbnails_high_quality_summary_on">Миниатюры ползунка прогресса — высокого качества</string>
<string name="revanced_seekbar_thumbnails_high_quality_summary_off">Миниатюры ползунка прогресса — среднего качества</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_on">Миниатюры ползунка прогресса в полноэкранном режиме — высокого качества</string>
<string name="revanced_seekbar_thumbnails_high_quality_legacy_summary_off">Миниатюры ползунка прогресса в полноэкранном режиме — среднего качества</string>
<string name="revanced_seekbar_thumbnails_high_quality_dialog_message">"Гэта таксама аднавіць мініатюры на трансляцыях у прамым эфіры, якія не маюць мініатюр з магчымасцю перамоткі.
Мініатюры з магчымасцю перамоткі будуць выкарыстоўваць тую ж якасць, што і бягучае відэа.
Гэтая функцыя лепш за ўсё працуе з якасцю відэа 720p або ніжэй і пры выкарыстанні вельмі хуткага Інтэрнэт-злучэння."</string>
<string name="revanced_restore_old_seekbar_thumbnails_title">Аднавіць старыя мініяцюры панэлі пошуку</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_on">Эскізы панэлі пошуку з\"явяцца над панэллю пошуку</string>
<string name="revanced_restore_old_seekbar_thumbnails_summary_off">Мініяцюры панэлі пошуку з\"явяцца ў поўнаэкранным рэжыме</string>
@@ -718,7 +837,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_general_time_without_sum_off">Паказана поўная даўжыня відэа</string>
<string name="revanced_sb_create_segment_category">Стварэнне новых сегментаў</string>
<string name="revanced_sb_enable_create_segment">Паказаць кнопку стварэння новага сегмента</string>
<string name="revanced_sb_enable_create_segment_sum_on">Паказана кнопка \"Стварыць новы сегмент\".</string>
<string name="revanced_sb_enable_create_segment_sum_on">Паказана кнопка \"Стварыць новы сегмент\"</string>
<string name="revanced_sb_enable_create_segment_sum_off">Кнопка \"Стварыць новы сегмент\" не паказваецца</string>
<string name="revanced_sb_general_adjusting">Адрэгулюйце новы крок сегмента</string>
<string name="revanced_sb_general_adjusting_sum">Колькасць мілісекунд, на якую перамяшчаюцца кнопкі рэгулявання часу пры стварэнні новых сегментаў</string>
@@ -738,6 +857,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_general_skipcount_sum_off">Адсочванне колькасці пропускаў не ўключана</string>
<string name="revanced_sb_general_min_duration">Мінімальная працягласць сегмента</string>
<string name="revanced_sb_general_min_duration_sum">Сегменты, карацейшыя за гэта значэнне (у секундах), не будуць паказвацца або прапускацца</string>
<string name="revanced_sb_general_min_duration_invalid">Неверная продолжительность</string>
<string name="revanced_sb_general_uuid">Ваш асабісты ідэнтыфікатар карыстальніка</string>
<string name="revanced_sb_general_uuid_sum">Гэта павінна быць прыватным. Гэта як пароль, і яго нельга нікому паведамляць. Калі ў кагосьці гэта ёсць, ён можа выдаваць сябе за вас</string>
<string name="revanced_sb_general_uuid_invalid">Прыватны ідэнтыфікатар карыстальніка павінен быць не менш за 30 сімвалаў</string>
@@ -753,7 +873,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_settings_import_successful">Налады паспяхова імпартаваны</string>
<string name="revanced_sb_settings_import_failed">Не ўдалося імпартаваць: %s</string>
<string name="revanced_sb_settings_export_failed">Не ўдалося экспартаваць: %s</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning">Вашы налады ўтрымліваюць прыватны ідэнтыфікатар карыстальніка SponsorBlock.\n\nВаш ідэнтыфікатар карыстальніка падобны на пароль, і яго ніколі не трэба раскрываць.\n</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning">"Вашы налады ўтрымліваюць асабісты ідэнтыфікатар SponsorBlock.
Ваш ідэнтыфікатар карыстальніка падобны да пароля, і яго ніколі не варта дзяліцца.
"</string>
<string name="revanced_sb_settings_revanced_export_user_id_warning_dismiss">Больш не паказваць</string>
<string name="revanced_sb_diff_segments">Змяніць паводзіны сегмента</string>
<string name="revanced_sb_segments_sponsor">Спонсар</string>
@@ -813,9 +937,10 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_submit_failed_invalid">Немагчыма адправіць сегмент: %s</string>
<string name="revanced_sb_submit_failed_timeout">SponsorBlock часова не працуе</string>
<string name="revanced_sb_submit_failed_unknown_error">Немагчыма адправіць сегмент (статус: %1$d %2$s)</string>
<string name="revanced_sb_submit_failed_rate_limit">Немагчыма адправіць сегмент.\nАцэнка абмежаваная (занадта шмат ад аднаго карыстальніка або IP)</string>
<string name="revanced_sb_submit_failed_rate_limit">Немагчыма адправіць сегмент. Абмежаванне па хуткасці (занадта шмат ад аднаго і таго ж карыстальніка або IP-адраса)</string>
<string name="revanced_sb_submit_failed_forbidden">Немагчыма адправіць сегмент: %s</string>
<string name="revanced_sb_submit_failed_duplicate">Немагчыма адправіць сегмент.\nУжо існуе</string>
<string name="revanced_sb_submit_failed_duplicate">"Немагчыма адправіць сегмент.
Ужо існуе"</string>
<string name="revanced_sb_submit_succeeded">Сегмент паспяхова адпраўлены</string>
<!-- Toast shown if network connection times out. Translations of this should not be longer than the original English or the text can be clipped and not entirely shown. -->
<string name="revanced_sb_sponsorblock_connection_failure_timeout">SponsorBlock часова недаступны (час чакання API скончыўся)</string>
@@ -838,7 +963,15 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_new_segment_time_start">Час пачатку сегмента</string>
<string name="revanced_sb_new_segment_time_end">Час заканчэння сегмента</string>
<string name="revanced_sb_new_segment_confirm_title">Ці правільны час?</string>
<string name="revanced_sb_new_segment_confirm_content">Сегмент ад\n\n%1$s\nда\n%2$s\n\n(%3$s)\n\nГатовы адправіць?</string>
<string name="revanced_sb_new_segment_confirm_content">"Сэгмэнт знаходзіцца паміж
%1$s
да
%2$s
(%3$s)
Гатовыя адправіць?"</string>
<string name="revanced_sb_new_segment_start_is_before_end">Пачынаць трэба раней за канец</string>
<string name="revanced_sb_new_segment_mark_locations_first">Спачатку адзначце два месцы на панэлі часу</string>
<string name="revanced_sb_new_segment_preview_segment_first">Папярэдні прагляд сегмента і пераканайцеся, што ён праходзіць плаўна</string>
@@ -856,6 +989,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_sb_stats_username_changed">Імя карыстальніка паспяхова зменена</string>
<string name="revanced_sb_stats_reputation">Ваша рэпутацыя &lt;b&gt;%.2f&lt;/b&gt;</string>
<string name="revanced_sb_stats_submissions">Вы стварылі &lt;b&gt;%s&lt;/b&gt; сегменты</string>
<string name="revanced_sb_stats_submissions_sum">Коснитесь, чтобы просмотреть ваши сегменты</string>
<string name="revanced_sb_stats_saved_zero">Табліца лідэраў SponsorBlock</string>
<string name="revanced_sb_stats_saved">Вы выратавалі людзей з &lt;b&gt;%s&lt;/b&gt; сегменты</string>
<string name="revanced_sb_stats_saved_sum_zero">Націсніце тут, каб убачыць глабальную статыстыку і вядучых удзельнікаў</string>
@@ -879,10 +1013,15 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_spoof_app_version_title">Версія праграмы Spoof</string>
<string name="revanced_spoof_app_version_summary_on">Версія падробленая</string>
<string name="revanced_spoof_app_version_summary_off">Версія не падробленая</string>
<string name="revanced_spoof_app_version_user_dialog_message">Версія праграмы будзе падменена на старую версію YouTube.\n\nГэта зменіць знешні выгляд і функцыі праграмы, але могуць узнікнуць невядомыя пабочныя эфекты.\n\nКалі пазней выключыць, рэкамендуецца ачысціць даныя праграмы, каб прадухіліць памылкі карыстацкага інтэрфейсу.</string>
<string name="revanced_spoof_app_version_user_dialog_message">"Версія прыкладання будзе падроблены да старэйшай версіі YouTube.
Гэта зменіць знешні выгляд і функцыі прыкладання, але могуць узнікнуць невядомыя пабочныя эфекты.
Калі пазней будзе адключана, рэкамендуецца ачысціць даныя прыкладання, каб пазбегнуць памылак у інтэрфейсе."</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>
<!-- 'RYD' is 'Return YouTube Dislike' -->
<string name="revanced_spoof_app_version_target_legacy_entry_1">18.33.40 - Аднаўленне RYD на Shorts у рэжыме інкогніта</string>
<string name="revanced_spoof_app_version_target_legacy_entry_2">18.20.39 - Аднавіць хуткасць шырокага відэа &amp; якаснае меню</string>
@@ -892,12 +1031,20 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="layout.startpage.changeStartPagePatch">
<string name="revanced_change_start_page_title">Усталяваць стартавую старонку</string>
<string name="revanced_change_start_page_entry_default">Па змаўчанні</string>
<string name="revanced_change_start_page_entry_browse">Обзор каналов</string>
<string name="revanced_change_start_page_entry_explore">Дасьледуйце</string>
<string name="revanced_change_start_page_entry_gaming">Игры</string>
<string name="revanced_change_start_page_entry_history">Гісторыя</string>
<string name="revanced_change_start_page_entry_library">Библиотека</string>
<string name="revanced_change_start_page_entry_liked_videos">Спадабаліся відэа</string>
<string name="revanced_change_start_page_entry_live">В прямом эфире</string>
<string name="revanced_change_start_page_entry_movies">Фильмы</string>
<string name="revanced_change_start_page_entry_music">Музыка</string>
<string name="revanced_change_start_page_entry_search">Пошук</string>
<string name="revanced_change_start_page_entry_sports">Спорт</string>
<string name="revanced_change_start_page_entry_subscriptions">Падпіскі</string>
<string name="revanced_change_start_page_entry_trending">У трэндзе</string>
<string name="revanced_change_start_page_entry_watch_later">Посмотреть позже</string>
</patch>
<patch id="layout.startupshortsreset.disableResumingShortsOnStartupPatch">
<string name="revanced_disable_resuming_shorts_player_title">Адключыць аднаўленне прайгравання Shorts</string>
@@ -905,6 +1052,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_disable_resuming_shorts_player_summary_off">Прайгравальнік Shorts адновіцца пры запуску праграмы</string>
</patch>
<patch id="layout.shortsautoplay.shortsAutoplayPatch">
<string name="revanced_shorts_autoplay_title">Автовоспроизведение Shorts</string>
<string name="revanced_shorts_autoplay_summary_on">Shorts будут воспроизводиться автоматически</string>
<string name="revanced_shorts_autoplay_summary_off">Shorts будут повторяться</string>
<string name="revanced_shorts_autoplay_background_title">Автовоспроизведение Shorts в фоновом режиме</string>
<string name="revanced_shorts_autoplay_background_summary_on">Shorts в фоновом режиме будут воспроизводиться автоматически</string>
<string name="revanced_shorts_autoplay_background_summary_off">Shorts в фоновом режиме будут повторяться</string>
</patch>
<patch id="layout.tablet.enableTabletLayoutPatch">
<string name="revanced_tablet_layout_title">Уключыць макет планшэта</string>
@@ -917,12 +1070,38 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_miniplayer_screen_summary">Змяніце стыль мінімізаванага плэера ў праграме</string>
<string name="revanced_miniplayer_type_title">Тып мініплэера</string>
<string name="revanced_miniplayer_type_entry_0">Інваліды</string>
<string name="revanced_miniplayer_type_entry_1">Арыгінал</string>
<string name="revanced_miniplayer_type_entry_1">Па змаўчанні</string>
<string name="revanced_miniplayer_type_entry_2">Минимальный</string>
<string name="revanced_miniplayer_type_entry_3">Планшэт</string>
<string name="revanced_miniplayer_type_entry_4">Сучасны 1</string>
<string name="revanced_miniplayer_type_entry_5">Сучасны 2</string>
<string name="revanced_miniplayer_type_entry_6">Сучасны 3</string>
<string name="revanced_miniplayer_rounded_corners_title">Включить закругленные углы</string>
<string name="revanced_miniplayer_rounded_corners_summary_on">Углы закруглены</string>
<string name="revanced_miniplayer_rounded_corners_summary_off">Углы квадратные</string>
<string name="revanced_miniplayer_double_tap_action_title">Включить двойное нажатие и масштабирование с помощьющипка</string>
<string name="revanced_miniplayer_double_tap_action_summary_on">"Уключана дзеянне «подвойнае націсканне» і «шчыпкі для змены памеру»
• Подвойнае націсканне, каб павялічыць памер міні-прайгравальніка
• Подвойнае націсканне яшчэ раз, каб аднавіць першапачатковы памер"</string>
<string name="revanced_miniplayer_double_tap_action_summary_off">Двойное нажатие и scalewithpinch отключены</string>
<string name="revanced_miniplayer_drag_and_drop_title">Включить перетаскивание</string>
<string name="revanced_miniplayer_drag_and_drop_summary_on">"Уключана перацягванне і кіданне
Міні-прайгравальнік можна перацягнуць у любы куток экрана"</string>
<string name="revanced_miniplayer_drag_and_drop_summary_off">Перетаскивание отключено</string>
<string name="revanced_miniplayer_horizontal_drag_title">Включить горизонтальный жест перетаскивания</string>
<string name="revanced_miniplayer_horizontal_drag_summary_on">"Уключаны жэст гарызантальнага перацягвання
Міні-прайгравальнік можна перацягнуць за межы экрана ўлева ці ўправа"</string>
<string name="revanced_miniplayer_horizontal_drag_summary_off">Горизонтальный жест перетаскивания отключен</string>
<string name="revanced_miniplayer_hide_expand_close_title">Скрыть кнопку закрытия</string>
<string name="revanced_miniplayer_hide_expand_close_summary_on">Кнопка закрытия скрыта</string>
<string name="revanced_miniplayer_hide_expand_close_summary_off">Кнопка закрытия отображается</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_title">Схаваць кнопкі разгортвання і закрыцця</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_on">"Кнопкі схаваны
Працягвайце пальцам, каб разгарнуць або закрыць"</string>
<string name="revanced_miniplayer_hide_expand_close_legacy_summary_off">Паказваюцца кнопкі разгарнуць і закрыць</string>
<string name="revanced_miniplayer_hide_subtext_title">Схаваць падтэксты</string>
<string name="revanced_miniplayer_hide_subtext_summary_on">Падтэксты схаваныя</string>
@@ -930,6 +1109,9 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_miniplayer_hide_rewind_forward_title">Схаваць кнопкі пераходу наперад і назад</string>
<string name="revanced_miniplayer_hide_rewind_forward_summary_on">Пераход наперад і назад схаваны</string>
<string name="revanced_miniplayer_hide_rewind_forward_summary_off">Паказваецца пераход наперад і назад</string>
<string name="revanced_miniplayer_width_dip_title">Размер по умолчанию</string>
<string name="revanced_miniplayer_width_dip_summary">Начальная размер на экране в пикселях</string>
<string name="revanced_miniplayer_width_dip_invalid_toast">Размер пикселя должен быть в пределах %1$s и %2$s</string>
<string name="revanced_miniplayer_opacity_title">Непразрыстасць накладання</string>
<string name="revanced_miniplayer_opacity_summary">Значэнне непразрыстасці паміж 0-100, дзе 0 - празрысты</string>
<string name="revanced_miniplayer_opacity_invalid_toast">Непразрыстасць накладання міні-плэера павінна быць ад 0 да 100</string>
@@ -945,11 +1127,14 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_seekbar_custom_color_summary_off">Паказаны зыходны колер панэлі пошуку</string>
<string name="revanced_seekbar_custom_color_value_title">Карыстальніцкі колер панэлі пошуку</string>
<string name="revanced_seekbar_custom_color_value_summary">Колер панэлі пошуку</string>
<string name="revanced_seekbar_custom_color_invalid">Неверное значение цвета ползунка прогресса</string>
</patch>
<patch id="layout.thumbnails.bypassImageRegionRestrictionsPatch">
<string name="revanced_bypass_image_region_restrictions_title">Абыход абмежаванняў рэгіёну</string>
<string name="revanced_bypass_image_region_restrictions_summary_on">Выкарыстанне хаста відарысаў yt4.ggpht.com</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">Выкарыстанне арыгінальнага хаста відарысаў\n\nУключэнне гэтай опцыі можа выправіць адсутнічаючыя відарысы, якія заблакіраваныя ў некаторых рэгіёнах</string>
<string name="revanced_bypass_image_region_restrictions_summary_off">"Выкарыстоўваецца арыгінальны хост малюнкаў
Уключэнне гэтага можа выправіць праблему з адсутнасцю малюнкаў, якія заблакаваны ў некаторых рэгіёнах"</string>
</patch>
<patch id="layout.thumbnails.alternativeThumbnailsPatch">
<!-- 'Home' should be translated using the same localized wording YouTube displays for the home tab. -->
@@ -964,7 +1149,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_alt_thumbnail_options_entry_2">DeArrow &amp; Арыгінальныя мініяцюры</string>
<string name="revanced_alt_thumbnail_options_entry_3">DeArrow &amp; Усё ж захоплівае</string>
<string name="revanced_alt_thumbnail_options_entry_4">Усё ж захоплівае</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">DeArrow прадастаўляе краўдсорсінгавыя мініяцюры для відэа YouTube. Гэтыя мініяцюры часта больш рэлевантныя, чым тыя, якія прадастаўляе YouTube\n\nКалі яны ўключаны, URL-адрасы відэа будуць адпраўляцца на сервер API, а іншыя даныя не адпраўляюцца. Калі відэа не мае мініяцюр DeArrow, то паказваюцца арыгінальныя або фотаздымкі\n\nНацісніце тут, каб даведацца больш пра DeArrow</string>
<string name="revanced_alt_thumbnail_dearrow_about_summary">"DeArrow забяспечвае мініатюры для відэа YouTube, якія збіраюцца ад карыстальнікаў. Гэтыя мініатюры часта больш актуальныя, чым тыя, якія прадастаўляецца YouTube.
Калі ўключана, URL відэа будуць адпраўлены на сервер API, і ніякія іншыя даныя не адпраўляюцца. Калі відэа не мае мініатюр DeArrow, то паказваюцца арыгінальныя або здымкі.
Націсніце тут, каб даведацца больш пра DeArrow"</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_title">Паказаць тост, калі API недаступны</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_on">Тост паказваецца, калі DeArrow недаступны</string>
<string name="revanced_alt_thumbnail_dearrow_connection_toast_summary_off">Тост не паказваецца, калі DeArrow недаступны</string>
@@ -993,6 +1182,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="misc.dns.checkWatchHistoryDomainNameResolutionPatch">
<string name="revanced_check_watch_history_domain_name_dialog_title">Увага</string>
<string name="revanced_check_watch_history_domain_name_dialog_message">Ваша гісторыя прагляду не захоўваецца.&lt;br&gt;&lt;br&gt;Гэта, хутчэй за ўсё, выклікана DNS-блакіроўшчыкам рэкламы або сеткавым праксі.&lt;br&gt;&lt;br&gt;Каб выправіць гэта, дадайце &lt;b&gt;s.youtube.com&lt;/b&gt; у белы спіс або адключыце ўсе DNS-блакіроўшчыкі і праксі.</string>
<string name="revanced_check_watch_history_domain_name_dialog_ignore">Больш не паказваць</string>
</patch>
<patch id="misc.autorepeat.autoRepeatPatch">
@@ -1002,8 +1192,12 @@ This is because Crowdin requires temporarily flattening this file and removing t
</patch>
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
<string name="revanced_spoof_device_dimensions_title">Памеры падманнага прылады</string>
<string name="revanced_spoof_device_dimensions_summary_on">Памеры прылады падробленыя\n\nМожа быць разблакіравана больш высокая якасць відэа, але вы можаце сутыкнуцца з затрымкамі пры прайграванні, пагаршэннем часу аўтаномнай працы і невядомымі пабочнымі эфектамі</string>
<string name="revanced_spoof_device_dimensions_summary_off">Памеры прылады не падробленыя\n\nУключэнне гэтага можа разблакіраваць больш высокую якасць відэа</string>
<string name="revanced_spoof_device_dimensions_summary_on">"Памеры прылады падроблены
Магчыма, будуць разблакаваны больш высокія якасці відэа, але вы можаце сутыкнуцца з заіканнем відэа, горшым тэрмінам службы батарэі і невядомымі пабочнымі эфектамі"</string>
<string name="revanced_spoof_device_dimensions_summary_off">"Памеры прылады не падроблены
Уключэнне гэтага можа разблакаваць больш высокія якасці відэа"</string>
<string name="revanced_spoof_device_dimensions_user_dialog_message">Уключэнне гэтага можа прывесці да прыпынкаў прайгравання відэа, пагаршэння тэрміну службы батарэі і невядомых пабочных эфектаў.</string>
</patch>
<patch id="misc.gms.gmsCoreSupportResourcePatch">
@@ -1030,6 +1224,11 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_disable_zoom_haptics_summary_on">Тактыльныя функцыі адключаны</string>
<string name="revanced_disable_zoom_haptics_summary_off">Тактыльныя сігналы ўключаны</string>
</patch>
<patch id="video.audio.forceOriginalAudioPatch">
<string name="revanced_force_original_audio_title">Вымушанае арыгінальнае аўдыё</string>
<string name="revanced_force_original_audio_summary_on">Выкарыстанне арыгінальнага аўдыё</string>
<string name="revanced_force_original_audio_summary_off">Выкарыстанне аўдыё па змаўчанні</string>
</patch>
<patch id="video.quality.rememberVideoQualityPatch">
<!-- Translations should use the same text as revanced_custom_playback_speeds_auto -->
<string name="revanced_video_quality_default_entry_1">Аўто</string>
@@ -1039,7 +1238,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_video_quality_default_wifi_title">Стандартная якасць відэа ў сетцы Wi-Fi</string>
<string name="revanced_video_quality_default_mobile_title">Стандартная якасць відэа ў мабільнай сетцы</string>
<string name="revanced_remember_video_quality_mobile">мабільны</string>
<string name="revanced_remember_video_quality_wifi">Wi-Fi</string>
<string name="revanced_remember_video_quality_wifi">wi-fi</string>
<string name="revanced_remember_video_quality_toast">Стандартная якасць %1$s зменена на: %2$s</string>
</patch>
<patch id="video.speed.button.playbackSpeedButtonPatch">
@@ -1048,9 +1247,13 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_playback_speed_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_custom_playback_speeds_title">Карыстальніцкія хуткасці прайгравання</string>
<string name="revanced_custom_playback_speeds_invalid">Карыстальніцкія хуткасці павінны быць менш за %s. Выкарыстанне значэнняў па змаўчанні.</string>
<string name="revanced_custom_playback_speeds_parse_exception">Няправільныя карыстальніцкія хуткасці прайгравання. Выкарыстанне значэнняў па змаўчанні.</string>
<string name="revanced_custom_playback_speeds_summary">Добавьте или измените пользовательскую скорость воспроизведения</string>
<string name="revanced_custom_playback_speeds_invalid">Нестандартныя хуткасці павінны быць менш за %s</string>
<string name="revanced_custom_playback_speeds_parse_exception">Несапраўдныя нестандартныя хуткасці прайгравання</string>
<string name="revanced_custom_playback_speeds_auto">Аўто</string>
</patch>
<patch id="video.speed.remember.rememberPlaybackSpeedPatch">
@@ -1071,7 +1274,82 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_slide_to_seek_summary_off">Слайд для пошуку не ўключаны</string>
</patch>
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
<string name="revanced_spoof_video_streams_screen_title">Подделывать потоки видео</string>
<string name="revanced_spoof_video_streams_screen_summary">Имитируйте потоки видео клиентов, чтобы предотвратить проблемы с воспроизведением</string>
<string name="revanced_spoof_video_streams_title">Подделывать потоки видео</string>
<string name="revanced_spoof_video_streams_summary_on">Потоки видео подделаны</string>
<string name="revanced_spoof_video_streams_summary_off">"Відэаструм не падроблены
Прайграванне відэа можа не працаваць"</string>
<string name="revanced_spoof_video_streams_user_dialog_message">Адключэнне гэтай налады можа выклікаць праблемы з прайграваннем відэа.</string>
<string name="revanced_spoof_video_streams_client_title">Клиент по умолчанию</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">Принудительно AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Видеокодек принудительно установлен в AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Видеокодек определяется автоматически</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Уключэнне гэтага можа палепшыць тэрмін службы батарэі і выправіць заіканне відэа.
AVC мае максімальную раздзяляльнасць 1080p, аўдыякадэкар Opus недаступны, і відэа будзе выкарыстоўваць больш Інтэрнэт-даных, чым VP9 або AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_title">Пабочныя эфекты падмены iOS</string>
<string name="revanced_spoof_video_streams_about_ios_summary">"• Прыватныя дзіцячыя відэа могуць не прайгравацца
• Відэа заканчваюцца на 1 секунду раней"</string>
<string name="revanced_spoof_video_streams_about_android_vr_title">Побочные эффекты подмены Android VR</string>
<string name="revanced_spoof_video_streams_about_android_vr_summary">"• Дзіцячыя відэа могуць не прайгравацца
• Трансляцыі ў прамым эфіры пачынаюцца з пачатку
• Відэа заканчваюцца на 1 секунду раней"</string>
<string name="revanced_spoof_video_streams_language_title">Язык потока аудио по умолчанию</string>
<string name="revanced_spoof_video_streams_language_DEFAULT">Язык приложения</string>
<string name="revanced_spoof_video_streams_language_AR">Арабский</string>
<string name="revanced_spoof_video_streams_language_AZ">Азербайджанский</string>
<string name="revanced_spoof_video_streams_language_BG">Болгарский</string>
<string name="revanced_spoof_video_streams_language_BN">Бенгальский</string>
<string name="revanced_spoof_video_streams_language_CA">Каталонский</string>
<string name="revanced_spoof_video_streams_language_CS">Чешский</string>
<string name="revanced_spoof_video_streams_language_DA">Датский</string>
<string name="revanced_spoof_video_streams_language_DE">Немецкий</string>
<string name="revanced_spoof_video_streams_language_EL">Греческий</string>
<string name="revanced_spoof_video_streams_language_EN">Англійская</string>
<string name="revanced_spoof_video_streams_language_ES">Іспанская</string>
<string name="revanced_spoof_video_streams_language_ET">Эстонская</string>
<string name="revanced_spoof_video_streams_language_FA">Фарсі</string>
<string name="revanced_spoof_video_streams_language_FI">Фінская</string>
<string name="revanced_spoof_video_streams_language_FR">Французская</string>
<string name="revanced_spoof_video_streams_language_GU">Гуяраті</string>
<string name="revanced_spoof_video_streams_language_HI">Хянді</string>
<string name="revanced_spoof_video_streams_language_HR">Хорватская</string>
<string name="revanced_spoof_video_streams_language_HU">Венгерская</string>
<string name="revanced_spoof_video_streams_language_ID">Инданезійская</string>
<string name="revanced_spoof_video_streams_language_IT">Итальянская</string>
<string name="revanced_spoof_video_streams_language_JA">Японская</string>
<string name="revanced_spoof_video_streams_language_KK">Казахская</string>
<string name="revanced_spoof_video_streams_language_KO">Корэйская</string>
<string name="revanced_spoof_video_streams_language_LT">Літоуская</string>
<string name="revanced_spoof_video_streams_language_LV">Латышская</string>
<string name="revanced_spoof_video_streams_language_MK">Македонская</string>
<string name="revanced_spoof_video_streams_language_MN">Монгольская</string>
<string name="revanced_spoof_video_streams_language_MR">Маратхі</string>
<string name="revanced_spoof_video_streams_language_MS">Малайская</string>
<string name="revanced_spoof_video_streams_language_MY">Брыманская</string>
<string name="revanced_spoof_video_streams_language_NL">Нідрландский</string>
<string name="revanced_spoof_video_streams_language_OR">Орія</string>
<string name="revanced_spoof_video_streams_language_PA">Панджабский</string>
<string name="revanced_spoof_video_streams_language_PL">Польская</string>
<string name="revanced_spoof_video_streams_language_PT_BR">Партугальская (Бразілія)</string>
<string name="revanced_spoof_video_streams_language_PT_PT">Партугальская (Партугалія)</string>
<string name="revanced_spoof_video_streams_language_RO">Румынская</string>
<string name="revanced_spoof_video_streams_language_RU">Русская</string>
<string name="revanced_spoof_video_streams_language_SK">Словацкия</string>
<string name="revanced_spoof_video_streams_language_SL">Словенская</string>
<string name="revanced_spoof_video_streams_language_SR">Сербская</string>
<string name="revanced_spoof_video_streams_language_SV">Швецкая</string>
<string name="revanced_spoof_video_streams_language_SW">Суахілі</string>
<string name="revanced_spoof_video_streams_language_TA">Тамільская</string>
<string name="revanced_spoof_video_streams_language_TE">Тэлугу</string>
<string name="revanced_spoof_video_streams_language_TH">Тайская</string>
<string name="revanced_spoof_video_streams_language_TR">Турэцкая</string>
<string name="revanced_spoof_video_streams_language_UK">Украёнская</string>
<string name="revanced_spoof_video_streams_language_UR">Урду</string>
<string name="revanced_spoof_video_streams_language_VI">Віетнамская</string>
<string name="revanced_spoof_video_streams_language_ZH">Кітайская</string>
</patch>
</app>
<app id="twitch">
@@ -1081,8 +1359,8 @@ This is because Crowdin requires temporarily flattening this file and removing t
<string name="revanced_block_audio_ads_summary_off">Аўдыёрэклама разблакіравана</string>
</patch>
<patch id="ad.embedded.embeddedAdsPatch">
<string name="revanced_embedded_ads_service_unavailable">%s недаступны. Рэклама можа паказвацца. Паспрабуйце пераключыцца на іншую службу блакіроўкі рэкламы ў наладах.</string>
<string name="revanced_embedded_ads_service_failed">Сервер %s вярнуў памылку. Рэклама можа паказвацца. Паспрабуйце пераключыцца на іншую службу блакіроўкі рэкламы ў наладах.</string>
<string name="revanced_embedded_ads_service_unavailable">%s недаступны, рэклама можа паказвацца. Паспрабуйце змяніць службу блакавання рэкламы ў наладах.</string>
<string name="revanced_embedded_ads_service_failed">%s выдаў памылку, рэклама можа паказвацца. Паспрабуйце змяніць службу блакавання рэкламы ў наладах.</string>
<string name="revanced_block_embedded_ads_title">Блакіраваць убудаваную відэарэкламу</string>
<string name="revanced_block_embedded_ads_entry_1">Інваліды</string>
<string name="revanced_block_embedded_ads_entry_2">Светлавы проксі</string>
@@ -1114,6 +1392,7 @@ This is because Crowdin requires temporarily flattening this file and removing t
<patch id="misc.settings.settingsPatch">
<string name="revanced_settings">Налады ReVanced</string>
<string name="revanced_about_title">Пра нас</string>
<string name="revanced_about_summary">Раскажыць “ReVanced”</string>
<string name="revanced_ads_screen_title">Аб\"явы</string>
<string name="revanced_ads_screen_summary">Налады блакіроўкі рэкламы</string>
<string name="revanced_chat_screen_title">Чат</string>

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