Compare commits

..

73 Commits

Author SHA1 Message Date
semantic-release-bot
f304c178e2 chore: Release v5.38.0-dev.1 [skip ci]
# [5.38.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.37.1-dev.3...v5.38.0-dev.1) (2025-09-15)

### Features

* **Instagram:** Add `Hide explore feed` patch ([#5856](https://github.com/ReVanced/revanced-patches/issues/5856)) ([1d65887](1d65887e01))
2025-09-15 19:30:52 +00:00
brosssh
1d65887e01 feat(Instagram): Add Hide explore feed patch (#5856) 2025-09-15 23:28:01 +04:00
github-actions[bot]
6b6eea8414 chore: Sync translations (#5864) 2025-09-15 23:26:07 +04:00
semantic-release-bot
1db131e90e chore: Release v5.37.1-dev.3 [skip ci]
## [5.37.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.37.1-dev.2...v5.37.1-dev.3) (2025-09-15)

### Bug Fixes

* **Spoof video streams:** Remove Android TV and iOS TV clients, add experimental VisionOS, add temporary fix for `Force original audio` to work with any spoof client ([#5861](https://github.com/ReVanced/revanced-patches/issues/5861)) ([abe3943](abe3943f98))
2025-09-15 17:02:01 +00:00
LisoUseInAIKyrios
abe3943f98 fix(Spoof video streams): Remove Android TV and iOS TV clients, add experimental VisionOS, add temporary fix for Force original audio to work with any spoof client (#5861) 2025-09-15 20:58:56 +04:00
semantic-release-bot
cb6d802de3 chore: Release v5.37.1-dev.2 [skip ci]
## [5.37.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.37.1-dev.1...v5.37.1-dev.2) (2025-09-15)

### Bug Fixes

* **Instagram - Hide navigation buttons:** Support v397.1.0.52.81 ([#5855](https://github.com/ReVanced/revanced-patches/issues/5855)) ([f11d1ef](f11d1ef990))
2025-09-15 12:52:54 +00:00
brosssh
f11d1ef990 fix(Instagram - Hide navigation buttons): Support v397.1.0.52.81 (#5855) 2025-09-15 16:48:55 +04:00
semantic-release-bot
3d25da18bc chore: Release v5.37.1-dev.1 [skip ci]
## [5.37.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.37.0...v5.37.1-dev.1) (2025-09-15)

### Bug Fixes

* **YouTube Music - Spoof video streams:** Fix playback issues when using a cellular network ([fa04c8e](fa04c8eecf))
2025-09-15 12:47:02 +00:00
LisoUseInAIKyrios
fa04c8eecf fix(YouTube Music - Spoof video streams): Fix playback issues when using a cellular network
Code adapted from 5f35e51a27
2025-09-15 16:43:04 +04:00
semantic-release-bot
105f6e0e97 chore: Release v5.37.0 [skip ci]
# [5.37.0](https://github.com/ReVanced/revanced-patches/compare/v5.36.0...v5.37.0) (2025-09-15)

### Bug Fixes

* **Instagram - Hide navigation buttons:** Add constrain to known working version ([e6c79f1](e6c79f1383))
* Resolve patching with dev branch ([09b941a](09b941abf0))
* **Spotify:** Remove broken `Spoof client` patch ([#5833](https://github.com/ReVanced/revanced-patches/issues/5833)) ([dcd4245](dcd42454bd))
* **Viber - Hide ads:** Add constrain to known working version ([2db0948](2db0948bea))
* **YouTube Music - Spoof streaming data:** Fix audio playback stuttering ([#5839](https://github.com/ReVanced/revanced-patches/issues/5839)) ([2a85a3b](2a85a3b290))

### Features

* **Viber:** Add `Hide ads` patch ([#5826](https://github.com/ReVanced/revanced-patches/issues/5826)) ([0abfab7](0abfab79d7))
2025-09-15 06:45:56 +00:00
LisoUseInAIKyrios
7d59efe05d chore: Merge branch dev to main (#5830) 2025-09-15 10:43:05 +04:00
github-actions[bot]
81ff5576b0 chore: Sync translations (#5854) 2025-09-15 10:41:42 +04:00
semantic-release-bot
9a5c102c0d chore: Release v5.37.0-dev.6 [skip ci]
# [5.37.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.5...v5.37.0-dev.6) (2025-09-15)

### Bug Fixes

* **Instagram - Hide navigation buttons:** Add constrain to known working version ([e6c79f1](e6c79f1383))
2025-09-15 06:40:30 +00:00
LisoUseInAIKyrios
e6c79f1383 fix(Instagram - Hide navigation buttons): Add constrain to known working version 2025-09-15 10:36:57 +04:00
semantic-release-bot
2a582eced8 chore: Release v5.37.0-dev.5 [skip ci]
# [5.37.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.4...v5.37.0-dev.5) (2025-09-15)

### Bug Fixes

* **Viber - Hide ads:** Add constrain to known working version ([2db0948](2db0948bea))
2025-09-15 06:29:31 +00:00
LisoUseInAIKyrios
2db0948bea fix(Viber - Hide ads): Add constrain to known working version 2025-09-15 10:26:30 +04:00
semantic-release-bot
a3ba92e742 chore: Release v5.37.0-dev.4 [skip ci]
# [5.37.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.3...v5.37.0-dev.4) (2025-09-14)

### Bug Fixes

* **YouTube Music - Spoof streaming data:** Fix audio playback stuttering ([#5839](https://github.com/ReVanced/revanced-patches/issues/5839)) ([2a85a3b](2a85a3b290))
2025-09-14 18:22:57 +00:00
LisoUseInAIKyrios
2a85a3b290 fix(YouTube Music - Spoof streaming data): Fix audio playback stuttering (#5839) 2025-09-14 22:19:13 +04:00
semantic-release-bot
eee72208dd chore: Release v5.37.0-dev.3 [skip ci]
# [5.37.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.2...v5.37.0-dev.3) (2025-09-14)

### Bug Fixes

* **Spotify:** Remove broken `Spoof client` patch ([#5833](https://github.com/ReVanced/revanced-patches/issues/5833)) ([dcd4245](dcd42454bd))
2025-09-14 17:15:28 +00:00
LisoUseInAIKyrios
dcd42454bd fix(Spotify): Remove broken Spoof client patch (#5833) 2025-09-14 21:11:15 +04:00
LisoUseInAIKyrios
782353c18a refactor(Spoof video streams): Handle migration of default spoof client for users upgrading from very old patches 2025-09-14 18:06:40 +04:00
semantic-release-bot
b53b870e8f chore: Release v5.37.0-dev.2 [skip ci]
# [5.37.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.1...v5.37.0-dev.2) (2025-09-14)

### Bug Fixes

* Resolve patching with dev branch ([09b941a](09b941abf0))
2025-09-14 12:00:38 +00:00
LisoUseInAIKyrios
09b941abf0 fix: Resolve patching with dev branch 2025-09-14 15:58:05 +04:00
semantic-release-bot
678ef4052e chore: Release v5.37.0-dev.1 [skip ci]
# [5.37.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.36.0...v5.37.0-dev.1) (2025-09-14)

### Features

* **Viber:** Add `Hide ads` patch ([#5826](https://github.com/ReVanced/revanced-patches/issues/5826)) ([0abfab7](0abfab79d7))
2025-09-14 11:52:21 +00:00
Samo Hribar
0abfab79d7 feat(Viber): Add Hide ads patch (#5826) 2025-09-14 15:49:52 +04:00
LisoUseInAIKyrios
61cadf72cd refactor(Spoof video streams): Back port code from v22 branch to support patching the latest YT Music. Using any target above 7.49.52 is untested and only recommended for experimental or development purposes. 2025-09-14 15:49:35 +04:00
github-actions[bot]
e12359b94f chore: Sync translations (#5829) 2025-09-14 15:46:32 +04:00
semantic-release-bot
c001daba4a chore: Release v5.36.0 [skip ci]
# [5.36.0](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0) (2025-09-14)

### Bug Fixes

* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](88b47ef414))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](8cd8e59bbc))
* **YouTube Music:** Resolve playback issues, change recommended app target to `7.29.52` ([#5813](https://github.com/ReVanced/revanced-patches/issues/5813)) ([a53b00d](a53b00dd51))

### Features

* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](9d4aa5cd16))
2025-09-14 06:56:22 +00:00
LisoUseInAIKyrios
e136f62d6e chore: Merge branch dev to main (#5800) 2025-09-14 10:53:28 +04:00
semantic-release-bot
8ec405a359 chore: Release v5.36.0-dev.1 [skip ci]
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0-dev.1) (2025-09-13)

### Bug Fixes

* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](88b47ef414))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](8cd8e59bbc))
* **YouTube Music:** Resolve playback issues, change recommended app target to `7.29.52` ([#5813](https://github.com/ReVanced/revanced-patches/issues/5813)) ([a53b00d](a53b00dd51))

### Features

* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](9d4aa5cd16))
2025-09-13 15:31:54 +00:00
github-actions[bot]
2f4b3a887b chore: Sync translations (#5821) 2025-09-13 19:28:15 +04:00
semantic-release-bot
d1fabb242b chore: Release v5.36.0-dev.1 [skip ci]
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0-dev.1) (2025-09-13)

### Bug Fixes

* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](88b47ef414))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](8cd8e59bbc))
* **YouTube Music:** Resolve playback issues, change recommended app target to `7.29.52` ([#5813](https://github.com/ReVanced/revanced-patches/issues/5813)) ([a53b00d](a53b00dd51))

### Features

* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](9d4aa5cd16))
2025-09-13 15:15:40 +00:00
LisoUseInAIKyrios
a53b00dd51 fix(YouTube Music): Resolve playback issues, change recommended app target to 7.29.52 (#5813) 2025-09-13 19:12:00 +04:00
semantic-release-bot
850c13e98e chore: Release v5.36.0-dev.1 [skip ci]
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0-dev.1) (2025-09-13)

### Bug Fixes

* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](88b47ef414))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](8cd8e59bbc))

### Features

* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](9d4aa5cd16))
2025-09-13 07:00:16 +00:00
LisoUseInAIKyrios
4310789a26 chore: Fix api 2025-09-13 10:56:43 +04:00
semantic-release-bot
c4a720fbd3 chore: Release v5.36.0-dev.1 [skip ci]
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0-dev.1) (2025-09-12)

### Bug Fixes

* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](88b47ef414))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](8cd8e59bbc))

### Features

* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](9d4aa5cd16))
2025-09-12 15:08:03 +00:00
LisoUseInAIKyrios
3bdb8dbce0 chore(YouTube - SponsorBlock): Adjust strings for consistency / clarity
Strings taken from https://github.com/ajayyy/ExtensionTranslations/blob/master/en/messages.json
2025-09-12 18:49:34 +04:00
LisoUseInAIKyrios
4894f33c96 chore: fix compilation 2025-09-12 18:49:33 +04:00
semantic-release-bot
7f6093ee66 chore: Release v5.36.0-dev.1 [skip ci]
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.1-dev.1...v5.36.0-dev.1) (2025-09-12)

### Features

* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](9d4aa5cd16))
2025-09-12 01:59:47 +00:00
LisoUseInAIKyrios
9d4aa5cd16 feat(YouTube - SponsorBlock): Add 'Hook' segment category (#5783) 2025-09-12 05:56:50 +04:00
oSumAtrIX
5ace6f587c chore: Add ads.fund verification file [skip ci] (#5786) 2025-09-11 16:00:24 +02:00
semantic-release-bot
796f56745e chore: Release v5.35.1-dev.1 [skip ci]
## [5.35.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.35.1-dev.1) (2025-09-11)

### Bug Fixes

* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](88b47ef414))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](8cd8e59bbc))
2025-09-11 01:29:16 +00:00
hoodles
88b47ef414 fix(Duolingo - Disable ads): Support latest app target (#5782) 2025-09-11 03:26:28 +02:00
LisoUseInAIKyrios
8cd8e59bbc fix(YouTube - Hide layout components): Hide new type of Playable shelf 2025-09-11 03:24:09 +02:00
LisoUseInAIKyrios
6e72b14d07 refactor(YouTube - Video Quality): Handle extremely slow internet connections that initially can use -1 quality index 2025-09-11 02:58:33 +02:00
LisoUseInAIKyrios
52b088327b chore: Fix api dump 2025-09-10 21:58:27 +02:00
semantic-release-bot
8e934cc56b chore: Release v5.35.0 [skip ci]
# [5.35.0](https://github.com/ReVanced/revanced-patches/compare/v5.34.0...v5.35.0) (2025-09-09)

### Bug Fixes

* **Instagram - Hide navigation buttons:** Fix Manager patching error ([0a8cd7a](0a8cd7a7db))
* **Proton mail:** Constrain patches to last working app target ([1895291](189529151a))
* Revert dependency updates to fix Manager pre-release patching ([9256aa4](9256aa4548))
* **Spotify - Unlock Premium:** Make compatible with latest versions again by fixing fingerprint ([#5684](https://github.com/ReVanced/revanced-patches/issues/5684)) ([23496c7](23496c7c36))
* **YouTube - Hide layout components:** Hide Playable shelf header ([1473db0](1473db0bef))

### Features

* **BaconReader:** Add `Fix Redgifs API` patch ([#5761](https://github.com/ReVanced/revanced-patches/issues/5761)) ([144af2f](144af2f07e))
* **Boost/Sync for Reddit:** Add `Fix Redgifs` patch  ([#5725](https://github.com/ReVanced/revanced-patches/issues/5725)) ([c66c42e](c66c42e946))
* **Instagram:** Add `Hide navigation buttons` patch ([#5678](https://github.com/ReVanced/revanced-patches/issues/5678)) ([1dbc2d4](1dbc2d4057))
* **Instagram:** Add `Hide Stories from Home` patch ([#5756](https://github.com/ReVanced/revanced-patches/issues/5756)) ([b8629aa](b8629aacb6))
2025-09-09 19:38:18 +00:00
LisoUseInAIKyrios
b3140d909b chore: Merge branch dev to main (#5691) 2025-09-09 21:34:30 +02:00
github-actions[bot]
97645aa9f4 chore: Sync translations (#5777) 2025-09-09 21:32:56 +02:00
semantic-release-bot
603e2d018c chore: Release v5.35.0-dev.5 [skip ci]
# [5.35.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.35.0-dev.4...v5.35.0-dev.5) (2025-09-06)

### Features

* **BaconReader:** Add `Fix Redgifs API` patch ([#5761](https://github.com/ReVanced/revanced-patches/issues/5761)) ([144af2f](144af2f07e))
* **Instagram:** Add `Hide Stories from Home` patch ([#5756](https://github.com/ReVanced/revanced-patches/issues/5756)) ([b8629aa](b8629aacb6))
2025-09-06 10:56:54 +00:00
Eric Ahn
144af2f07e feat(BaconReader): Add Fix Redgifs API patch (#5761) 2025-09-06 12:53:26 +02:00
PainfulPaladins
b8629aacb6 feat(Instagram): Add Hide Stories from Home patch (#5756) 2025-09-06 12:53:08 +02:00
github-actions[bot]
3951527f51 chore: Sync translations (#5768) 2025-09-06 12:52:48 +02:00
semantic-release-bot
7a8b618c4e chore: Release v5.35.0-dev.4 [skip ci]
# [5.35.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.35.0-dev.3...v5.35.0-dev.4) (2025-09-04)

### Features

* **Boost/Sync for Reddit:** Add `Fix Redgifs` patch  ([#5725](https://github.com/ReVanced/revanced-patches/issues/5725)) ([c66c42e](c66c42e946))
2025-09-04 21:33:33 +00:00
Eric Ahn
c66c42e946 feat(Boost/Sync for Reddit): Add Fix Redgifs patch (#5725) 2025-09-04 23:29:58 +02:00
semantic-release-bot
b340769cf3 chore: Release v5.35.0-dev.3 [skip ci]
# [5.35.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.35.0-dev.2...v5.35.0-dev.3) (2025-09-04)

### Bug Fixes

* **Instagram - Hide navigation buttons:** Fix Manager patching error ([0a8cd7a](0a8cd7a7db))
2025-09-04 14:06:03 +00:00
LisoUseInAIKyrios
0a8cd7a7db fix(Instagram - Hide navigation buttons): Fix Manager patching error 2025-09-04 16:01:50 +02:00
semantic-release-bot
39f90e4b11 chore: Release v5.35.0-dev.2 [skip ci]
# [5.35.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.35.0-dev.1...v5.35.0-dev.2) (2025-09-04)

### Bug Fixes

* Revert dependency updates to fix Manager pre-release patching ([9256aa4](9256aa4548))
2025-09-04 10:27:39 +00:00
LisoUseInAIKyrios
9256aa4548 fix: Revert dependency updates to fix Manager pre-release patching 2025-09-04 12:23:56 +02:00
semantic-release-bot
7973c75552 chore: Release v5.35.0-dev.1 [skip ci]
# [5.35.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.34.1-dev.3...v5.35.0-dev.1) (2025-09-03)

### Features

* **Instagram:** Add `Hide navigation buttons` patch ([#5678](https://github.com/ReVanced/revanced-patches/issues/5678)) ([1dbc2d4](1dbc2d4057))
2025-09-03 17:43:47 +00:00
github-actions[bot]
2b2307416a chore: Sync translations (#5755) 2025-09-03 19:41:04 +02:00
PainfulPaladins
1dbc2d4057 feat(Instagram): Add Hide navigation buttons patch (#5678)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
2025-09-03 19:39:25 +02:00
dependabot[bot]
f6917dc361 chore(deps): Bump com.google.protobuf:protoc from 4.31.1 to 4.32.0 (#5751) 2025-09-02 18:28:15 +02:00
dependabot[bot]
d2f043e11a chore(deps): Bump com.google.protobuf:protobuf-javalite from 4.31.1 to 4.32.0 (#5750) 2025-09-02 17:10:45 +02:00
dependabot[bot]
a392bc0dfd chore(deps): Bump actions/setup-java from 4 to 5 (#5746) 2025-09-02 12:43:12 +02:00
dependabot[bot]
dfc127048a chore(deps): Bump actions/attest-build-provenance from 2 to 3 (#5743) 2025-09-02 12:42:08 +02:00
dependabot[bot]
ed31d0cab6 chore(deps): Bump actions/checkout from 4 to 5 (#5745) 2025-09-02 12:41:29 +02:00
dependabot[bot]
0df6315f9c chore(deps): Bump cycjimmy/semantic-release-action from 4 to 5 (#5741) 2025-09-02 12:40:08 +02:00
semantic-release-bot
f14259f9ef chore: Release v5.34.1-dev.3 [skip ci]
## [5.34.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.34.1-dev.2...v5.34.1-dev.3) (2025-08-24)

### Bug Fixes

* **YouTube - Hide layout components:** Hide Playable shelf header ([1473db0](1473db0bef))
2025-08-24 03:30:00 +00:00
LisoUseInAIKyrios
1473db0bef fix(YouTube - Hide layout components): Hide Playable shelf header 2025-08-23 23:26:02 -04:00
github-actions[bot]
829ca58a55 chore: Sync translations (#5707) 2025-08-23 23:23:49 -04:00
semantic-release-bot
aace741e25 chore: Release v5.34.1-dev.2 [skip ci]
## [5.34.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.34.1-dev.1...v5.34.1-dev.2) (2025-08-22)

### Bug Fixes

* **Proton mail:** Constrain patches to last working app target ([1895291](189529151a))
2025-08-22 04:12:59 +00:00
LisoUseInAIKyrios
189529151a fix(Proton mail): Constrain patches to last working app target 2025-08-22 00:10:03 -04:00
140 changed files with 2826 additions and 1998 deletions

View File

@@ -1,3 +1,241 @@
# [5.38.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.37.1-dev.3...v5.38.0-dev.1) (2025-09-15)
### Features
* **Instagram:** Add `Hide explore feed` patch ([#5856](https://github.com/ReVanced/revanced-patches/issues/5856)) ([1d65887](https://github.com/ReVanced/revanced-patches/commit/1d65887e015a067196f5a84db486fff355c96596))
## [5.37.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.37.1-dev.2...v5.37.1-dev.3) (2025-09-15)
### Bug Fixes
* **Spoof video streams:** Remove Android TV and iOS TV clients, add experimental VisionOS, add temporary fix for `Force original audio` to work with any spoof client ([#5861](https://github.com/ReVanced/revanced-patches/issues/5861)) ([abe3943](https://github.com/ReVanced/revanced-patches/commit/abe3943f98fd86dcd74c7e07cf65d3c7fc24fef9))
## [5.37.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.37.1-dev.1...v5.37.1-dev.2) (2025-09-15)
### Bug Fixes
* **Instagram - Hide navigation buttons:** Support v397.1.0.52.81 ([#5855](https://github.com/ReVanced/revanced-patches/issues/5855)) ([f11d1ef](https://github.com/ReVanced/revanced-patches/commit/f11d1ef9907082512f139d4ab0e2e9f707de7e48))
## [5.37.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.37.0...v5.37.1-dev.1) (2025-09-15)
### Bug Fixes
* **YouTube Music - Spoof video streams:** Fix playback issues when using a cellular network ([fa04c8e](https://github.com/ReVanced/revanced-patches/commit/fa04c8eecfbdd0b6ed082b464ca9032536d71762))
# [5.37.0](https://github.com/ReVanced/revanced-patches/compare/v5.36.0...v5.37.0) (2025-09-15)
### Bug Fixes
* **Instagram - Hide navigation buttons:** Add constrain to known working version ([e6c79f1](https://github.com/ReVanced/revanced-patches/commit/e6c79f13834c83fef04e4dee5e628cb0b9a27765))
* Resolve patching with dev branch ([09b941a](https://github.com/ReVanced/revanced-patches/commit/09b941abf0e8029999565082b02a88b5de507ec4))
* **Spotify:** Remove broken `Spoof client` patch ([#5833](https://github.com/ReVanced/revanced-patches/issues/5833)) ([dcd4245](https://github.com/ReVanced/revanced-patches/commit/dcd42454bd5f87dddd720534f6120c4ef90063a3))
* **Viber - Hide ads:** Add constrain to known working version ([2db0948](https://github.com/ReVanced/revanced-patches/commit/2db0948beaf2b68391a1fe7f21e92d31c7df61e7))
* **YouTube Music - Spoof streaming data:** Fix audio playback stuttering ([#5839](https://github.com/ReVanced/revanced-patches/issues/5839)) ([2a85a3b](https://github.com/ReVanced/revanced-patches/commit/2a85a3b29092729ae16d1fd93803634ce5f08e95))
### Features
* **Viber:** Add `Hide ads` patch ([#5826](https://github.com/ReVanced/revanced-patches/issues/5826)) ([0abfab7](https://github.com/ReVanced/revanced-patches/commit/0abfab79d7cda15bf17c53679fbfffb021662649))
# [5.37.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.5...v5.37.0-dev.6) (2025-09-15)
### Bug Fixes
* **Instagram - Hide navigation buttons:** Add constrain to known working version ([e6c79f1](https://github.com/ReVanced/revanced-patches/commit/e6c79f13834c83fef04e4dee5e628cb0b9a27765))
# [5.37.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.4...v5.37.0-dev.5) (2025-09-15)
### Bug Fixes
* **Viber - Hide ads:** Add constrain to known working version ([2db0948](https://github.com/ReVanced/revanced-patches/commit/2db0948beaf2b68391a1fe7f21e92d31c7df61e7))
# [5.37.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.3...v5.37.0-dev.4) (2025-09-14)
### Bug Fixes
* **YouTube Music - Spoof streaming data:** Fix audio playback stuttering ([#5839](https://github.com/ReVanced/revanced-patches/issues/5839)) ([2a85a3b](https://github.com/ReVanced/revanced-patches/commit/2a85a3b29092729ae16d1fd93803634ce5f08e95))
# [5.37.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.2...v5.37.0-dev.3) (2025-09-14)
### Bug Fixes
* **Spotify:** Remove broken `Spoof client` patch ([#5833](https://github.com/ReVanced/revanced-patches/issues/5833)) ([dcd4245](https://github.com/ReVanced/revanced-patches/commit/dcd42454bd5f87dddd720534f6120c4ef90063a3))
# [5.37.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.37.0-dev.1...v5.37.0-dev.2) (2025-09-14)
### Bug Fixes
* Resolve patching with dev branch ([09b941a](https://github.com/ReVanced/revanced-patches/commit/09b941abf0e8029999565082b02a88b5de507ec4))
# [5.37.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.36.0...v5.37.0-dev.1) (2025-09-14)
### Features
* **Viber:** Add `Hide ads` patch ([#5826](https://github.com/ReVanced/revanced-patches/issues/5826)) ([0abfab7](https://github.com/ReVanced/revanced-patches/commit/0abfab79d7cda15bf17c53679fbfffb021662649))
# [5.36.0](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0) (2025-09-14)
### Bug Fixes
* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](https://github.com/ReVanced/revanced-patches/commit/88b47ef414cd073ec3800258b32aceb6f383a411))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](https://github.com/ReVanced/revanced-patches/commit/8cd8e59bbc3a878269276b8ae5f627b044d157f0))
* **YouTube Music:** Resolve playback issues, change recommended app target to `7.29.52` ([#5813](https://github.com/ReVanced/revanced-patches/issues/5813)) ([a53b00d](https://github.com/ReVanced/revanced-patches/commit/a53b00dd514dbe2b3406f3c1013a4f58a7f481c5))
### Features
* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](https://github.com/ReVanced/revanced-patches/commit/9d4aa5cd16a6f9e95cf7c626351b46b86ca80efe))
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0-dev.1) (2025-09-13)
### Bug Fixes
* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](https://github.com/ReVanced/revanced-patches/commit/88b47ef414cd073ec3800258b32aceb6f383a411))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](https://github.com/ReVanced/revanced-patches/commit/8cd8e59bbc3a878269276b8ae5f627b044d157f0))
* **YouTube Music:** Resolve playback issues, change recommended app target to `7.29.52` ([#5813](https://github.com/ReVanced/revanced-patches/issues/5813)) ([a53b00d](https://github.com/ReVanced/revanced-patches/commit/a53b00dd514dbe2b3406f3c1013a4f58a7f481c5))
### Features
* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](https://github.com/ReVanced/revanced-patches/commit/9d4aa5cd16a6f9e95cf7c626351b46b86ca80efe))
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0-dev.1) (2025-09-13)
### Bug Fixes
* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](https://github.com/ReVanced/revanced-patches/commit/88b47ef414cd073ec3800258b32aceb6f383a411))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](https://github.com/ReVanced/revanced-patches/commit/8cd8e59bbc3a878269276b8ae5f627b044d157f0))
* **YouTube Music:** Resolve playback issues, change recommended app target to `7.29.52` ([#5813](https://github.com/ReVanced/revanced-patches/issues/5813)) ([a53b00d](https://github.com/ReVanced/revanced-patches/commit/a53b00dd514dbe2b3406f3c1013a4f58a7f481c5))
### Features
* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](https://github.com/ReVanced/revanced-patches/commit/9d4aa5cd16a6f9e95cf7c626351b46b86ca80efe))
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0-dev.1) (2025-09-13)
### Bug Fixes
* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](https://github.com/ReVanced/revanced-patches/commit/88b47ef414cd073ec3800258b32aceb6f383a411))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](https://github.com/ReVanced/revanced-patches/commit/8cd8e59bbc3a878269276b8ae5f627b044d157f0))
### Features
* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](https://github.com/ReVanced/revanced-patches/commit/9d4aa5cd16a6f9e95cf7c626351b46b86ca80efe))
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.36.0-dev.1) (2025-09-12)
### Bug Fixes
* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([88b47ef](https://github.com/ReVanced/revanced-patches/commit/88b47ef414cd073ec3800258b32aceb6f383a411))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([8cd8e59](https://github.com/ReVanced/revanced-patches/commit/8cd8e59bbc3a878269276b8ae5f627b044d157f0))
### Features
* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([9d4aa5c](https://github.com/ReVanced/revanced-patches/commit/9d4aa5cd16a6f9e95cf7c626351b46b86ca80efe))
# [5.36.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.1-dev.1...v5.36.0-dev.1) (2025-09-12)
### Features
* **YouTube - SponsorBlock:** Add 'Hook' segment category ([#5783](https://github.com/ReVanced/revanced-patches/issues/5783)) ([2e042c4](https://github.com/ReVanced/revanced-patches/commit/2e042c4b3366fa3daf991d5560fcae991d00ad12))
## [5.35.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.35.0...v5.35.1-dev.1) (2025-09-11)
### Bug Fixes
* **Duolingo - Disable ads:** Support latest app target ([#5782](https://github.com/ReVanced/revanced-patches/issues/5782)) ([8491516](https://github.com/ReVanced/revanced-patches/commit/849151637389b8f399356d0d331bb74482f3f05d))
* **YouTube - Hide layout components:** Hide new type of Playable shelf ([3af4126](https://github.com/ReVanced/revanced-patches/commit/3af41265338ddaab52d009f53370c57abddd4599))
# [5.35.0](https://github.com/ReVanced/revanced-patches/compare/v5.34.0...v5.35.0) (2025-09-09)
### Bug Fixes
* **Instagram - Hide navigation buttons:** Fix Manager patching error ([0d10e94](https://github.com/ReVanced/revanced-patches/commit/0d10e94663283fac09f3efc57c9b9805c38c4e13))
* **Proton mail:** Constrain patches to last working app target ([21c34b9](https://github.com/ReVanced/revanced-patches/commit/21c34b908e07a97de8c31c7c828b44a8cc4739b6))
* Revert dependency updates to fix Manager pre-release patching ([4c7a1a8](https://github.com/ReVanced/revanced-patches/commit/4c7a1a8554c67797bf663e5230f566c5a9b229af))
* **Spotify - Unlock Premium:** Make compatible with latest versions again by fixing fingerprint ([#5684](https://github.com/ReVanced/revanced-patches/issues/5684)) ([30dcff1](https://github.com/ReVanced/revanced-patches/commit/30dcff13a56883efc499b71faadb403877cd1c67))
* **YouTube - Hide layout components:** Hide Playable shelf header ([fbb5046](https://github.com/ReVanced/revanced-patches/commit/fbb50463f0e3f533a278c5251cfbce59f09ce641))
### Features
* **BaconReader:** Add `Fix Redgifs API` patch ([#5761](https://github.com/ReVanced/revanced-patches/issues/5761)) ([08868c0](https://github.com/ReVanced/revanced-patches/commit/08868c00d3c4f1f37f4a77f333a03ca5a3259b59))
* **Boost/Sync for Reddit:** Add `Fix Redgifs` patch ([#5725](https://github.com/ReVanced/revanced-patches/issues/5725)) ([c5e8079](https://github.com/ReVanced/revanced-patches/commit/c5e8079eab08075a72078cd0fa79f3beb1f75d98))
* **Instagram:** Add `Hide navigation buttons` patch ([#5678](https://github.com/ReVanced/revanced-patches/issues/5678)) ([415cf0f](https://github.com/ReVanced/revanced-patches/commit/415cf0fb5b9b3dcaf4592943a69eea1c10447b07))
* **Instagram:** Add `Hide Stories from Home` patch ([#5756](https://github.com/ReVanced/revanced-patches/issues/5756)) ([3ae3251](https://github.com/ReVanced/revanced-patches/commit/3ae3251dc0317b6ced136fe9aa14be369642f203))
# [5.35.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.35.0-dev.4...v5.35.0-dev.5) (2025-09-06)
### Features
* **BaconReader:** Add `Fix Redgifs API` patch ([#5761](https://github.com/ReVanced/revanced-patches/issues/5761)) ([08868c0](https://github.com/ReVanced/revanced-patches/commit/08868c00d3c4f1f37f4a77f333a03ca5a3259b59))
* **Instagram:** Add `Hide Stories from Home` patch ([#5756](https://github.com/ReVanced/revanced-patches/issues/5756)) ([3ae3251](https://github.com/ReVanced/revanced-patches/commit/3ae3251dc0317b6ced136fe9aa14be369642f203))
# [5.35.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.35.0-dev.3...v5.35.0-dev.4) (2025-09-04)
### Features
* **Boost/Sync for Reddit:** Add `Fix Redgifs` patch ([#5725](https://github.com/ReVanced/revanced-patches/issues/5725)) ([c5e8079](https://github.com/ReVanced/revanced-patches/commit/c5e8079eab08075a72078cd0fa79f3beb1f75d98))
# [5.35.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.35.0-dev.2...v5.35.0-dev.3) (2025-09-04)
### Bug Fixes
* **Instagram - Hide navigation buttons:** Fix Manager patching error ([0d10e94](https://github.com/ReVanced/revanced-patches/commit/0d10e94663283fac09f3efc57c9b9805c38c4e13))
# [5.35.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.35.0-dev.1...v5.35.0-dev.2) (2025-09-04)
### Bug Fixes
* Revert dependency updates to fix Manager pre-release patching ([4c7a1a8](https://github.com/ReVanced/revanced-patches/commit/4c7a1a8554c67797bf663e5230f566c5a9b229af))
# [5.35.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.34.1-dev.3...v5.35.0-dev.1) (2025-09-03)
### Features
* **Instagram:** Add `Hide navigation buttons` patch ([#5678](https://github.com/ReVanced/revanced-patches/issues/5678)) ([415cf0f](https://github.com/ReVanced/revanced-patches/commit/415cf0fb5b9b3dcaf4592943a69eea1c10447b07))
## [5.34.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.34.1-dev.2...v5.34.1-dev.3) (2025-08-24)
### Bug Fixes
* **YouTube - Hide layout components:** Hide Playable shelf header ([fbb5046](https://github.com/ReVanced/revanced-patches/commit/fbb50463f0e3f533a278c5251cfbce59f09ce641))
## [5.34.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.34.1-dev.1...v5.34.1-dev.2) (2025-08-22)
### Bug Fixes
* **Proton mail:** Constrain patches to last working app target ([21c34b9](https://github.com/ReVanced/revanced-patches/commit/21c34b908e07a97de8c31c7c828b44a8cc4739b6))
## [5.34.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.34.0...v5.34.1-dev.1) (2025-08-21)

8
adsfund.json Normal file
View File

@@ -0,0 +1,8 @@
{
"info": "This is verification file for ads.fund project",
"project": {
"name": "Revanced Patches",
"walletAddress": "0x7ab4091e00363654bf84B34151225742cd92FCE5",
"tokenAddress": "0xadf325f255083a3f3d9a9d01ffb3db52a148d802"
}
}

View File

@@ -0,0 +1,5 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
}

View File

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

View File

@@ -0,0 +1,22 @@
package app.revanced.extension.baconreader;
import app.revanced.extension.shared.fixes.redgifs.BaseFixRedgifsApiPatch;
import okhttp3.OkHttpClient;
/**
* @noinspection unused
*/
public class FixRedgifsApiPatch extends BaseFixRedgifsApiPatch {
static {
INSTANCE = new FixRedgifsApiPatch();
}
public String getDefaultUserAgent() {
// BaconReader uses a static user agent for Redgifs API calls
return "BaconReader";
}
public static OkHttpClient install(OkHttpClient.Builder builder) {
return builder.addInterceptor(INSTANCE).build();
}
}

View File

@@ -1,4 +1,6 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:boostforreddit:stub"))
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
}

View File

@@ -0,0 +1,22 @@
package app.revanced.extension.boostforreddit;
import app.revanced.extension.shared.fixes.redgifs.BaseFixRedgifsApiPatch;
import okhttp3.OkHttpClient;
/**
* @noinspection unused
*/
public class FixRedgifsApiPatch extends BaseFixRedgifsApiPatch {
static {
INSTANCE = new FixRedgifsApiPatch();
}
public String getDefaultUserAgent() {
// Boost uses a static user agent for Redgifs API calls
return "Boost";
}
public static OkHttpClient createClient() {
return new OkHttpClient.Builder().addInterceptor(INSTANCE).build();
}
}

View File

@@ -1,3 +1,9 @@
dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:youtube:stub"))
compileOnly(libs.annotation)
}
android {
defaultConfig {
minSdk = 26

View File

@@ -0,0 +1,27 @@
package app.revanced.extension.music.patches.spoof;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48;
import static app.revanced.extension.shared.spoof.ClientType.VISIONOS;
import java.util.List;
import app.revanced.extension.shared.spoof.ClientType;
import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
@SuppressWarnings("unused")
public class SpoofVideoStreamsPatch {
/**
* Injection point.
*/
public static void setClientOrderToUse() {
List<ClientType> availableClients = List.of(
ANDROID_VR_1_43_32,
ANDROID_VR_1_61_48,
VISIONOS
);
StreamingDataRequest.setClientOrderToUse(availableClients, ANDROID_VR_1_43_32);
}
}

View File

@@ -1,27 +0,0 @@
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

@@ -1,3 +1,4 @@
dependencies {
implementation(project(":extensions:shared:library"))
compileOnly(libs.okhttp)
}

View File

@@ -18,4 +18,5 @@ android {
dependencies {
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
}

View File

@@ -0,0 +1,71 @@
package app.revanced.extension.shared.fixes.redgifs;
import androidx.annotation.NonNull;
import org.json.JSONException;
import java.io.IOException;
import java.net.HttpURLConnection;
import app.revanced.extension.shared.Logger;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public abstract class BaseFixRedgifsApiPatch implements Interceptor {
protected static BaseFixRedgifsApiPatch INSTANCE;
public abstract String getDefaultUserAgent();
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request();
if (!request.url().host().equals("api.redgifs.com")) {
return chain.proceed(request);
}
String userAgent = getDefaultUserAgent();
if (request.header("Authorization") != null) {
Response response = chain.proceed(request.newBuilder().header("User-Agent", userAgent).build());
if (response.isSuccessful()) {
return response;
}
// It's possible that the user agent is being overwritten later down in the interceptor
// chain, so make sure we grab the new user agent from the request headers.
userAgent = response.request().header("User-Agent");
response.close();
}
try {
RedgifsTokenManager.RedgifsToken token = RedgifsTokenManager.refreshToken(userAgent);
// Emulate response for old OAuth endpoint
if (request.url().encodedPath().equals("/v2/oauth/client")) {
String responseBody = RedgifsTokenManager.getEmulatedOAuthResponseBody(token);
return new Response.Builder()
.message("OK")
.code(HttpURLConnection.HTTP_OK)
.protocol(Protocol.HTTP_1_1)
.request(request)
.header("Content-Type", "application/json")
.body(ResponseBody.create(
responseBody, MediaType.get("application/json")))
.build();
}
Request modifiedRequest = request.newBuilder()
.header("Authorization", "Bearer " + token.getAccessToken())
.header("User-Agent", userAgent)
.build();
return chain.proceed(modifiedRequest);
} catch (JSONException ex) {
Logger.printException(() -> "Could not parse Redgifs response", ex);
throw new IOException(ex);
}
}
}

View File

@@ -0,0 +1,94 @@
package app.revanced.extension.shared.fixes.redgifs;
import static app.revanced.extension.shared.requests.Route.Method.GET;
import androidx.annotation.GuardedBy;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import app.revanced.extension.shared.requests.Requester;
/**
* Manages Redgifs token lifecycle.
*/
public class RedgifsTokenManager {
public static class RedgifsToken {
// Expire after 23 hours to provide some breathing room
private static final long EXPIRY_SECONDS = 23 * 60 * 60;
private final String accessToken;
private final long refreshTimeInSeconds;
public RedgifsToken(String accessToken, long refreshTime) {
this.accessToken = accessToken;
this.refreshTimeInSeconds = refreshTime;
}
public String getAccessToken() {
return accessToken;
}
public long getExpiryTimeInSeconds() {
return refreshTimeInSeconds + EXPIRY_SECONDS;
}
public boolean isValid() {
if (accessToken == null) return false;
return getExpiryTimeInSeconds() >= System.currentTimeMillis() / 1000;
}
}
public static final String REDGIFS_API_HOST = "https://api.redgifs.com";
private static final String GET_TEMPORARY_TOKEN = REDGIFS_API_HOST + "/v2/auth/temporary";
@GuardedBy("itself")
private static final Map<String, RedgifsToken> tokenMap = new HashMap<>();
private static String getToken(String userAgent) throws IOException, JSONException {
HttpURLConnection connection = (HttpURLConnection) new URL(GET_TEMPORARY_TOKEN).openConnection();
connection.setFixedLengthStreamingMode(0);
connection.setRequestMethod(GET.name());
connection.setRequestProperty("User-Agent", userAgent);
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Accept", "application/json");
connection.setUseCaches(false);
JSONObject responseObject = Requester.parseJSONObject(connection);
return responseObject.getString("token");
}
public static RedgifsToken refreshToken(String userAgent) throws IOException, JSONException {
synchronized(tokenMap) {
// Reference: https://github.com/JeffreyCA/Apollo-ImprovedCustomApi/pull/67
RedgifsToken token = tokenMap.get(userAgent);
if (token != null && token.isValid()) {
return token;
}
// Copy user agent from original request if present because Redgifs verifies
// that the user agent in subsequent requests matches the one in the OAuth token.
String accessToken = getToken(userAgent);
long refreshTime = System.currentTimeMillis() / 1000;
token = new RedgifsToken(accessToken, refreshTime);
tokenMap.put(userAgent, token);
return token;
}
}
public static String getEmulatedOAuthResponseBody(RedgifsToken token) throws JSONException {
// Reference: https://github.com/JeffreyCA/Apollo-ImprovedCustomApi/pull/67
JSONObject responseObject = new JSONObject();
responseObject.put("access_token", token.accessToken);
responseObject.put("expiry_time", token.getExpiryTimeInSeconds() - (System.currentTimeMillis() / 1000));
responseObject.put("scope", "read");
responseObject.put("token_type", "Bearer");
return responseObject.toString();
}
}

View File

@@ -4,7 +4,6 @@ import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
import app.revanced.extension.shared.spoof.ClientType;
@@ -31,9 +30,6 @@ public class BaseSettings {
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<AppLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AppLanguage.DEFAULT, new AudioStreamLanguageOverrideAvailability());
public static final BooleanSetting SPOOF_STREAMING_DATA_STATS_FOR_NERDS = new BooleanSetting("revanced_spoof_streaming_data_stats_for_nerds", TRUE, parent(SPOOF_VIDEO_STREAMS));
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
// Client type must be last spoof setting due to cyclic references.
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_UNPLUGGED, true, parent(SPOOF_VIDEO_STREAMS));
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type", ClientType.ANDROID_VR_1_61_48, true, parent(SPOOF_VIDEO_STREAMS));
}

View File

@@ -2,17 +2,21 @@ package app.revanced.extension.shared.spoof;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Locale;
import java.util.Objects;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.BaseSettings;
public enum ClientType {
/**
* Video not playable: Kids / Paid / Movie / Private / Age-restricted.
* This client can only be used when logged out.
*/
// https://dumps.tadiphone.dev/dumps/oculus/eureka
ANDROID_VR_NO_AUTH(
ANDROID_VR_1_61_48(
28,
"ANDROID_VR",
"com.google.android.apps.youtube.vr.oculus",
@@ -27,29 +31,32 @@ public enum ClientType {
"1.61.48",
false,
false,
"Android VR No auth"
"Android VR 1.61"
),
// Chromecast with Google TV 4K.
// https://dumps.tadiphone.dev/dumps/google/kirkwood
ANDROID_UNPLUGGED(
29,
"ANDROID_UNPLUGGED",
"com.google.android.apps.youtube.unplugged",
"Google",
"Google TV Streamer",
"Android",
"14",
"34",
"UTT3.240625.001.K5",
"132.0.6808.3",
"8.49.0",
true,
true,
"Android TV"
/**
* Uses non adaptive bitrate, which fixes audio stuttering with YT Music.
* Does not use AV1.
*/
ANDROID_VR_1_43_32(
ANDROID_VR_1_61_48.id,
ANDROID_VR_1_61_48.clientName,
Objects.requireNonNull(ANDROID_VR_1_61_48.packageName),
ANDROID_VR_1_61_48.deviceMake,
ANDROID_VR_1_61_48.deviceModel,
ANDROID_VR_1_61_48.osName,
ANDROID_VR_1_61_48.osVersion,
Objects.requireNonNull(ANDROID_VR_1_61_48.androidSdkVersion),
Objects.requireNonNull(ANDROID_VR_1_61_48.buildId),
"107.0.5284.2",
"1.43.32",
ANDROID_VR_1_61_48.requiresAuth,
ANDROID_VR_1_61_48.useAuth,
"Android VR 1.43"
),
// Cannot play livestreams and lacks HDR, but can play videos with music and labeled "for children".
// Google Pixel 9 Pro Fold
// https://dumps.tadiphone.dev/dumps/google/barbet
/**
* Cannot play livestreams and lacks HDR, but can play videos with music and labeled "for children".
* <a href="https://dumps.tadiphone.dev/dumps/google/barbet">Google Pixel 9 Pro Fold</a>
*/
ANDROID_CREATOR(
14,
"ANDROID_CREATOR",
@@ -66,58 +73,22 @@ public enum ClientType {
true,
"Android Creator"
),
IOS_UNPLUGGED(
33,
"IOS_UNPLUGGED",
"com.google.ios.youtubeunplugged",
/**
* Internal YT client for an unreleased YT client. May stop working at any time.
*/
VISIONOS(101,
"VISIONOS",
"Apple",
forceAVC()
// 11 Pro Max (last device with iOS 13)
? "iPhone12,5"
// 15 Pro Max
: "iPhone16,2",
"iOS",
forceAVC()
// iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1.
? "13.7.17H35"
: "18.2.22C152",
null,
null,
null,
// Version number should be a valid iOS release.
// https://www.ipa4fun.com/history/152043/
forceAVC()
// Some newer versions can also force AVC,
// but 6.45 is the last version that supports iOS 13.
? "6.45"
: "8.49",
true,
true,
forceAVC()
? "iOS TV Force AVC"
: "iOS TV"
),
ANDROID_VR_AUTH(
ANDROID_VR_NO_AUTH.id,
ANDROID_VR_NO_AUTH.clientName,
ANDROID_VR_NO_AUTH.packageName,
ANDROID_VR_NO_AUTH.deviceMake,
ANDROID_VR_NO_AUTH.deviceModel,
ANDROID_VR_NO_AUTH.osName,
ANDROID_VR_NO_AUTH.osVersion,
ANDROID_VR_NO_AUTH.androidSdkVersion,
ANDROID_VR_NO_AUTH.buildId,
ANDROID_VR_NO_AUTH.cronetVersion,
ANDROID_VR_NO_AUTH.clientVersion,
ANDROID_VR_NO_AUTH.requiresAuth,
true,
"Android VR Auth"
"RealityDevice14,1",
"visionOS",
"1.3.21O771",
"0.1",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15",
false,
false,
"visionOS"
);
private static boolean forceAVC() {
return BaseSettings.SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC.get();
}
/**
* YouTube
* <a href="https://github.com/zerodytrash/YouTube-Internal-Clients?tab=readme-ov-file#clients">client type</a>
@@ -129,6 +100,7 @@ public enum ClientType {
/**
* App package name.
*/
@Nullable
private final String packageName;
/**
@@ -198,17 +170,20 @@ public enum ClientType {
*/
public final String friendlyName;
/**
* Android constructor.
*/
@SuppressWarnings("ConstantLocale")
ClientType(int id,
String clientName,
String packageName,
@NonNull String packageName,
String deviceMake,
String deviceModel,
String osName,
String osVersion,
@Nullable String androidSdkVersion,
@Nullable String buildId,
@Nullable String cronetVersion,
@NonNull String androidSdkVersion,
@NonNull String buildId,
@NonNull String cronetVersion,
String clientVersion,
boolean requiresAuth,
boolean useAuth,
@@ -229,31 +204,44 @@ public enum ClientType {
this.friendlyName = friendlyName;
Locale defaultLocale = Locale.getDefault();
if (androidSdkVersion == null) {
// Convert version from '18.2.22C152' into '18_2_22'
String userAgentOsVersion = osVersion
.replaceAll("(\\d+\\.\\d+\\.\\d+).*", "$1")
.replace(".", "_");
// https://github.com/mitmproxy/mitmproxy/issues/4836
this.userAgent = String.format("%s/%s (%s; U; CPU iOS %s like Mac OS X; %s)",
packageName,
clientVersion,
deviceModel,
userAgentOsVersion,
defaultLocale
);
} else {
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s; Cronet/%s)",
packageName,
clientVersion,
osVersion,
defaultLocale,
deviceModel,
Objects.requireNonNull(buildId),
Objects.requireNonNull(cronetVersion)
);
}
this.userAgent = String.format("%s/%s (Linux; U; Android %s; %s; %s; Build/%s; Cronet/%s)",
packageName,
clientVersion,
osVersion,
defaultLocale,
deviceModel,
Objects.requireNonNull(buildId),
Objects.requireNonNull(cronetVersion)
);
Logger.printDebug(() -> "userAgent: " + this.userAgent);
}
@SuppressWarnings("ConstantLocale")
ClientType(int id,
String clientName,
String deviceMake,
String deviceModel,
String osName,
String osVersion,
String clientVersion,
String userAgent,
boolean requiresAuth,
boolean useAuth,
String friendlyName) {
this.id = id;
this.clientName = clientName;
this.deviceMake = deviceMake;
this.deviceModel = deviceModel;
this.osName = osName;
this.osVersion = osVersion;
this.clientVersion = clientVersion;
this.userAgent = userAgent;
this.requiresAuth = requiresAuth;
this.useAuth = useAuth;
this.friendlyName = friendlyName;
this.packageName = null;
this.androidSdkVersion = null;
this.buildId = null;
this.cronetVersion = null;
}
}

View File

@@ -10,6 +10,7 @@ import java.util.Map;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
@@ -19,13 +20,25 @@ public class SpoofVideoStreamsPatch {
private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_VIDEO_STREAMS.get();
private static final boolean FIX_HLS_CURRENT_TIME = SPOOF_STREAMING_DATA
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.VISIONOS;
@Nullable
private static volatile AppLanguage languageOverride;
/**
* Any unreachable ip address. Used to intentionally fail requests.
* Domain used for internet connectivity verification.
* It has an empty response body and is only used to check for a 204 response code.
* <p>
* If an unreachable IP address (127.0.0.1) is used, no response code is provided.
* <p>
* YouTube handles unreachable IP addresses without issue.
* YouTube Music has an issue with waiting for the Cronet connect timeout of 30s on mobile networks.
* <p>
* Using a VPN or DNS can temporarily resolve this issue,
* But the ideal workaround is to avoid using an unreachable IP address.
*/
private static final String UNREACHABLE_HOST_URI_STRING = "https://127.0.0.0";
private static final Uri UNREACHABLE_HOST_URI = Uri.parse(UNREACHABLE_HOST_URI_STRING);
private static final String INTERNET_CONNECTION_CHECK_URI_STRING = "https://www.google.com/gen_204";
private static final Uri INTERNET_CONNECTION_CHECK_URI = Uri.parse(INTERNET_CONNECTION_CHECK_URI_STRING);
/**
* @return If this patch was included during patching.
@@ -34,10 +47,21 @@ public class SpoofVideoStreamsPatch {
return false; // Modified during patching.
}
public static boolean notSpoofingToAndroid() {
return !isPatchIncluded()
|| !BaseSettings.SPOOF_VIDEO_STREAMS.get()
|| BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
public static boolean spoofingToClientWithNoMultiAudioStreams() {
return isPatchIncluded() && BaseSettings.SPOOF_VIDEO_STREAMS.get();
}
/**
* @param language Language override for non-authenticated requests. If this is null then
* {@link BaseSettings#SPOOF_VIDEO_STREAMS_LANGUAGE} is used.
*/
public static void setLanguageOverride(@Nullable AppLanguage language) {
languageOverride = language;
}
@Nullable
public static AppLanguage getLanguageOverride() {
return languageOverride;
}
/**
@@ -53,9 +77,9 @@ public class SpoofVideoStreamsPatch {
String path = playerRequestUri.getPath();
if (path != null && path.contains("get_watch")) {
Logger.printDebug(() -> "Blocking 'get_watch' by returning unreachable uri");
Logger.printDebug(() -> "Blocking 'get_watch' by returning internet connection check uri");
return UNREACHABLE_HOST_URI;
return INTERNET_CONNECTION_CHECK_URI;
}
} catch (Exception ex) {
Logger.printException(() -> "blockGetWatchRequest failure", ex);
@@ -77,9 +101,9 @@ public class SpoofVideoStreamsPatch {
String path = originalUri.getPath();
if (path != null && path.contains("initplayback")) {
Logger.printDebug(() -> "Blocking 'initplayback' by clearing query");
Logger.printDebug(() -> "Blocking 'initplayback' by returning internet connection check uri");
return originalUri.buildUpon().clearQuery().build().toString();
return INTERNET_CONNECTION_CHECK_URI_STRING;
}
} catch (Exception ex) {
Logger.printException(() -> "blockInitPlaybackRequest failure", ex);
@@ -252,16 +276,8 @@ public class SpoofVideoStreamsPatch {
public static final class AudioStreamLanguageOverrideAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.ANDROID_VR_NO_AUTH;
}
}
public static final class SpoofiOSAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
return BaseSettings.SPOOF_VIDEO_STREAMS.get()
&& BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS_UNPLUGGED;
// Since all current clients are un-authenticated, this works for all spoof clients.
return BaseSettings.SPOOF_VIDEO_STREAMS.get();
}
}
}

View File

@@ -1,5 +1,7 @@
package app.revanced.extension.shared.spoof.requests;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32;
import org.json.JSONException;
import org.json.JSONObject;
@@ -10,8 +12,10 @@ import java.util.Locale;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.requests.Requester;
import app.revanced.extension.shared.requests.Route;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.spoof.ClientType;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
final class PlayerRoutes {
static final Route.CompiledRoute GET_STREAMING_DATA = new Route(
@@ -37,14 +41,16 @@ final class PlayerRoutes {
try {
JSONObject context = new JSONObject();
// Can override default language only if no login is used.
// Could use preferred audio for all clients that do not login,
// but if this is a fall over client it will set the language even though
// the audio language is not selectable in the UI.
ClientType userSelectedClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
Locale streamLocale = userSelectedClient == ClientType.ANDROID_VR_NO_AUTH
? BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getLocale()
: Locale.getDefault();
AppLanguage language = SpoofVideoStreamsPatch.getLanguageOverride();
if (language == null || BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ANDROID_VR_1_43_32) {
// Force original audio has not overrode the language.
// Or if YT has fallen over to the very last client (VR 1.43), then always
// use the app language because forcing an audio stream of specific languages
// can sometimes fail so it's better to try and load something rather than nothing.
language = BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get();
}
//noinspection ExtractMethodRecommender
Locale streamLocale = language.getLocale();
JSONObject client = new JSONObject();
client.put("deviceMake", clientType.deviceMake);

View File

@@ -35,21 +35,27 @@ import app.revanced.extension.shared.spoof.ClientType;
*/
public class StreamingDataRequest {
private static final ClientType[] CLIENT_ORDER_TO_USE;
private static volatile ClientType[] clientOrderToUse = ClientType.values();
static {
ClientType[] allClientTypes = ClientType.values();
ClientType preferredClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
public static void setClientOrderToUse(List<ClientType> availableClients, ClientType preferredClient) {
Objects.requireNonNull(preferredClient);
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
CLIENT_ORDER_TO_USE[0] = preferredClient;
int availableClientSize = availableClients.size();
if (!availableClients.contains(preferredClient)) {
availableClientSize++;
}
clientOrderToUse = new ClientType[availableClientSize];
clientOrderToUse[0] = preferredClient;
int i = 1;
for (ClientType c : allClientTypes) {
for (ClientType c : availableClients) {
if (c != preferredClient) {
CLIENT_ORDER_TO_USE[i++] = c;
clientOrderToUse[i++] = c;
}
}
Logger.printDebug(() -> "Available spoof clients: " + Arrays.toString(clientOrderToUse));
}
private static final String AUTHORIZATION_HEADER = "Authorization";
@@ -193,9 +199,9 @@ public class StreamingDataRequest {
// Retry with different client if empty response body is received.
int i = 0;
for (ClientType clientType : CLIENT_ORDER_TO_USE) {
for (ClientType clientType : clientOrderToUse) {
// Show an error if the last client type fails, or if debug is enabled then show for all attempts.
final boolean showErrorToast = (++i == CLIENT_ORDER_TO_USE.length) || debugEnabled;
final boolean showErrorToast = (++i == clientOrderToUse.length) || debugEnabled;
HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast);
if (connection != null) {

View File

@@ -2,4 +2,5 @@ dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:syncforreddit:stub"))
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
}

View File

@@ -0,0 +1,22 @@
package app.revanced.extension.syncforreddit;
import app.revanced.extension.shared.fixes.redgifs.BaseFixRedgifsApiPatch;
import okhttp3.OkHttpClient;
/**
* @noinspection unused
*/
public class FixRedgifsApiPatch extends BaseFixRedgifsApiPatch {
static {
INSTANCE = new FixRedgifsApiPatch();
}
public String getDefaultUserAgent() {
// To be filled in by patch
return "";
}
public static OkHttpClient install(OkHttpClient.Builder builder) {
return builder.addInterceptor(INSTANCE).build();
}
}

View File

@@ -1,7 +1,7 @@
package app.revanced.extension.youtube.patches;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.Setting;
import app.revanced.extension.shared.settings.AppLanguage;
import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
import app.revanced.extension.youtube.settings.Settings;
@@ -11,16 +11,20 @@ public class ForceOriginalAudioPatch {
private static final String DEFAULT_AUDIO_TRACKS_SUFFIX = ".4";
/**
* If the conditions to use this patch were present when the app launched.
* Injection point.
*/
public static boolean PATCH_AVAILABLE = SpoofVideoStreamsPatch.notSpoofingToAndroid();
public static final class ForceOriginalAudioAvailability implements Setting.Availability {
@Override
public boolean isAvailable() {
// Check conditions of launch and now. Otherwise if spoofing is changed
// without a restart the setting will show as available when it's not.
return PATCH_AVAILABLE && SpoofVideoStreamsPatch.notSpoofingToAndroid();
public static void setPreferredLanguage() {
if (Settings.FORCE_ORIGINAL_AUDIO.get()) {
// None of the current spoof clients support audio track menu,
// And all are un-authenticated and can request any language code
// (authenticated requests ignore the language code and always use the account language).
// To still support force original audio, if it's enabled then pick a language
// that is not auto-dubbed by YouTube: https://support.google.com/youtube/answer/15569972
// but the language is also supported natively by the Meta Quest device that
// Android VR is spoofing.
AppLanguage override = AppLanguage.SV;
Logger.printDebug(() -> "Setting language override: " + override);
SpoofVideoStreamsPatch.setLanguageOverride(override);
}
}

View File

@@ -513,6 +513,9 @@ public final class VideoInformation {
Logger.printDebug(() -> "VideoQualities: " + Arrays.toString(currentQualities));
}
// On extremely slow internet connections the index can initially be -1
originalQualityIndex = Math.max(0, originalQualityIndex);
VideoQuality updatedCurrentQuality = qualities[originalQualityIndex];
if (updatedCurrentQuality.patch_getResolution() != AUTOMATIC_VIDEO_QUALITY_VALUE
&& (currentQuality == null || currentQuality != updatedCurrentQuality)) {

View File

@@ -45,6 +45,7 @@ public final class LayoutComponentsFilter extends Filter {
private final StringFilterGroup chipBar;
private final StringFilterGroup channelProfile;
private final ByteArrayFilterGroupList channelProfileBuffer;
private final ByteArrayFilterGroup playablesBuffer;
public LayoutComponentsFilter() {
exceptions.addPatterns(
@@ -190,6 +191,12 @@ public final class LayoutComponentsFilter extends Filter {
"mini_game_card.eml"
);
// Playable horizontal shelf header.
playablesBuffer = new ByteArrayFilterGroup(
Settings.HIDE_PLAYABLES,
"FEmini_app_destination"
);
final var quickActions = new StringFilterGroup(
Settings.HIDE_QUICK_ACTIONS,
"quick_actions"
@@ -342,7 +349,9 @@ public final class LayoutComponentsFilter extends Filter {
}
if (matchedGroup == horizontalShelves) {
return contentIndex == 0 && (hideShelves() || ticketShelf.check(buffer).isFiltered());
return contentIndex == 0 && (hideShelves()
|| ticketShelf.check(buffer).isFiltered()
|| playablesBuffer.check(buffer).isFiltered());
}
if (matchedGroup == chipBar) {

View File

@@ -9,13 +9,13 @@ import app.revanced.extension.youtube.shared.ShortsPlayerState;
public class PlayerFlyoutMenuItemsFilter extends Filter {
public static final class HideAudioFlyoutMenuAvailability implements Setting.Availability {
private static final boolean AVAILABLE_ON_LAUNCH = SpoofVideoStreamsPatch.notSpoofingToAndroid();
private static final boolean AVAILABLE_ON_LAUNCH = !SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams();
@Override
public boolean isAvailable() {
// Check conditions of launch and now. Otherwise if spoofing is changed
// without a restart the setting will show as available when it's not.
return AVAILABLE_ON_LAUNCH && SpoofVideoStreamsPatch.notSpoofingToAndroid();
return AVAILABLE_ON_LAUNCH && !SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams();
}
}

View File

@@ -0,0 +1,32 @@
package app.revanced.extension.youtube.patches.spoof;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_CREATOR;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_43_32;
import static app.revanced.extension.shared.spoof.ClientType.ANDROID_VR_1_61_48;
import static app.revanced.extension.shared.spoof.ClientType.VISIONOS;
import java.util.List;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.spoof.ClientType;
import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
@SuppressWarnings("unused")
public class SpoofVideoStreamsPatch {
/**
* Injection point.
*/
public static void setClientOrderToUse() {
List<ClientType> availableClients = List.of(
ANDROID_VR_1_61_48,
ANDROID_CREATOR,
VISIONOS,
// VR 1.43 must be last as spoof streaming data handles it slightly differently.
ANDROID_VR_1_43_32
);
StreamingDataRequest.setClientOrderToUse(availableClients,
BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get());
}
}

View File

@@ -12,7 +12,6 @@ import static app.revanced.extension.youtube.patches.ChangeHeaderPatch.HeaderLog
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.ChangeStartPageTypeAvailability;
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
import static app.revanced.extension.youtube.patches.ExitFullscreenPatch.FullscreenMode;
import static app.revanced.extension.youtube.patches.ForceOriginalAudioPatch.ForceOriginalAudioAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType;
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MINIMAL;
@@ -75,7 +74,7 @@ public class Settings extends BaseSettings {
"0.25\n0.5\n0.75\n1.0\n1.25\n1.5\n1.75\n2.0\n2.5\n3.0\n4.0\n5.0\n6.0\n7.0\n8.0", true);
// Audio
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE, new ForceOriginalAudioAvailability());
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE, true);
// Ads
public static final BooleanSetting HIDE_CREATOR_STORE_SHELF = new BooleanSetting("revanced_hide_creator_store_shelf", TRUE);
@@ -436,6 +435,9 @@ public class Settings extends BaseSettings {
public static final StringSetting SB_CATEGORY_HIGHLIGHT = new StringSetting("sb_highlight", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_HIGHLIGHT_COLOR = new StringSetting("sb_highlight_color", "#FF1684");
public static final FloatSetting SB_CATEGORY_HIGHLIGHT_OPACITY = new FloatSetting("sb_highlight_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_HOOK = new StringSetting("sb_hook", IGNORE.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_HOOK_COLOR = new StringSetting("sb_hook_color", "#395699");
public static final FloatSetting SB_CATEGORY_HOOK_OPACITY = new FloatSetting("sb_hook_opacity", 0.8f);
public static final StringSetting SB_CATEGORY_INTRO = new StringSetting("sb_intro", MANUAL_SKIP.reVancedKeyValue);
public static final StringSetting SB_CATEGORY_INTRO_COLOR = new StringSetting("sb_intro_color", "#00FFFF");
public static final FloatSetting SB_CATEGORY_INTRO_OPACITY = new FloatSetting("sb_intro_opacity", 0.8f);

View File

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

View File

@@ -12,8 +12,8 @@ import app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch;
public class HideAudioFlyoutMenuPreference extends SwitchPreference {
{
// Audio menu is not available if spoofing to Android client type.
if (!SpoofVideoStreamsPatch.notSpoofingToAndroid()) {
// Audio menu is not available if spoofing to most client types.
if (SpoofVideoStreamsPatch.spoofingToClientWithNoMultiAudioStreams()) {
String summary = str("revanced_hide_player_flyout_audio_track_not_available");
setSummary(summary);
setSummaryOn(summary);

View File

@@ -78,20 +78,17 @@ public class SpoofStreamingDataSideEffectsPreference extends Preference {
Logger.printDebug(() -> "Updating spoof stream side effects preference");
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
String key = "revanced_spoof_video_streams_about_" +
(clientType == ClientType.IOS_UNPLUGGED
? "ios_tv"
: "android");
String title = str(key + "_title");
String summary = str(key + "_summary");
// Android VR supports AV1 but all other clients do not.
if (clientType != ClientType.ANDROID_VR_AUTH && clientType != ClientType.ANDROID_VR_NO_AUTH) {
summary += '\n' + str("revanced_spoof_video_streams_about_no_av1");
}
String title = str("revanced_spoof_video_streams_about_title");
// Currently only Android VR and VisionOS are supported, and both have the same base side effects.
String summary = str("revanced_spoof_video_streams_about_android_summary");
summary += '\n' + str("revanced_spoof_video_streams_about_kids_videos");
if (clientType == ClientType.VISIONOS) {
summary = str("revanced_spoof_video_streams_about_experimental")
+ '\n' + summary
+ '\n' + str("revanced_spoof_video_streams_about_no_av1");
}
setTitle(title);
setSummary(summary);
}

View File

@@ -15,6 +15,7 @@ import android.graphics.drawable.shapes.RoundRectShape;
import android.text.TextUtils;
import android.util.Range;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
@@ -167,6 +168,11 @@ public class SegmentPlaybackController {
*/
private static WeakReference<Dialog> toastDialogRef = new WeakReference<>(null);
/**
* Visibility of the ad progress UI component.
*/
private static volatile int adProgressTextVisibility = -1;
static {
// Dismiss toast if app changes to PiP while undo skip is shown.
PlayerType.getOnChange().addObserver((PlayerType type) -> {
@@ -336,6 +342,7 @@ public class SegmentPlaybackController {
*/
static void executeDownloadSegments(String videoId) {
Objects.requireNonNull(videoId);
Utils.verifyOffMainThread();
SponsorSegment[] segments = SBRequester.getSegments(videoId);
@@ -367,6 +374,35 @@ public class SegmentPlaybackController {
});
}
/**
* Injection point.
*/
@SuppressWarnings("unused")
public static void setAdProgressTextVisibility(int visibility) {
if (adProgressTextVisibility != visibility) {
adProgressTextVisibility = visibility;
Logger.printDebug(() -> {
String visibilityMessage = switch (visibility) {
case View.VISIBLE -> "VISIBLE";
case View.GONE -> "GONE";
case View.INVISIBLE -> "INVISIBLE";
default -> "UNKNOWN";
};
return "AdProgressText visibility changed to: " + visibilityMessage;
});
}
}
/**
* When a video ad is playing in a regular video player, segments or the Skip button should be hidden.
* @return Whether the Ad Progress TextView is visible in the regular video player.
*/
public static boolean isAdProgressTextVisible() {
return adProgressTextVisibility == View.VISIBLE;
}
/**
* Injection point.
* Updates SponsorBlock every 1000ms.
@@ -376,7 +412,8 @@ public class SegmentPlaybackController {
try {
if (!Settings.SB_ENABLED.get()
|| PlayerType.getCurrent().isNoneOrHidden() // Shorts playback.
|| segments == null || segments.length == 0) {
|| segments == null || segments.length == 0
|| isAdProgressTextVisible()) {
return;
}
Logger.printDebug(() -> "setVideoTime: " + millis);
@@ -671,7 +708,14 @@ public class SegmentPlaybackController {
// Check for any smaller embedded segments, and count those as auto-skipped.
final boolean showSkipToast = Settings.SB_TOAST_ON_SKIP.get();
for (SponsorSegment otherSegment : Objects.requireNonNull(segments)) {
if (segmentToSkip.end < otherSegment.start) {
if (otherSegment.end <= segmentToSkip.start) {
// Other segment does not overlap, and is before this skipped segment.
// This situation can only happen if a video is opened and adjusted to
// a later time in the video where earlier auto skip segments
// have not been encountered yet.
continue;
}
if (segmentToSkip.end <= otherSegment.start) {
break; // No other segments can be contained.
}
@@ -922,7 +966,8 @@ public class SegmentPlaybackController {
public static String appendTimeWithoutSegments(String totalTime) {
try {
if (Settings.SB_ENABLED.get() && Settings.SB_VIDEO_LENGTH_WITHOUT_SEGMENTS.get()
&& !TextUtils.isEmpty(totalTime) && !TextUtils.isEmpty(timeWithoutSegments)) {
&& !TextUtils.isEmpty(totalTime) && !TextUtils.isEmpty(timeWithoutSegments)
&& !isAdProgressTextVisible()) {
// Force LTR layout, to match the same LTR video time/length layout YouTube uses for all languages
return "\u202D" + totalTime + timeWithoutSegments; // u202D = left to right override
}
@@ -983,7 +1028,7 @@ public class SegmentPlaybackController {
@SuppressWarnings("unused")
public static void drawSponsorTimeBars(final Canvas canvas, final float posY) {
try {
if (segments == null) return;
if (segments == null || isAdProgressTextVisible()) return;
final long videoLength = VideoInformation.getVideoLength();
if (videoLength <= 0) return;

View File

@@ -52,6 +52,8 @@ public enum SegmentCategory {
sf("revanced_sb_skip_button_preview_beginning"), sf("revanced_sb_skip_button_preview_middle"), sf("revanced_sb_skip_button_preview_end"),
sf("revanced_sb_skipped_preview_beginning"), sf("revanced_sb_skipped_preview_middle"), sf("revanced_sb_skipped_preview_end"),
SB_CATEGORY_PREVIEW, SB_CATEGORY_PREVIEW_COLOR, SB_CATEGORY_PREVIEW_OPACITY),
HOOK("hook", sf("revanced_sb_segments_hook"), sf("revanced_sb_segments_hook_sum"), sf("revanced_sb_skip_button_hook"), sf("revanced_sb_skipped_hook"),
SB_CATEGORY_HOOK, SB_CATEGORY_HOOK_COLOR, SB_CATEGORY_HOOK_OPACITY),
FILLER("filler", sf("revanced_sb_segments_filler"), sf("revanced_sb_segments_filler_sum"), sf("revanced_sb_skip_button_filler"), sf("revanced_sb_skipped_filler"),
SB_CATEGORY_FILLER, SB_CATEGORY_FILLER_COLOR, SB_CATEGORY_FILLER_OPACITY),
MUSIC_OFFTOPIC("music_offtopic", sf("revanced_sb_segments_nomusic"), sf("revanced_sb_segments_nomusic_sum"), sf("revanced_sb_skip_button_nomusic"), sf("revanced_sb_skipped_nomusic"),
@@ -69,6 +71,7 @@ public enum SegmentCategory {
INTRO,
OUTRO,
PREVIEW,
HOOK,
FILLER,
MUSIC_OFFTOPIC,
};
@@ -81,6 +84,7 @@ public enum SegmentCategory {
INTRO,
OUTRO,
PREVIEW,
HOOK,
FILLER,
MUSIC_OFFTOPIC,
};

View File

@@ -6,6 +6,7 @@ import androidx.annotation.Nullable;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.youtube.settings.Settings;
import app.revanced.extension.youtube.sponsorblock.SegmentPlaybackController;
import app.revanced.extension.youtube.videoplayer.PlayerControlButton;
@SuppressWarnings("unused")
@@ -26,7 +27,7 @@ public class CreateSegmentButton {
controlsView,
"revanced_sb_create_segment_button",
null,
CreateSegmentButton::shouldBeShown,
CreateSegmentButton::isButtonEnabled,
v -> SponsorBlockViewController.toggleNewSegmentLayoutVisibility(),
null
);
@@ -56,7 +57,8 @@ public class CreateSegmentButton {
if (instance != null) instance.setVisibility(visible, animated);
}
private static boolean shouldBeShown() {
return Settings.SB_ENABLED.get() && Settings.SB_CREATE_NEW_SEGMENT.get();
private static boolean isButtonEnabled() {
return Settings.SB_ENABLED.get() && Settings.SB_CREATE_NEW_SEGMENT.get()
&& !SegmentPlaybackController.isAdProgressTextVisible();
}
}

View File

@@ -28,7 +28,7 @@ public class VotingButton {
controlsView,
"revanced_sb_voting_button",
null,
VotingButton::shouldBeShown,
VotingButton::isButtonEnabled,
v -> SponsorBlockUtils.onVotingClicked(v.getContext()),
null
);
@@ -58,8 +58,9 @@ public class VotingButton {
if (instance != null) instance.setVisibility(visible, animated);
}
private static boolean shouldBeShown() {
private static boolean isButtonEnabled() {
return Settings.SB_ENABLED.get() && Settings.SB_VOTING_BUTTON.get()
&& SegmentPlaybackController.videoHasSegments();
&& SegmentPlaybackController.videoHasSegments()
&& !SegmentPlaybackController.isAdProgressTextVisible();
}
}

View File

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

View File

@@ -11,8 +11,8 @@ appcompat = "1.7.0"
okhttp = "5.0.0-alpha.14"
retrofit = "2.11.0"
guava = "33.4.0-jre"
protobuf-javalite = "4.31.1"
protoc = "4.31.1"
protobuf-javalite = "4.32.0"
protoc = "4.32.0"
protobuf = "0.9.5"
antlr4 = "4.13.2"
nanohttpd = "2.3.1"

View File

@@ -264,6 +264,18 @@ public final class app/revanced/patches/instagram/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/instagram/hide/explore/HideExploreFeedKt {
public static final fun getHideExportFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/instagram/hide/navigation/HideNavigationButtonsKt {
public static final fun getHideNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/instagram/hide/stories/HideStoriesKt {
public static final fun getHideStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/instagram/misc/signature/SignatureCheckPatchKt {
public static final fun getSignatureCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -384,14 +396,21 @@ 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/SpoofClientPatchKt {
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
public final class app/revanced/patches/music/misc/spoof/SpoofVideoStreamsKt {
public static final fun getSpoofVideoStreamsPatch ()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/music/playservice/VersionCheckPatchKt {
public static final fun getVersionCheckPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
public static final fun is_7_33_or_greater ()Z
public static final fun is_8_11_or_greater ()Z
public static final fun is_8_15_or_greater ()Z
}
public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -508,6 +527,13 @@ public final class app/revanced/patches/reddit/ad/general/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/FixRedgifsApiPatchKt {
public static final field CREATE_NEW_CLIENT_METHOD Ljava/lang/String;
public static final field INSTALL_NEW_CLIENT_METHOD Ljava/lang/String;
public static final fun fixRedgifsApiPatch (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Lapp/revanced/patcher/patch/BytecodePatch;
public static synthetic fun fixRedgifsApiPatch$default (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/FixSLinksPatchKt {
public static final field RESOLVE_S_LINK_METHOD Ljava/lang/String;
public static final field SET_ACCESS_TOKEN_METHOD Ljava/lang/String;
@@ -524,6 +550,14 @@ public final class app/revanced/patches/reddit/customclients/baconreader/api/Spo
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/baconreader/fix/redgifs/FixRedgifsApiPatchKt {
public static final fun getFixRedgifsApi ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/baconreader/misc/extension/SharedExtensionPatchKt {
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/boostforreddit/ads/DisableAdsPatchKt {
public static final fun getDisableAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -536,6 +570,10 @@ public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/
public static final fun getFixAudioMissingInDownloadsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/redgifs/FixRedgifsApiPatchKt {
public static final fun getFixRedgifsApi ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/boostforreddit/fix/slink/FixSLinksPatchKt {
public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String;
public static final fun getFixSlinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
@@ -606,6 +644,10 @@ public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/redgifs/FixRedgifsApiPatchKt {
public static final fun getFixRedgifsApi ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/reddit/customclients/sync/syncforreddit/fix/slink/FixSLinksPatchKt {
public static final field EXTENSION_CLASS_DESCRIPTOR Ljava/lang/String;
public static final fun getFixSLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
@@ -952,10 +994,6 @@ public final class app/revanced/patches/spotify/lite/ondemand/OnDemandPatchKt {
public static final fun getOnDemandPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/UnlockPremiumPatchKt {
public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt {
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
@@ -1208,6 +1246,10 @@ public final class app/revanced/patches/twitter/misc/links/SanitizeSharingLinksP
public static final fun getSanitizeSharingLinksPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/viber/ads/HideAdsPatchKt {
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/vsco/misc/pro/UnlockProPatchKt {
public static final fun getUnlockProPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

View File

@@ -19,14 +19,16 @@ val disableAdsPatch = bytecodePatch(
// SharedPreferences has a debug boolean value with key "disable_ads", which maps to "DebugCategory.DISABLE_ADS".
//
// MonetizationDebugSettings seems to be the most general setting to work fine.
initializeMonetizationDebugSettingsFingerprint.method.apply {
val insertIndex = initializeMonetizationDebugSettingsFingerprint.patternMatch!!.startIndex
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
initializeMonetizationDebugSettingsFingerprint
.match(monetizationDebugSettingsToStringFingerprint.classDef)
.method.apply {
val insertIndex = initializeMonetizationDebugSettingsFingerprint.patternMatch!!.startIndex
val register = getInstruction<TwoRegisterInstruction>(insertIndex).registerA
addInstructions(
insertIndex,
"const/4 v$register, 0x1",
)
}
addInstructions(
insertIndex,
"const/4 v$register, 0x1",
)
}
}
}

View File

@@ -7,13 +7,11 @@ import com.android.tools.smali.dexlib2.Opcode
internal val initializeMonetizationDebugSettingsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
returns("V")
parameters(
"Z", // disableAds
"Z", // useDebugBilling
"Z", // showManageSubscriptions
"Z", // alwaysShowSuperAds
// matches "Lcom/duolingo/debug/FamilyQuestOverride;" or "Lcom/duolingo/data/debug/monetization/FamilyQuestOverride;"
"Lcom/duolingo/",
)
// Parameters have not been reliable for fingerprinting between versions.
opcodes(Opcode.IPUT_BOOLEAN)
}
internal val monetizationDebugSettingsToStringFingerprint = fingerprint {
strings("MonetizationDebugSettings(") // Partial string match.
custom { method, _ -> method.name == "toString" }
}

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.instagram.hide.explore
import app.revanced.patcher.fingerprint
internal val exploreResponseJsonParserFingerprint = fingerprint {
strings("sectional_items", "ExploreTopicalFeedResponse")
custom { method, _ -> method.name == "parseFromJson" }
}

View File

@@ -0,0 +1,33 @@
package app.revanced.patches.instagram.hide.explore
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
val hideExportFeedPatch = bytecodePatch(
name = "Hide explore feed",
description = "Hides posts and reels from the explore/search page.",
use = false
) {
compatibleWith("com.instagram.android")
execute {
exploreResponseJsonParserFingerprint.method.apply {
val sectionalItemStringIndex = exploreResponseJsonParserFingerprint.stringMatches!!.first().index
val sectionalItemStringRegister = getInstruction<OneRegisterInstruction>(sectionalItemStringIndex).registerA
/**
* Replacing the JSON key we want to skip with a random string that is not a valid JSON key.
* This way the feeds array will never be populated.
* Received JSON keys that are not handled are simply ignored, so there are no side effects.
*/
replaceInstruction(
sectionalItemStringIndex,
"const-string v$sectionalItemStringRegister, \"BOGUS\""
)
}
}
}

View File

@@ -0,0 +1,29 @@
package app.revanced.patches.instagram.hide.navigation
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.Opcode
internal val tabCreateButtonsLoopStartFingerprint = fingerprint {
returns("V")
strings("InstagramMainActivity.createTabButtons")
opcodes(
//Loop Start
Opcode.IF_GE, // Check if index is finished (index, size)
//Injection
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT_OBJECT
)
}
internal val tabCreateButtonsLoopEndFingerprint = fingerprint {
returns("V")
strings("InstagramMainActivity.createTabButtons")
opcodes(
Opcode.IPUT_OBJECT,
// Injection Jump
Opcode.ADD_INT_LIT8, //Increase Index
Opcode.GOTO // Jump to loopStart
// LoopEnd
)
}

View File

@@ -0,0 +1,80 @@
package app.revanced.patches.instagram.hide.navigation
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.booleanOption
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.util.findFreeRegister
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
import java.util.logging.Logger
@Suppress("unused")
val hideNavigationButtonsPatch = bytecodePatch(
name = "Hide navigation buttons",
description = "Hides navigation bar buttons, such as the Reels and Create button.",
use = false
) {
compatibleWith("com.instagram.android"("397.1.0.52.81"))
val hideReels by booleanOption(
key = "hideReels",
default = true,
title = "Hide Reels",
description = "Permanently hides the Reels button."
)
val hideCreate by booleanOption(
key = "hideCreate",
default = true,
title = "Hide Create",
description = "Permanently hides the Create button."
)
execute {
if (!hideReels!! && !hideCreate!!) {
return@execute Logger.getLogger(this::class.java.name).warning(
"No hide navigation buttons options are enabled. No changes made."
)
}
tabCreateButtonsLoopStartFingerprint.method.apply {
// Check the current loop index, and skip over adding the
// navigation button view if the index matches a given button.
val startIndex = tabCreateButtonsLoopStartFingerprint.patternMatch!!.startIndex
val endIndex = tabCreateButtonsLoopEndFingerprint.patternMatch!!.endIndex
val insertIndex = startIndex + 1
val loopIndexRegister = getInstruction<TwoRegisterInstruction>(startIndex).registerA
val freeRegister = findFreeRegister(insertIndex, loopIndexRegister)
val instruction = getInstruction(endIndex - 1)
var instructions = buildString {
if (hideCreate!!) {
appendLine(
"""
const v$freeRegister, 0x2
if-eq v$freeRegister, v$loopIndexRegister, :skipAddView
"""
)
}
if (hideReels!!) {
appendLine(
"""
const v$freeRegister, 0x3
if-eq v$freeRegister, v$loopIndexRegister, :skipAddView
"""
)
}
}
addInstructionsWithLabels(
insertIndex,
instructions,
ExternalLabel("skipAddView", instruction)
)
}
}
}

View File

@@ -0,0 +1,17 @@
package app.revanced.patches.instagram.hide.stories
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.Opcode
internal val getOrCreateAvatarViewFingerprint = fingerprint {
parameters()
returns("L")
custom { method, classDef ->
classDef.type == "Lcom/instagram/reels/ui/views/reelavatar/RecyclerReelAvatarView;"
}
opcodes(
Opcode.INVOKE_VIRTUAL,
Opcode.IPUT_OBJECT,
Opcode.INVOKE_VIRTUAL // Add View (Story)
)
}

View File

@@ -0,0 +1,20 @@
package app.revanced.patches.instagram.hide.stories
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.patch.bytecodePatch
@Suppress("unused")
val hideStoriesPatch = bytecodePatch(
name = "Hide Stories from Home",
description = "Hides Stories from the main page, by removing the buttons.",
use = false
) {
compatibleWith("com.instagram.android")
execute {
val addStoryMethod = getOrCreateAvatarViewFingerprint.method // Creates Story
val addStoryEndIndex = getOrCreateAvatarViewFingerprint.patternMatch!!.endIndex
// Remove addView of Story.
addStoryMethod.removeInstruction(addStoryEndIndex)
}
}

View File

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

View File

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

View File

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

View File

@@ -9,7 +9,11 @@ val permanentShufflePatch = bytecodePatch(
description = "Permanently remember your shuffle preference " +
"even if the playlist ends or another track is played."
) {
compatibleWith("com.google.android.apps.youtube.music")
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.29.52"
)
)
execute {
disableShuffleFingerprint.method.addInstruction(0, "return-void")

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,11 @@ 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.returnEarly(true)

View File

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

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.spoofClientPatch
import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch
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(spoofClientPatch)
dependsOn(spoofVideoStreamsPatch)
compatibleWith(MUSIC_PACKAGE_NAME)
}

View File

@@ -1,39 +0,0 @@
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

@@ -1,105 +0,0 @@
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
private 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

@@ -0,0 +1,33 @@
package app.revanced.patches.music.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint
import app.revanced.patches.music.playservice.is_7_33_or_greater
import app.revanced.patches.music.playservice.is_8_11_or_greater
import app.revanced.patches.music.playservice.is_8_15_or_greater
import app.revanced.patches.music.playservice.versionCheckPatch
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/spoof/SpoofVideoStreamsPatch;"
val spoofVideoStreamsPatch = spoofVideoStreamsPatch(
fixMediaFetchHotConfigChanges = { true },
fixMediaFetchHotConfigAlternativeChanges = { is_8_11_or_greater && !is_8_15_or_greater },
fixParsePlaybackResponseFeatureFlag = { is_7_33_or_greater },
block = {
compatibleWith(
"com.google.android.apps.youtube.music"(
"7.29.52"
)
)
dependsOn(sharedExtensionPatch, versionCheckPatch, userAgentClientSpoofPatch)
},
executeBlock = {
musicActivityOnCreateFingerprint.method.addInstruction(
1, // Must use 1 index so context is set by extension patch.
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setClientOrderToUse()V"
)
}
)

View File

@@ -0,0 +1,28 @@
@file:Suppress("ktlint:standard:property-naming")
package app.revanced.patches.music.playservice
import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.findPlayStoreServicesVersion
var is_7_33_or_greater = false
private set
var is_8_11_or_greater = false
private set
var is_8_15_or_greater = false
private set
val versionCheckPatch = resourcePatch(
description = "Uses the Play Store service version to find the major/minor version of the YouTube Music target app.",
) {
execute {
// The app version is missing from the decompiled manifest,
// so instead use the Google Play services version and compare against specific releases.
val playStoreServicesVersion = findPlayStoreServicesVersion()
// All bug fix releases always seem to use the same play store version as the minor version.
is_7_33_or_greater = 245199000 <= playStoreServicesVersion
is_8_11_or_greater = 251199000 <= playStoreServicesVersion
is_8_15_or_greater = 251530000 <= playStoreServicesVersion
}
}

View File

@@ -8,7 +8,7 @@ val removeFreeAccountsLimitPatch = resourcePatch(
name = "Remove free accounts limit",
description = "Removes the limit for maximum free accounts logged in.",
) {
compatibleWith("ch.protonmail.android")
compatibleWith("ch.protonmail.android"("4.15.0"))
execute {
document("res/values/integers.xml").use { document ->

View File

@@ -10,7 +10,7 @@ val removeSentFromSignaturePatch = resourcePatch(
name = "Remove 'Sent from' signature",
description = "Removes the 'Sent from Proton Mail mobile' signature from emails.",
) {
compatibleWith("ch.protonmail.android")
compatibleWith("ch.protonmail.android"("4.15.0"))
execute {
val stringResourceFiles = mutableListOf<File>()

View File

@@ -0,0 +1,17 @@
package app.revanced.patches.reddit.customclients
import app.revanced.patcher.patch.BytecodePatchBuilder
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.bytecodePatch
const val INSTALL_NEW_CLIENT_METHOD = "install(Lokhttp3/OkHttpClient${'$'}Builder;)Lokhttp3/OkHttpClient;"
const val CREATE_NEW_CLIENT_METHOD = "createClient()Lokhttp3/OkHttpClient;"
fun fixRedgifsApiPatch(
extensionPatch: Patch<*>,
block: BytecodePatchBuilder.() -> Unit = {},
) = bytecodePatch(name = "Fix Redgifs API") {
dependsOn(extensionPatch)
block()
}

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.reddit.customclients.baconreader.fix.redgifs
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal val getOkHttpClientFingerprint = fingerprint {
returns("Lokhttp3/OkHttpClient;")
parameters()
custom { method, classDef ->
classDef.type == "Lcom/onelouder/baconreader/media/gfycat/RedGifsManager;" && method.name == "getOkhttpClient"
}
}

View File

@@ -0,0 +1,53 @@
package app.revanced.patches.reddit.customclients.baconreader.fix.redgifs
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.reddit.customclients.INSTALL_NEW_CLIENT_METHOD
import app.revanced.patches.reddit.customclients.baconreader.misc.extension.sharedExtensionPatch
import app.revanced.patches.reddit.customclients.fixRedgifsApiPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/baconreader/FixRedgifsApiPatch;"
@Suppress("unused")
val fixRedgifsApi = fixRedgifsApiPatch(
extensionPatch = sharedExtensionPatch
) {
compatibleWith(
"com.onelouder.baconreader",
"com.onelouder.baconreader.premium",
)
execute {
// region Patch Redgifs OkHttp3 client.
getOkHttpClientFingerprint.method.apply {
// Remove conflicting OkHttp interceptors.
val originalInterceptorInstallIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.NEW_INSTANCE && getReference<TypeReference>()?.type == "Lcom/onelouder/baconreader/media/gfycat/RedGifsManager\$HeaderInterceptor;"
}
removeInstructions(originalInterceptorInstallIndex, 5)
val index = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.name == "build" && reference.definingClass == "Lokhttp3/OkHttpClient\$Builder;"
}
val register = getInstruction<FiveRegisterInstruction>(index).registerC
replaceInstruction(
index,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$INSTALL_NEW_CLIENT_METHOD
"""
)
}
// endregion
}
}

View File

@@ -0,0 +1,6 @@
package app.revanced.patches.reddit.customclients.baconreader.misc.extension
import app.revanced.patches.reddit.customclients.baconreader.misc.extension.hooks.initHook
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
val sharedExtensionPatch = sharedExtensionPatch("baconreader", initHook)

View File

@@ -0,0 +1,9 @@
package app.revanced.patches.reddit.customclients.baconreader.misc.extension.hooks
import app.revanced.patches.shared.misc.extension.extensionHook
internal val initHook = extensionHook {
custom { method, _ ->
method.definingClass == "Lcom/onelouder/baconreader/BaconReader;" && method.name == "onCreate"
}
}

View File

@@ -0,0 +1,20 @@
package app.revanced.patches.reddit.customclients.boostforreddit.fix.redgifs
import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val createOkHttpClientFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE)
opcodes(
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT
)
custom { _, classDef -> classDef.sourceFile == "RedGifsAPIv2.java" }
}

View File

@@ -0,0 +1,38 @@
package app.revanced.patches.reddit.customclients.boostforreddit.fix.redgifs
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.reddit.customclients.CREATE_NEW_CLIENT_METHOD
import app.revanced.patches.reddit.customclients.boostforreddit.misc.extension.sharedExtensionPatch
import app.revanced.patches.reddit.customclients.fixRedgifsApiPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/boostforreddit/FixRedgifsApiPatch;"
@Suppress("unused")
val fixRedgifsApi = fixRedgifsApiPatch(
extensionPatch = sharedExtensionPatch
) {
compatibleWith("com.rubenmayayo.reddit")
execute {
// region Patch Redgifs OkHttp3 client.
createOkHttpClientFingerprint.method.apply {
val index = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.name == "build" && reference.definingClass == "Lokhttp3/OkHttpClient\$Builder;"
}
replaceInstruction(
index,
"""
invoke-static { }, ${EXTENSION_CLASS_DESCRIPTOR}->$CREATE_NEW_CLIENT_METHOD
"""
)
}
// endregion
}
}

View File

@@ -0,0 +1,39 @@
package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.redgifs
import app.revanced.patcher.extensions.InstructionExtensions.instructions
import app.revanced.patcher.fingerprint
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.writeRegister
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction11n
internal val createOkHttpClientFingerprint = fingerprint {
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
returns("V")
parameters()
custom { method, classDef ->
// There are four functions (each creating a client) defined in this file with very similar fingerprints.
// We're looking for the one that only creates one object (the builder) and sets client options true
// (thus never reloading the register with a 0).
classDef.sourceFile == "OkHttpHelper.java" &&
method.instructions.count { it.opcode == Opcode.NEW_INSTANCE } == 1 &&
method.indexOfFirstInstruction {
opcode == Opcode.CONST_4 && writeRegister == 1 && (this as Instruction11n).narrowLiteral == 0
} == -1
}
}
internal val getDefaultUserAgentFingerprint = fingerprint {
custom { method, classDef ->
method.name == "getDefaultUserAgent" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}
internal val getOriginalUserAgentFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Ljava/lang/String;")
parameters()
custom { _, classDef -> classDef.sourceFile == "AccountSingleton.java" }
}

View File

@@ -0,0 +1,56 @@
package app.revanced.patches.reddit.customclients.sync.syncforreddit.fix.redgifs
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
import app.revanced.patches.reddit.customclients.INSTALL_NEW_CLIENT_METHOD
import app.revanced.patches.reddit.customclients.fixRedgifsApiPatch
import app.revanced.patches.reddit.customclients.sync.syncforreddit.extension.sharedExtensionPatch
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.reference.MethodReference
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/syncforreddit/FixRedgifsApiPatch;"
@Suppress("unused")
val fixRedgifsApi = fixRedgifsApiPatch(
extensionPatch = sharedExtensionPatch
) {
compatibleWith(
"com.laurencedawson.reddit_sync",
"com.laurencedawson.reddit_sync.pro",
"com.laurencedawson.reddit_sync.dev",
)
execute {
// region Patch Redgifs OkHttp3 client.
createOkHttpClientFingerprint.method.apply {
val index = indexOfFirstInstructionOrThrow {
val reference = getReference<MethodReference>()
reference?.name == "build" && reference.definingClass == "Lokhttp3/OkHttpClient\$Builder;"
}
val register = getInstruction<FiveRegisterInstruction>(index).registerC
replaceInstruction(
index,
"""
invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->$INSTALL_NEW_CLIENT_METHOD
"""
)
}
getDefaultUserAgentFingerprint.method.apply {
addInstructions(
0,
"""
invoke-static { }, ${getOriginalUserAgentFingerprint.method}
move-result-object v0
return-object v0
"""
)
}
// endregion
}
}

View File

@@ -1,9 +1,12 @@
package app.revanced.patches.shared.misc.spoof
import app.revanced.patcher.fingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
internal val buildInitPlaybackRequestFingerprint = fingerprint {
returns("Lorg/chromium/net/UrlRequest\$Builder;")
@@ -35,8 +38,15 @@ internal val buildPlayerRequestURIFingerprint = fingerprint {
internal val buildRequestFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Lorg/chromium/net/UrlRequest;")
returns("Lorg/chromium/net/UrlRequest") // UrlRequest; or UrlRequest$Builder;
custom { methodDef, _ ->
if (methodDef.indexOfFirstInstruction {
val reference = getReference<MethodReference>()
reference?.name == "newUrlRequestBuilder"
} < 0) {
return@custom false
}
// Different targets have slightly different parameters
// Earlier targets have parameters:
@@ -58,12 +68,22 @@ internal val buildRequestFingerprint = fingerprint {
// Lorg/chromium/net/UrlRequest\$Callback;
// L
// 20.16+ uses a refactored and extracted method:
// L
// Ljava/util/Map;
// [B
// L
// Lorg/chromium/net/UrlRequest$Callback;
// L
val parameterTypes = methodDef.parameterTypes
(parameterTypes.size == 7 || parameterTypes.size == 8) &&
parameterTypes[1] == "Ljava/util/Map;" // URL headers.
val parameterTypesSize = parameterTypes.size
(parameterTypesSize == 6 || parameterTypesSize == 7 || parameterTypesSize == 8) &&
parameterTypes[1] == "Ljava/util/Map;" // URL headers.
}
}
internal val protobufClassParseByteBufferFingerprint = fingerprint {
accessFlags(AccessFlags.PROTECTED, AccessFlags.STATIC)
returns("L")
@@ -148,7 +168,8 @@ internal val mediaFetchHotConfigFingerprint = fingerprint {
literal { MEDIA_FETCH_HOT_CONFIG_FEATURE_FLAG }
}
// 20.10+
// YT 20.10+, YT Music 8.11 - 8.14.
// Flag is missing in YT Music 8.15+, and it is not known if a replacement flag/feature exists.
internal const val MEDIA_FETCH_HOT_CONFIG_ALTERNATIVE_FEATURE_FLAG = 45683169L
internal val mediaFetchHotConfigAlternativeFingerprint = fingerprint {
@@ -162,7 +183,6 @@ internal val mediaFetchHotConfigAlternativeFingerprint = fingerprint {
internal const val PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG = 45665455L
internal val playbackStartDescriptorFeatureFlagFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters()
returns("Z")
literal { PLAYBACK_START_CHECK_ENDPOINT_USED_FEATURE_FLAG }

View File

@@ -10,6 +10,7 @@ import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
import app.revanced.patches.all.misc.resources.addResourcesPatch
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
import app.revanced.util.findFreeRegister
import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference
@@ -53,9 +54,8 @@ fun spoofVideoStreamsPatch(
// region Block /initplayback requests to fall back to /get_watch requests.
val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex
buildInitPlaybackRequestFingerprint.method.apply {
val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex
val targetRegister = getInstruction<OneRegisterInstruction>(moveUriStringIndex).registerA
addInstructions(
@@ -63,7 +63,7 @@ fun spoofVideoStreamsPatch(
"""
invoke-static { v$targetRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockInitPlaybackRequest(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$targetRegister
""",
"""
)
}
@@ -71,9 +71,8 @@ fun spoofVideoStreamsPatch(
// region Block /get_watch requests to fall back to /player requests.
val invokeToStringIndex = buildPlayerRequestURIFingerprint.patternMatch!!.startIndex
buildPlayerRequestURIFingerprint.method.apply {
val invokeToStringIndex = buildPlayerRequestURIFingerprint.patternMatch!!.startIndex
val uriRegister = getInstruction<FiveRegisterInstruction>(invokeToStringIndex).registerC
addInstructions(
@@ -81,7 +80,7 @@ fun spoofVideoStreamsPatch(
"""
invoke-static { v$uriRegister }, $EXTENSION_CLASS_DESCRIPTOR->blockGetWatchRequest(Landroid/net/Uri;)Landroid/net/Uri;
move-result-object v$uriRegister
""",
"""
)
}
@@ -178,9 +177,9 @@ fun spoofVideoStreamsPatch(
:disabled
return-void
""",
"""
)
},
}
)
}
@@ -199,17 +198,17 @@ fun spoofVideoStreamsPatch(
addInstructions(
targetIndex,
"""
# Field a: Stream uri.
# Field c: Http method.
# Field d: Post data.
move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register.
iget-object v1, v0, $definingClass->a:Landroid/net/Uri;
iget v2, v0, $definingClass->c:I
iget-object v3, v0, $definingClass->d:[B
invoke-static { v1, v2, v3 }, $EXTENSION_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B
move-result-object v1
iput-object v1, v0, $definingClass->d:[B
""",
# Field a: Stream uri.
# Field c: Http method.
# Field d: Post data.
move-object v0, p0 # method has over 15 registers and must copy p0 to a lower register.
iget-object v1, v0, $definingClass->a:Landroid/net/Uri;
iget v2, v0, $definingClass->c:I
iget-object v3, v0, $definingClass->d:[B
invoke-static { v1, v2, v3 }, $EXTENSION_CLASS_DESCRIPTOR->removeVideoPlaybackPostBody(Landroid/net/Uri;I[B)[B
move-result-object v1
iput-object v1, v0, $definingClass->d:[B
"""
)
}

View File

@@ -187,11 +187,16 @@ val customThemePatch = resourcePatch(
}
// Login screen gradient.
document("res/drawable/start_screen_gradient.xml").use { document ->
val gradientNode = document.getElementsByTagName("gradient").item(0) as Element
try {
document("res/drawable/start_screen_gradient.xml").use { document ->
val gradientNode = document.getElementsByTagName("gradient").item(0) as Element
gradientNode.setAttribute("android:startColor", "@color/gray_7")
gradientNode.setAttribute("android:endColor", "@color/gray_7")
gradientNode.setAttribute("android:startColor", "@color/gray_7")
gradientNode.setAttribute("android:endColor", "@color/gray_7")
}
} catch (_: Exception) {
// Fails for 9.0.66+
// printWarn("Failed to locate start_screen_gradient.xml, skipping modification.")
}
}
}

View File

@@ -1,145 +0,0 @@
package app.revanced.patches.spotify.misc
import app.revanced.patcher.fingerprint
import app.revanced.patcher.patch.BytecodePatchContext
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
context(BytecodePatchContext)
internal val accountAttributeFingerprint get() = fingerprint {
custom { _, classDef -> classDef.type == "Lcom/spotify/remoteconfig/internal/AccountAttribute;" }
}
context(BytecodePatchContext)
internal val productStateProtoGetMapFingerprint get() = fingerprint {
returns("Ljava/util/Map;")
custom { _, classDef -> classDef.type == "Lcom/spotify/remoteconfig/internal/ProductStateProto;" }
}
internal val buildQueryParametersFingerprint = fingerprint {
strings("trackRows", "device_type:tablet")
}
internal val contextMenuViewModelClassFingerprint = fingerprint {
strings("ContextMenuViewModel(header=")
}
/**
* Used in versions older than "9.0.60.128".
*/
internal val oldContextMenuViewModelAddItemFingerprint = fingerprint {
parameters("L")
returns("V")
custom { method, _ ->
method.indexOfFirstInstruction {
getReference<MethodReference>()?.name == "add"
} >= 0
}
}
internal val contextMenuViewModelConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
}
/**
* Used to find the interface name of a context menu item.
*/
internal val removeItemFromPlaylistContextMenuItemClassFingerprint = fingerprint {
strings("spotify:playlist:", "REMOVE_TRACK")
}
internal const val CONTEXT_MENU_ITEM_CLASS_DESCRIPTOR_PLACEHOLDER = "Lapp/revanced/ContextMenuItemPlaceholder;"
internal val extensionFilterContextMenuItemsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Ljava/util/List;")
parameters("Ljava/util/List;")
custom { method, classDef ->
method.name == "filterContextMenuItems" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
}
}
internal val getViewModelFingerprint = fingerprint {
custom { method, _ -> method.name == "getViewModel" }
}
internal val contextFromJsonFingerprint = fingerprint {
opcodes(
Opcode.INVOKE_STATIC,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_STATIC
)
custom { method, classDef ->
method.name == "fromJson" &&
classDef.type.endsWith("voiceassistants/playermodels/ContextJsonAdapter;")
}
}
internal val readPlayerOptionOverridesFingerprint = fingerprint {
custom { method, classDef ->
method.name == "readPlayerOptionOverrides" &&
classDef.type.endsWith("voiceassistants/playermodels/PreparePlayOptionsJsonAdapter;")
}
}
internal val protobufListsFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
custom { method, _ -> method.name == "emptyProtobufList" }
}
internal val abstractProtobufListEnsureIsMutableFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
parameters()
returns("V")
custom { method, _ ->
method.indexOfFirstInstruction {
getReference<TypeReference>()?.type == "Ljava/lang/UnsupportedOperationException;"
} >= 0
}
}
internal fun structureGetSectionsFingerprint(className: String) = fingerprint {
custom { method, classDef ->
classDef.type.endsWith(className) && method.indexOfFirstInstruction {
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.name == "sections_"
} >= 0
}
}
internal val homeSectionFingerprint = fingerprint {
custom { _, classDef -> classDef.type.endsWith("homeapi/proto/Section;") }
}
internal val homeStructureGetSectionsFingerprint =
structureGetSectionsFingerprint("homeapi/proto/HomeStructure;")
internal val browseSectionFingerprint = fingerprint {
custom { _, classDef-> classDef.type.endsWith("browsita/v1/resolved/Section;") }
}
internal val browseStructureGetSectionsFingerprint =
structureGetSectionsFingerprint("browsita/v1/resolved/BrowseStructure;")
internal fun reactivexFunctionApplyWithClassInitFingerprint(className: String) = fingerprint {
returns("Ljava/lang/Object;")
parameters("Ljava/lang/Object;")
custom { method, _ ->
method.name == "apply" && method.indexOfFirstInstruction {
opcode == Opcode.NEW_INSTANCE && getReference<TypeReference>()?.type?.endsWith(className) == true
} >= 0
}
}
internal const val PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME = "FetchMessageRequest;"
internal val pendragonJsonFetchMessageRequestFingerprint =
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_JSON_FETCH_MESSAGE_REQUEST_CLASS_NAME)
internal const val PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME = "FetchMessageListRequest;"
internal val pendragonProtoFetchMessageListRequestFingerprint =
reactivexFunctionApplyWithClassInitFingerprint(PENDRAGON_PROTO_FETCH_MESSAGE_LIST_REQUEST_CLASS_NAME)

View File

@@ -12,9 +12,9 @@ import app.revanced.util.returnEarly
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/fix/SpoofClientPatch;"
@Deprecated("Patch no longer functions")
@Suppress("unused")
val spoofClientPatch = bytecodePatch(
name = "Spoof client",
description = "Spoofs the client to fix various functions of the app.",
) {
val requestListenerPort by intOption(

View File

@@ -1,12 +1,9 @@
package app.revanced.patches.spotify.navbar
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.spotify.misc.unlockPremiumPatch
@Deprecated("Superseded by unlockPremiumPatch", ReplaceWith("unlockPremiumPatch"))
@Deprecated("Obsolete and will be deleted soon")
@Suppress("unused")
val premiumNavbarTabPatch = bytecodePatch(
description = "Hides the premium tab from the navigation bar.",
) {
dependsOn(unlockPremiumPatch)
}
)

View File

@@ -0,0 +1,13 @@
package app.revanced.patches.viber.ads
import app.revanced.patcher.fingerprint
internal val adsFreeFingerprint = fingerprint {
returns("I")
parameters()
custom { method, classDef ->
classDef.type.contains("com/viber/voip/feature/viberplus") &&
classDef.superclass?.contains("com/viber/voip/core/feature") == true && // Must extend com.viber.voip.core.feature.?
classDef.methods.count() == 1
}
}

View File

@@ -0,0 +1,17 @@
package app.revanced.patches.viber.ads
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.returnEarly
@Suppress("unused")
val hideAdsPatch = bytecodePatch(
name = "Hide Ads",
description = "Hides ad banners between chats.",
) {
compatibleWith("com.viber.voip"("25.9.2.0"))
execute {
// Return 1 (true) indicating ads should be disabled.
adsFreeFingerprint.method.returnEarly(1)
}
}

View File

@@ -89,7 +89,7 @@ val downloadsPatch = bytecodePatch(
// Main activity is used to launch downloader intent.
mainActivityOnCreateFingerprint.method.addInstruction(
1,
0,
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->activityCreated(Landroid/app/Activity;)V"
)

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.layout.seekbar
import app.revanced.patcher.fingerprint
import app.revanced.patches.youtube.shared.YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
import app.revanced.util.containsLiteralInstruction
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstruction
@@ -103,7 +104,7 @@ internal val launchScreenLayoutTypeFingerprint = fingerprint {
custom { method, _ ->
val firstParameter = method.parameterTypes.firstOrNull()
// 19.25 - 19.45
(firstParameter == "Lcom/google/android/apps/youtube/app/watchwhile/MainActivity;"
(firstParameter == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
|| firstParameter == "Landroid/app/Activity;") // 19.46+
&& method.containsLiteralInstruction(launchScreenLayoutTypeLotteFeatureFlag)
}

View File

@@ -68,7 +68,7 @@ val shortsAutoplayPatch = bytecodePatch(
// Main activity is used to check if app is in pip mode.
mainActivityOnCreateFingerprint.method.addInstruction(
1,
0,
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->setMainActivity(Landroid/app/Activity;)V",
)

View File

@@ -90,8 +90,8 @@ val openShortsInRegularPlayerPatch = bytecodePatch(
// Activity is used as the context to launch an Intent.
mainActivityOnCreateFingerprint.method.addInstruction(
1,
"invoke-static/range { p0 .. p0 }, ${EXTENSION_CLASS_DESCRIPTOR}->" +
0,
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
"setMainActivity(Landroid/app/Activity;)V",
)

View File

@@ -1,8 +1,11 @@
package app.revanced.patches.youtube.layout.sponsorblock
import app.revanced.patcher.fingerprint
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionReversed
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.Method
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
@@ -56,3 +59,20 @@ internal val rectangleFieldInvalidatorFingerprint = fingerprint {
reference?.parameterTypes?.size == 1 && reference.name == "invalidate" // the reference is the invalidate(..) method
}
}
internal val adProgressTextViewVisibilityFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
returns("V")
parameters("Z")
custom { method, _ ->
indexOfAdProgressTextViewVisibilityInstruction(method) >= 0
}
}
internal fun indexOfAdProgressTextViewVisibilityInstruction(method: Method) =
method.indexOfFirstInstructionReversed {
val reference = getReference<MethodReference>()
reference?.definingClass ==
"Lcom/google/android/libraries/youtube/ads/player/ui/AdProgressTextView;"
&& reference.name =="setVisibility"
}

View File

@@ -252,5 +252,16 @@ val sponsorBlockPatch = bytecodePatch(
}
} ?: throw PatchException("Could not find the method which contains the replaceMeWith* strings")
}
adProgressTextViewVisibilityFingerprint.method.apply {
val index = indexOfAdProgressTextViewVisibilityInstruction(this)
val register = getInstruction<FiveRegisterInstruction>(index).registerD
addInstructionsAtControlFlowLabel(
index,
"invoke-static { v$register }, $EXTENSION_SEGMENT_PLAYBACK_CONTROLLER_CLASS_DESCRIPTOR->setAdProgressTextVisibility(I)V"
)
}
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.patches.youtube.layout.theme
import app.revanced.patcher.fingerprint
import app.revanced.patches.youtube.shared.YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
import app.revanced.util.literal
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
@@ -37,6 +38,6 @@ internal val splashScreenStyleFingerprint = fingerprint {
parameters("Landroid/os/Bundle;")
literal { SPLASH_SCREEN_STYLE_FEATURE_FLAG }
custom { method, classDef ->
method.name == "onCreate" && classDef.endsWith("/MainActivity;")
method.name == "onCreate" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
}
}

View File

@@ -40,9 +40,7 @@ val announcementsPatch = bytecodePatch(
)
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,
0,
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->showAnnouncement(Landroid/app/Activity;)V",
)
}

View File

@@ -34,12 +34,7 @@ val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch(
addResources("youtube", "misc.dns.checkWatchHistoryDomainNameResolutionPatch")
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
// unless a third patch is added that this patch and GmsCoreSupport depend on to manage
// the order of the patches.
1,
0,
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->checkDnsResolver(Landroid/app/Activity;)V",
)
}

View File

@@ -3,4 +3,5 @@ package app.revanced.patches.youtube.misc.extension
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
import app.revanced.patches.youtube.misc.extension.hooks.*
val sharedExtensionPatch = sharedExtensionPatch("youtube", applicationInitHook)
val sharedExtensionPatch = sharedExtensionPatch("youtube",
applicationInitHook, applicationInitOnCrateHook)

View File

@@ -1,11 +1,23 @@
package app.revanced.patches.youtube.misc.extension.hooks
import app.revanced.patches.shared.misc.extension.extensionHook
import app.revanced.patches.youtube.shared.YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
/**
* Hooks the context when the app is launched as a regular application (and is not an embedded video playback).
*/
// Extension context is the Activity itself.
internal val applicationInitHook = extensionHook {
// Does _not_ resolve to the YouTube main activity.
// Required as some hooked code runs before the main activity is launched.
strings("Application creation", "Application.onCreate")
}
internal val applicationInitOnCrateHook = extensionHook {
returns("V")
parameters("Landroid/os/Bundle;")
custom { method, classDef ->
method.name == "onCreate" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
}
}

View File

@@ -127,8 +127,7 @@ val navigationBarHookPatch = bytecodePatch(description = "Hooks the active navig
// Litho filtering based on navigation tab before the tab is updated.
mainActivityOnBackPressedFingerprint.method.addInstruction(
0,
"invoke-static { p0 }, " +
"$EXTENSION_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)V",
"invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)V",
)
// Hook the search bar.

View File

@@ -3,7 +3,7 @@
package app.revanced.patches.youtube.misc.playservice
import app.revanced.patcher.patch.resourcePatch
import app.revanced.util.findElementByAttributeValueOrThrow
import app.revanced.util.findPlayStoreServicesVersion
@Deprecated("19.34.42 is the lowest supported version")
var is_19_03_or_greater = false
@@ -77,12 +77,7 @@ val versionCheckPatch = resourcePatch(
execute {
// The app version is missing from the decompiled manifest,
// so instead use the Google Play services version and compare against specific releases.
val playStoreServicesVersion = document("res/values/integers.xml").use { document ->
document.documentElement.childNodes.findElementByAttributeValueOrThrow(
"name",
"google_play_services_version",
).textContent.toInt()
}
val playStoreServicesVersion = findPlayStoreServicesVersion()
// All bug fix releases always seem to use the same play store version as the minor version.
is_19_03_or_greater = 240402000 <= playStoreServicesVersion

View File

@@ -1,5 +1,6 @@
package app.revanced.patches.youtube.misc.spoof
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patches.all.misc.resources.addResources
import app.revanced.patches.shared.misc.settings.preference.ListPreference
import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference
@@ -13,56 +14,69 @@ import app.revanced.patches.youtube.misc.playservice.is_20_14_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
val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
compatibleWith(
"com.google.android.youtube"(
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
"20.13.41",
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/spoof/SpoofVideoStreamsPatch;"
val spoofVideoStreamsPatch = spoofVideoStreamsPatch(
block = {
compatibleWith(
"com.google.android.youtube"(
"19.34.42",
"19.43.41",
"19.47.53",
"20.07.39",
"20.12.46",
"20.13.41",
)
)
)
dependsOn(
userAgentClientSpoofPatch,
settingsPatch,
versionCheckPatch
)
}, {
is_19_34_or_greater
}, {
// In 20.14 the flag was merged with 20.03 start playback flag.
is_20_10_or_greater && !is_20_14_or_greater
}, {
is_20_03_or_greater
}, {
addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch")
dependsOn(
userAgentClientSpoofPatch,
settingsPatch,
versionCheckPatch
)
},
fixMediaFetchHotConfigChanges = {
is_19_34_or_greater
},
fixMediaFetchHotConfigAlternativeChanges = {
// In 20.14 the flag was merged with 20.03 start playback flag.
is_20_10_or_greater && !is_20_14_or_greater
},
fixParsePlaybackResponseFeatureFlag = {
is_20_03_or_greater
},
executeBlock = {
addResources("youtube", "misc.fix.playback.spoofVideoStreamsPatch")
PreferenceScreen.MISC.addPreferences(
PreferenceScreenPreference(
key = "revanced_spoof_video_streams_screen",
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
preferences = setOf(
SwitchPreference("revanced_spoof_video_streams"),
ListPreference("revanced_spoof_video_streams_client_type"),
NonInteractivePreference(
// Requires a key and title but the actual text is chosen at runtime.
key = "revanced_spoof_video_streams_about_android",
tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference"
),
ListPreference(
key = "revanced_spoof_video_streams_language",
// Language strings are declared in Setting patch.
entriesKey = "revanced_language_entries",
entryValuesKey = "revanced_language_entry_values",
tag = "app.revanced.extension.shared.settings.preference.SortedListPreference"
),
SwitchPreference("revanced_spoof_video_streams_ios_force_avc"),
SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"),
),
),
)
})
PreferenceScreen.MISC.addPreferences(
PreferenceScreenPreference(
key = "revanced_spoof_video_streams_screen",
sorting = PreferenceScreenPreference.Sorting.UNSORTED,
preferences = setOf(
SwitchPreference("revanced_spoof_video_streams"),
ListPreference("revanced_spoof_video_streams_client_type"),
NonInteractivePreference(
// Requires a key and title but the actual text is chosen at runtime.
key = "revanced_spoof_video_streams_about_android",
tag = "app.revanced.extension.youtube.settings.preference.SpoofStreamingDataSideEffectsPreference"
),
ListPreference(
key = "revanced_spoof_video_streams_language",
// Language strings are declared in Setting patch.
entriesKey = "revanced_language_entries",
entryValuesKey = "revanced_language_entry_values",
tag = "app.revanced.extension.shared.settings.preference.SortedListPreference"
),
SwitchPreference("revanced_spoof_streaming_data_stats_for_nerds"),
)
)
)
mainActivityOnCreateFingerprint.method.addInstruction(
0,
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setClientOrderToUse()V"
)
}
)

View File

@@ -4,6 +4,8 @@ import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal const val YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE = "Lcom/google/android/apps/youtube/app/watchwhile/MainActivity;"
internal val conversionContextFingerprintToString = fingerprint {
parameters()
strings(
@@ -48,7 +50,7 @@ internal val mainActivityConstructorFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
parameters()
custom { _, classDef ->
classDef.endsWith("/MainActivity;")
classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
}
}
@@ -57,7 +59,7 @@ internal val mainActivityOnBackPressedFingerprint = fingerprint {
returns("V")
parameters()
custom { method, classDef ->
method.name == "onBackPressed" && classDef.endsWith("/MainActivity;")
method.name == "onBackPressed" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
}
}
@@ -65,7 +67,7 @@ internal val mainActivityOnCreateFingerprint = fingerprint {
returns("V")
parameters("Landroid/os/Bundle;")
custom { method, classDef ->
method.name == "onCreate" && classDef.endsWith("/MainActivity;")
method.name == "onCreate" && classDef.type == YOUTUBE_MAIN_ACTIVITY_CLASS_TYPE
}
}

View File

@@ -1,5 +1,6 @@
package app.revanced.patches.youtube.video.audio
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
@@ -14,6 +15,7 @@ import app.revanced.patches.youtube.misc.playservice.is_20_07_or_greater
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
import app.revanced.patches.youtube.misc.settings.settingsPatch
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
import app.revanced.util.findMethodFromToString
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.insertLiteralOverride
@@ -55,10 +57,12 @@ val forceOriginalAudioPatch = bytecodePatch(
addResources("youtube", "video.audio.forceOriginalAudioPatch")
PreferenceScreen.VIDEO.addPreferences(
SwitchPreference(
key = "revanced_force_original_audio",
tag = "app.revanced.extension.youtube.settings.preference.ForceOriginalAudioSwitchPreference"
)
SwitchPreference("revanced_force_original_audio")
)
mainActivityOnCreateFingerprint.method.addInstruction(
0,
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->setPreferredLanguage()V"
)
// Disable feature flag that ignores the default track flag

View File

@@ -132,6 +132,7 @@ internal val Instruction.registersUsed: List<Int>
get() = when (this) {
is FiveRegisterInstruction -> {
when (registerCount) {
0 -> listOf()
1 -> listOf(registerC)
2 -> listOf(registerC, registerD)
3 -> listOf(registerC, registerD, registerE)

View File

@@ -178,3 +178,15 @@ internal fun Element.copyAttributesFrom(oldContainer: Element) {
setAttribute(attr.name, attr.value)
}
}
/**
* @return The play store services version.
*/
internal fun ResourcePatchContext.findPlayStoreServicesVersion(): Int =
document("res/values/integers.xml").use { document ->
document.documentElement.childNodes.findElementByAttributeValueOrThrow(
"name",
"google_play_services_version",
).textContent.toInt()
}

View File

@@ -700,9 +700,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_flyout_audio_track_summary_on">تم إخفاء قائمة المقطع الصوتي</string>
<string name="revanced_hide_player_flyout_audio_track_summary_off">يتم عرض قائمة المقطع الصوتي</string>
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'. -->
<string name="revanced_hide_player_flyout_audio_track_not_available">"تم إخفاء قائمة المقطع الصوتي
<string name="revanced_hide_player_flyout_audio_track_not_available">"قائمة المسارات الصوتية مخفية
لعرض قائمة المقطع الصوتي، غيّر 'Spoof Video Streams' إلى iOS TV"</string>
قائمة المسارات الصوتية غير متاحة عندما يكون \"تزوير تدفقات الفيديو\" مُمكَّنًا"</string>
<!-- '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">تم إخفاء قائمة المشاهدة في الوضع الافتراضي</string>
@@ -1075,10 +1075,12 @@ Second \"item\" text"</string>
<string name="revanced_sb_segments_intro_sum">فاصل زمني بدون محتوى فعلي. قد يكون توقفًا مؤقتًا، أو إطارًا ثابتًا، أو رسومًا متحركة متكررة. لا يتضمن انتقالات تحتوي على معلومات</string>
<string name="revanced_sb_segments_outro">الخاتمة / تترات النهاية</string>
<string name="revanced_sb_segments_outro_sum">تتر النهاية أو عندما تظهر بطاقات نهاية YouTube، نهايات غير منطوقة. ليس للاستنتاجات مع المعلومات</string>
<string name="revanced_sb_segments_preview">معاينة/موجز/ربط</string>
<string name="revanced_sb_segments_hook">مقدمة / تحيات</string>
<string name="revanced_sb_segments_hook_sum">مقاطع دعائية مروية للفيديو القادم، تحيات ووداعات. لا تشمل الأقسام التي تضيف محتوى إضافيًا</string>
<string name="revanced_sb_segments_preview">معاينة / ملخص</string>
<string name="revanced_sb_segments_preview_sum">مجموعة من المقاطع التي توضح ما هو قادم أو ما حدث في الفيديو أو في مقاطع فيديو أخرى من السلسلة، حيث تتكرر جميع المعلومات في مكان آخر</string>
<string name="revanced_sb_segments_filler">خارج الموضوع/النكات</string>
<string name="revanced_sb_segments_filler_sum">تم إضافة مشاهد ملتقطة خارج الموضوع أو الفكاهة التي ليست مطلوبة لفهم المحتوى الرئيسي للفيديو. لا تتضمن مقاطع توفر تَعبِير أو تفاصيل الخلفية</string>
<string name="revanced_sb_segments_filler">استطراد / نكات</string>
<string name="revanced_sb_segments_filler_sum">مشاهد أو نكات جانبية غير ضرورية لفهم المحتوى الرئيسي للفيديو. لا تشمل الأقسام التي توفر سياقًا أو تفاصيل خلفية</string>
<string name="revanced_sb_segments_nomusic">الموسيقى: مقطع غير موسيقي</string>
<string name="revanced_sb_segments_nomusic_sum">فقط للاستخدام في المقاطع الموسيقية. أقسام المقاطع الموسيقية بدون موسيقى، والتي لم يتم تغطيتها بالفعل من قبل فئة أخرى</string>
<string name="revanced_sb_skip_button_compact">تخطي</string>
@@ -1091,10 +1093,11 @@ Second \"item\" text"</string>
<string name="revanced_sb_skip_button_intro_middle">تخطي الفاصل</string>
<string name="revanced_sb_skip_button_intro_end">تخطي الفاصل</string>
<string name="revanced_sb_skip_button_outro">تخطي الخاتمة</string>
<string name="revanced_sb_skip_button_hook">تخطي المقدمة</string>
<string name="revanced_sb_skip_button_preview_beginning">تخطي النظرة العامة</string>
<string name="revanced_sb_skip_button_preview_middle">تخطي النظرة العامة</string>
<string name="revanced_sb_skip_button_preview_end">تخطي الملخص</string>
<string name="revanced_sb_skip_button_filler">تخطي مقطع غير ذي صلة</string>
<string name="revanced_sb_skip_button_filler">تخطي الجزء الجانبي</string>
<string name="revanced_sb_skip_button_nomusic">تخطي غير الموسيقى</string>
<string name="revanced_sb_skip_button_unsubmitted">تخطي المقطع</string>
<string name="revanced_sb_skipped_sponsor">تم تخطي الراعي</string>
@@ -1105,10 +1108,11 @@ Second \"item\" text"</string>
<string name="revanced_sb_skipped_intro_middle">تم تخطي الفاصل</string>
<string name="revanced_sb_skipped_intro_end">تم تخطي الفاصل</string>
<string name="revanced_sb_skipped_outro">تم تخطي الخاتمة</string>
<string name="revanced_sb_skipped_hook">تم تخطي المقدمة</string>
<string name="revanced_sb_skipped_preview_beginning">تم تخطي النظرة العامة</string>
<string name="revanced_sb_skipped_preview_middle">تم تخطي النظرة العامة</string>
<string name="revanced_sb_skipped_preview_end">تم تخطي الملخص</string>
<string name="revanced_sb_skipped_filler">تم تخطي مقطع غير ذي صلة</string>
<string name="revanced_sb_skipped_filler">تم تخطي الجزء الجانبي</string>
<string name="revanced_sb_skipped_nomusic">تم تخطي جزء غير موسيقي</string>
<string name="revanced_sb_skipped_unsubmitted">تم تخطي المقطع الغير المرسل</string>
<string name="revanced_sb_skipped_multiple_segments">تم تخطي عدة مقاطع</string>
@@ -1531,32 +1535,25 @@ Second \"item\" text"</string>
<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_on">"تدفقات الفيديو مزورة
إذا كنت مستخدمًا لـ YouTube Premium، فقد لا يكون هذا الإعداد مطلوبًا"</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_type_title">العميل الافتراضي</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">فرض iOS AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">يتم فرض ترميز فيديو على AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">يتم تحديد ترميز الفيديو تلقائيًا</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"قد يؤدي تمكين هذا إلى تحسين عمر البطارية وإصلاح تقطيع التشغيل.
AVC لديه حد أقصى للدقة 1080p، لا يتوفر ترميز الصوت Opus، وسوف يستخدم تشغيل الفيديو بيانات إنترنت أكثر من VP9 أو AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">الآثار الجانبية لمحاكاة هوية iOS</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"• قد لا يتم تشغيل الأفلام أو الفيديوهات المدفوعة
• مستوى الصوت الثابت غير متوفر
• تنتهي الفيديوهات قبل ب 1 ثانية"</string>
<string name="revanced_spoof_video_streams_about_title">الآثار الجانبية للتزوير</string>
<string name="revanced_spoof_video_streams_about_android_title">الآثار الجانبية لمحاكاة هوية Android</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• قائمة المقطع الصوتي مفقودة
• مستوى الصوت الثابت غير متاح
• فرض الصوت الأصلي غير متوفر"</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• قائمة المسارات الصوتية مفقودة
• مستوى الصوت المستقر غير متاح"</string>
<string name="revanced_spoof_video_streams_about_experimental">• عميل تجريبي وقد يتوقف عن العمل في أي وقت</string>
<string name="revanced_spoof_video_streams_about_no_av1">• لا يوجد ترميز الفيديو AV1</string>
<string name="revanced_spoof_video_streams_about_kids_videos">• قد لا يتم تشغيل الفيديوهات المخصصة للأطفال عند تسجيل الخروج أو عند استخدام وضع التصفح المتخفي</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">عرض في إحصاءات تقنية</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">يتم عرض نوع العميل في إحصاءات تقنية</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">تم إخفاء نوع العميل في إحصاءات تقنية</string>
<string name="revanced_spoof_video_streams_language_title">لغة البث الصوتي الافتراضية للواقع الافتراضي VR</string>
<string name="revanced_spoof_video_streams_language_title">لغة بث الصوت</string>
</patch>
</app>
<app id="twitch">

View File

@@ -700,9 +700,9 @@ Bu seçimi dəyişdirmə işə düşmürsə, Gizli rejimə keçməyə çalışı
<string name="revanced_hide_player_flyout_audio_track_summary_on">Səs axını menyusu gizlidir</string>
<string name="revanced_hide_player_flyout_audio_track_summary_off">Səs axını menyusu göstərilir</string>
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'. -->
<string name="revanced_hide_player_flyout_audio_track_not_available">"Audio trek seçimi gizlədilib
<string name="revanced_hide_player_flyout_audio_track_not_available">"Səs treki menyusu gizlidir
Audio trek seçimin göstərmək üçün \"Video axınları saxtalaşdır\"ı iOS TV-yə dəyiş"</string>
\"Video yayımları saxtalaşdır\" aktivləşdikdə səs trek menyusu mövcud deyil "</string>
<!-- '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-da İzləni\" gizlət</string>
<string name="revanced_hide_player_flyout_watch_in_vr_summary_on">VR menyusunda izləmə gizlidir</string>
@@ -1074,10 +1074,12 @@ Sizin istifadəçi Id-niz parol kimidir və heç vaxt paylaşmaq olmaz.
<string name="revanced_sb_segments_intro_sum">Həqiqi məzmunu olmayan aralıq. Fasilə, statik kadr və ya təkrarlanan animasiya ola bilər. Məlumat ehtiva edən keçidlər daxil deyil</string>
<string name="revanced_sb_segments_outro">Son kartlar / Zaminlər</string>
<string name="revanced_sb_segments_outro_sum">Zaminlər və ya YouTube bitiş kartları görünəndə. Məlumat daxil olan nəticələr üçün deyil</string>
<string name="revanced_sb_segments_preview">Önizləmə/Anons/Qısa</string>
<string name="revanced_sb_segments_hook">Giriş / Salamlar</string>
<string name="revanced_sb_segments_hook_sum">Yaxınlaşan video üçün danışılan kadrlar, salamlama və sağollaşma. Əlavə məzmun əlavə edən bölmələr daxil deyil</string>
<string name="revanced_sb_segments_preview">Önizləmə / Anons</string>
<string name="revanced_sb_segments_preview_sum">Videoda və ya seriyanın digər videolarında nə gözlənildiyini və ya baş verdiyini göstərən, bütün məlumatların başqa yerdə təkrarlandığı bölüm toplusu</string>
<string name="revanced_sb_segments_filler">Əlaqəsiz/Zarafatlar</string>
<string name="revanced_sb_segments_filler_sum">Videonun əsas məzmununu başa düşmək tələb olunmayan yalnız əlaqəsiz və ya yumor üçün əlavə edilmiş təsiredici səhnələr. Məzmun və ya arxa plan detallarınıqdim edən bölümlər daxil deyil</string>
<string name="revanced_sb_segments_filler">Əlaqəsiz / Zarafatlar</string>
<string name="revanced_sb_segments_filler_sum">Videonun əsas məzmunun başa düşmək tələb olunmayan əlaqəsiz səhnələr və ya zarafatlar. Məzmun və ya arxa plan təfərrüatlarınımin edən bölmələr daxil deyil</string>
<string name="revanced_sb_segments_nomusic">Musiqi: Musiqi olmayan bölmə</string>
<string name="revanced_sb_segments_nomusic_sum">Yalnız musiqi videolarında istifadə üçün. Artıq başqa kateqoriyaya aid edilməyən musiqisiz musiqi videoları bölmələri</string>
<string name="revanced_sb_skip_button_compact">Ötür</string>
@@ -1090,10 +1092,11 @@ Sizin istifadəçi Id-niz parol kimidir və heç vaxt paylaşmaq olmaz.
<string name="revanced_sb_skip_button_intro_middle">Fasiləni ötür</string>
<string name="revanced_sb_skip_button_intro_end">Fasiləni ötür</string>
<string name="revanced_sb_skip_button_outro">Bitişi ötür</string>
<string name="revanced_sb_skip_button_hook">Girişi ötür</string>
<string name="revanced_sb_skip_button_preview_beginning">Önizləməni ötür</string>
<string name="revanced_sb_skip_button_preview_middle">Önizləməni ötür</string>
<string name="revanced_sb_skip_button_preview_end">Anonsu ötür</string>
<string name="revanced_sb_skip_button_filler">Əlaqəsiz hissəni ötür</string>
<string name="revanced_sb_skip_button_filler">Əlaqəsiz olanı ötür</string>
<string name="revanced_sb_skip_button_nomusic">Musiqisiz hissəni ötür</string>
<string name="revanced_sb_skip_button_unsubmitted">Bölümü ötür</string>
<string name="revanced_sb_skipped_sponsor">Sponsorlu hissə ötürüldü</string>
@@ -1104,10 +1107,11 @@ Sizin istifadəçi Id-niz parol kimidir və heç vaxt paylaşmaq olmaz.
<string name="revanced_sb_skipped_intro_middle">Fasilə ötürüldü</string>
<string name="revanced_sb_skipped_intro_end">Fasilə ötürüldü</string>
<string name="revanced_sb_skipped_outro">Bitiş ötürüldü</string>
<string name="revanced_sb_skipped_hook">Giriş ötürüldü</string>
<string name="revanced_sb_skipped_preview_beginning">Önbaxış ötürüldü</string>
<string name="revanced_sb_skipped_preview_middle">Önbaxış ötürüldü</string>
<string name="revanced_sb_skipped_preview_end">Anons ötürüldü</string>
<string name="revanced_sb_skipped_filler">Əlaqəsiz hissə ötürüldü</string>
<string name="revanced_sb_skipped_filler">Əlaqəsiz ötürüldü</string>
<string name="revanced_sb_skipped_nomusic">Musiqi olmayan bölmə ötürüldü</string>
<string name="revanced_sb_skipped_unsubmitted">Göndərilməmiş bölüm ötürüldü</string>
<string name="revanced_sb_skipped_multiple_segments">Çoxlu bölümlər ötürüldü</string>
@@ -1530,32 +1534,25 @@ Bunu aktivləşdirmə daha yüksək video keyfiyyətləri əngəlin silə bilər
<string name="revanced_spoof_video_streams_screen_title">Video yayımları saxtalaşdır</string>
<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_on">"Video yayımları saxtalaşdırılıb
Əgər YouTube Premium istifadəçisisinizsə, bu tənzimlənmə tələb olunmaya 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_type_title">İlkin qəbuledici</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">IOS-da AVC (H.264)-ni məcbur et</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 kodlayıcı avtomatik müəyyən edilir</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 qırılmasın düzəldə bilər.
AVC maksimum 1080p görüntü imkanına malikdir, Opus audio kodlama olmur və video oynatma VP9 və ya AV1-dən daha çox internet məlumatı sərf edəcək."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">iOS saxtalaşdırma yan təsirləri</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"• Filmlər və ya ödənişli videolar oynadıla bilməz
• Stabil səs səviyyəsi mövcud deyil
• Videolar 1 saniyə tez bitir"</string>
<string name="revanced_spoof_video_streams_about_title">Saxtakarlıq yan təsirləri</string>
<string name="revanced_spoof_video_streams_about_android_title">Android saxtalaşdırma yan təsirləri</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• Səs treki menyusu yoxdur
• Sabit səs səviyyəsi yoxdur
• İlkin səsi məcbur etmə mümkün deyil"</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• Səs treki menyusu əlçatmazdır
• Sabit səs səviyyəsi yoxdur"</string>
<string name="revanced_spoof_video_streams_about_experimental">• Təcrübi qəbuledici və hər vaxt işləməyi dayandıra bilər</string>
<string name="revanced_spoof_video_streams_about_no_av1">• AV1 video kodlayıcı yoxdur</string>
<string name="revanced_spoof_video_streams_about_kids_videos">• Giriş edilməyəndə və ya gizli rejimdə uşaq videoları oynadıla bilməz</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">İstək üçün Statistikada göstər</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">Qəbuledici növü İstək üçün statistikada göstərilir</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Qəbuledici nerd üçün Statistikada gizlidir</string>
<string name="revanced_spoof_video_streams_language_title">VR-da ilkin səs yayımı dili</string>
<string name="revanced_spoof_video_streams_language_title">Səs yayım dili</string>
</patch>
</app>
<app id="twitch">

View File

@@ -700,9 +700,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_flyout_audio_track_summary_on">Меню гукавой дарожкі схавана</string>
<string name="revanced_hide_player_flyout_audio_track_summary_off">Адлюструецца меню гукавой дарожкі</string>
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'. -->
<string name="revanced_hide_player_flyout_audio_track_not_available">"Меню аўдыядарожкі схавана
<string name="revanced_hide_player_flyout_audio_track_not_available">"Меню гукавой дарожкі схавана
Каб паказаць меню аўдыядарожкі, змяніце \"Падробка відэаструменяў\" на iOS TV"</string>
Меню гукавой дарожкі недаступна, калі ўключана \"Падробка відэаструменяў\""</string>
<!-- '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>
@@ -1076,10 +1076,12 @@ Second \"item\" text"</string>
<string name="revanced_sb_segments_intro_sum">Інтэрвал без фактычнага зместу. Гэта можа быць паўза, статычны кадр або паўтаральная анімацыя. Не ўключае пераходы, якія змяшчаюць інфармацыю</string>
<string name="revanced_sb_segments_outro">Канцоўкі / Цітры</string>
<string name="revanced_sb_segments_outro_sum">Крэдыты або калі з\"яўляюцца канцавыя карткі YouTube. Не для высноў з інфармацыяй</string>
<string name="revanced_sb_segments_preview">Папярэдні прагляд/Паўзвядзенне/Хук</string>
<string name="revanced_sb_segments_hook">Уступ / Прывітанні</string>
<string name="revanced_sb_segments_hook_sum">Агучаныя трэйлеры для будучага відэа, прывітанні і развітанні. Не ўключае раздзелы, якія дадаюць дадатковы змест</string>
<string name="revanced_sb_segments_preview">Прадпрагляд / Паўтарэнне</string>
<string name="revanced_sb_segments_preview_sum">Калекцыя кліпаў, якія паказваюць, што адбываецца або што адбылося ў відэа ці ў іншых відэа серыі, дзе ўся інфармацыя паўтараецца ў іншым месцы</string>
<string name="revanced_sb_segments_filler">Філер Тангенс / Жарты</string>
<string name="revanced_sb_segments_filler_sum">Датычныя сцэны дададзены толькі для напаўнення або гумару, якія не патрэбныя для разумення асноўнага зместу відэа. Не ўключае сегменты, якія прадстаўляюць кантэкст або даведку</string>
<string name="revanced_sb_segments_filler">Лірычнае адступленне / Жарты</string>
<string name="revanced_sb_segments_filler_sum">Тангенцыяльныя сцэны або жарты, якія не патрабуюцца для разумення асноўнага зместу відэа. Не ўключае раздзелы, якія прадастаўляюць кантэкст або фонавыя дэталі</string>
<string name="revanced_sb_segments_nomusic">Музыка: немузычны раздзел</string>
<string name="revanced_sb_segments_nomusic_sum">Толькі для выкарыстання ў музычных відэа. Раздзелы музычных відэа без музыкі, якія яшчэ не ахоплены іншай катэгорыяй</string>
<string name="revanced_sb_skip_button_compact">Прапусціць</string>
@@ -1092,10 +1094,11 @@ Second \"item\" text"</string>
<string name="revanced_sb_skip_button_intro_middle">Прапусціць антракт</string>
<string name="revanced_sb_skip_button_intro_end">Прапусціць антракт</string>
<string name="revanced_sb_skip_button_outro">Прапусціць іншы</string>
<string name="revanced_sb_skip_button_hook">Прапусціць уступ</string>
<string name="revanced_sb_skip_button_preview_beginning">Прапусціць папярэдні прагляд</string>
<string name="revanced_sb_skip_button_preview_middle">Прапусціць папярэдні прагляд</string>
<string name="revanced_sb_skip_button_preview_end">Прапусціць рэзюмэ</string>
<string name="revanced_sb_skip_button_filler">Скіп напаўняльнік</string>
<string name="revanced_sb_skip_button_filler">Прапусціць тангенс</string>
<string name="revanced_sb_skip_button_nomusic">Прапусціць не музыку</string>
<string name="revanced_sb_skip_button_unsubmitted">Прапусціць сегмент</string>
<string name="revanced_sb_skipped_sponsor">Прапусціў спонсара</string>
@@ -1106,10 +1109,11 @@ Second \"item\" text"</string>
<string name="revanced_sb_skipped_intro_middle">Прапушчаны антракт</string>
<string name="revanced_sb_skipped_intro_end">Прапушчаны антракт</string>
<string name="revanced_sb_skipped_outro">Прапушчаны выхад</string>
<string name="revanced_sb_skipped_hook">Уступ прапушчаны</string>
<string name="revanced_sb_skipped_preview_beginning">Прапушчаны папярэдні прагляд</string>
<string name="revanced_sb_skipped_preview_middle">Прапушчаны папярэдні прагляд</string>
<string name="revanced_sb_skipped_preview_end">Прапушчаны рэзюмэ</string>
<string name="revanced_sb_skipped_filler">Прапушчаны напаўняльнік</string>
<string name="revanced_sb_skipped_filler">Тангенс прапушчаны</string>
<string name="revanced_sb_skipped_nomusic">Прапусціў немузычны раздзел</string>
<string name="revanced_sb_skipped_unsubmitted">Прапушчаны неадпраўлены сегмент</string>
<string name="revanced_sb_skipped_multiple_segments">Прапушчаны некалькі сегментаў</string>
@@ -1532,32 +1536,25 @@ Second \"item\" text"</string>
<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_on">"Відэаструмені падроблены
Калі вы карыстаецеся YouTube Premium, гэтая налада можа не спатрэбіцца"</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_type_title">Клиент по умолчанию</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">Вымусіць iOS AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Відэакaдэк зафіксаваны ў AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Відэакaдэк вызначаецца аўтаматычна</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Уключэнне гэтага можа палепшыць час аўтаномнай працы і выправіць заіканне прайгравання.
AVC мае максімальнае дазвол 1080p, аўдыёкадэк Opus недаступны, а прайграванне відэа будзе выкарыстоўваць больш інтэрнэт-даных, чым VP9 або AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">Пабочныя эфекты падробкі iOS</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"• Фільмы або платныя відэа могуць не прайгравацца
• Стабільная гучнасць недаступная
• Відэа заканчваюцца на 1 секунду раней"</string>
<string name="revanced_spoof_video_streams_about_title">Пабочныя эфекты падробкі</string>
<string name="revanced_spoof_video_streams_about_android_title">Побічныя эфекты падробкі Android</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• Меню гукавой дарожкі адсутнічае
• Стабільны гук недаступны
• Прымусовае арыгінальнае аўдыё недаступна"</string>
• Стабільная гучнасць недаступна"</string>
<string name="revanced_spoof_video_streams_about_experimental">• Эксперыментальны кліент і можа спыніць працу ў любы час</string>
<string name="revanced_spoof_video_streams_about_no_av1">• Няма відэакідавання AV1</string>
<string name="revanced_spoof_video_streams_about_kids_videos">• Дзіцячыя відэа могуць не прайгравацца ў стане выхаду з акаўнта або ў рэжыме інкогніта</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">Паказаць у статыстыцы для спецыялістаў</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">Тып кліента адлюстроўваецца ў статыстыцы для спецыялістаў</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Кліент схаваны ў статыстыцы для спецыялістаў</string>
<string name="revanced_spoof_video_streams_language_title">Мова гукавой дарожкі па змаўчанні для VR</string>
<string name="revanced_spoof_video_streams_language_title">Мова аўдыяпатоку</string>
</patch>
</app>
<app id="twitch">

View File

@@ -700,9 +700,9 @@ Second \"item\" text"</string>
<string name="revanced_hide_player_flyout_audio_track_summary_on">Менюто за избор на Аудио е скрито</string>
<string name="revanced_hide_player_flyout_audio_track_summary_off">Менюто за избор на Аудио се показва</string>
<!-- 'Spoof video streams' should be the same translation used for 'revanced_spoof_video_streams_screen_title'. -->
<string name="revanced_hide_player_flyout_audio_track_not_available">"Менюто за аудио тракове е скрито
<string name="revanced_hide_player_flyout_audio_track_not_available">"Менюто за аудиозаписи е скрито
За да покажете менюто за аудио тракове, променете \"Подмяна на видео потоци\" на iOS TV"</string>
Менюто за аудиозаписи не е налично, когато „Подменяне на видео потоцие активирано"</string>
<!-- '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>
@@ -1075,10 +1075,12 @@ Second \"item\" text"</string>
<string name="revanced_sb_segments_intro_sum">Интервал без реално съдържание. Може да бъде пауза, статичен кадър, повтаряща се анимация. Това не трябва да се използва за преходи, съдържащи информация</string>
<string name="revanced_sb_segments_outro">Край на клипа / Надписи</string>
<string name="revanced_sb_segments_outro_sum">Информация или когато се показват крайните карти на YouTube. Не за заключения с информация</string>
<string name="revanced_sb_segments_preview">Кратък преглед/Обобщение</string>
<string name="revanced_sb_segments_hook">Начало / Поздрави</string>
<string name="revanced_sb_segments_hook_sum">Озвучени трейлъри за предстоящото видео, поздрави и сбогувания. Не включва секции, които добавят допълнително съдържание</string>
<string name="revanced_sb_segments_preview">Предварителен преглед / Резюме</string>
<string name="revanced_sb_segments_preview_sum">Колекция от клипове, които показват какво предстои в този видеоклип или в други видеоклипове от поредицата, където цялата информация се повтаря по-късно във видеоклипа</string>
<string name="revanced_sb_segments_filler">Пълнеж/Шеги</string>
<string name="revanced_sb_segments_filler_sum">Сцени извън темата, добавени само за пълнеж или хумор, които не са необходими за разбирането на основното съдържание на видеоклипа. Това не трябва да включва сегменти, предоставящи контекст или справочни данни</string>
<string name="revanced_sb_segments_filler">Отклонение / Шеги</string>
<string name="revanced_sb_segments_filler_sum">Странични сцени или шеги, които не са необходими за разбиране на основното съдържание на видеоклипа. Не включва секции, предоставящи контекст или фонови детайли</string>
<string name="revanced_sb_segments_nomusic">Музика: Част без музика</string>
<string name="revanced_sb_segments_nomusic_sum">За използване само в музикални видеоклипове. Това трябва да се използва само за части от музикални видеоклипове, които вече не са обхванати от друга категория</string>
<string name="revanced_sb_skip_button_compact">Пропусни</string>
@@ -1091,10 +1093,11 @@ Second \"item\" text"</string>
<string name="revanced_sb_skip_button_intro_middle">Пропусни пауза</string>
<string name="revanced_sb_skip_button_intro_end">Пропусни пауза</string>
<string name="revanced_sb_skip_button_outro">Пропусни заключение</string>
<string name="revanced_sb_skip_button_hook">Пропускане на увод</string>
<string name="revanced_sb_skip_button_preview_beginning">Пропусни преглед</string>
<string name="revanced_sb_skip_button_preview_middle">Пропусни преглед</string>
<string name="revanced_sb_skip_button_preview_end">Пропуснете обобщението</string>
<string name="revanced_sb_skip_button_filler">Пропусни пълнеж</string>
<string name="revanced_sb_skip_button_filler">Пропускане на отклонение</string>
<string name="revanced_sb_skip_button_nomusic">Пропусни част без музика</string>
<string name="revanced_sb_skip_button_unsubmitted">Пропускане на сегмент</string>
<string name="revanced_sb_skipped_sponsor">Пропуснат спонсор</string>
@@ -1105,10 +1108,11 @@ Second \"item\" text"</string>
<string name="revanced_sb_skipped_intro_middle">Пропусни паузи</string>
<string name="revanced_sb_skipped_intro_end">Пропусни паузи</string>
<string name="revanced_sb_skipped_outro">Пропуснато заключение</string>
<string name="revanced_sb_skipped_hook">Пропуснато увод</string>
<string name="revanced_sb_skipped_preview_beginning">Пропуснат преглед</string>
<string name="revanced_sb_skipped_preview_middle">Пропуснат преглед</string>
<string name="revanced_sb_skipped_preview_end">Пропуснато повторение</string>
<string name="revanced_sb_skipped_filler">Пропуснат пълнеж</string>
<string name="revanced_sb_skipped_filler">Пропуснато отклонение</string>
<string name="revanced_sb_skipped_nomusic">Пропусната част без музика</string>
<string name="revanced_sb_skipped_unsubmitted">Пропуснат неизпратен сегмент</string>
<string name="revanced_sb_skipped_multiple_segments">Пропуснати множество части</string>
@@ -1531,32 +1535,25 @@ Second \"item\" text"</string>
<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_on">"Видео потоците са подменени
Ако сте потребител на YouTube Premium, тази настройка може да не е необходима"</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_type_title">Клиент по подразбиране</string>
<string name="revanced_spoof_video_streams_ios_force_avc_title">Принудително използване на AVC (H.264) на iOS</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_on">Видео кодекът е принудително зададен на AVC (H.264)</string>
<string name="revanced_spoof_video_streams_ios_force_avc_summary_off">Видео кодекът се определя автоматично</string>
<string name="revanced_spoof_video_streams_ios_force_avc_user_dialog_message">"Включването на това може да подобри живота на батерията и да поправи заекването при възпроизвеждане.
AVC има максимална резолюция от 1080p, Opus аудио кодек не е наличен и възпроизвеждането на видео ще използва повече интернет данни от VP9 или AV1."</string>
<string name="revanced_spoof_video_streams_about_ios_tv_title">Ефекти на измамата в iOS</string>
<string name="revanced_spoof_video_streams_about_ios_tv_summary">"• Филми или платени видеоклипове може да не се възпроизвеждат
• Стабилен звук не е наличен
• Видеоклиповете завършват 1 секунда по-рано"</string>
<string name="revanced_spoof_video_streams_about_title">Странични ефекти от подменянето</string>
<string name="revanced_spoof_video_streams_about_android_title">Strani4ni efekti na fal6ivoto predstavqne na Android</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• Lipsva menju za audio pisti
Ne e nali4na stabilna glasnost
• Ne e nali4na forsirana originalna audio pista"</string>
<string name="revanced_spoof_video_streams_about_android_summary">"• Липсва менюто за аудиозаписи
Стабилен звук не е наличен"</string>
<string name="revanced_spoof_video_streams_about_experimental">• Експериментален клиент и може да спре да работи по всяко време</string>
<string name="revanced_spoof_video_streams_about_no_av1">• Без AV1 видео кодек</string>
<string name="revanced_spoof_video_streams_about_kids_videos">• Детските видеоклипове може да не се възпроизвеждат, когато сте излезли от профила си или в режим \"инкогнито\"</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_title">Poka6i v Statistiki za nerds</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_on">Tipът na klienta se poka6va v Statistiki za nerds</string>
<string name="revanced_spoof_streaming_data_stats_for_nerds_summary_off">Klientът e skriт v Statistiki za nerds</string>
<string name="revanced_spoof_video_streams_language_title">Ezik po подразбиране za audio potok v VR</string>
<string name="revanced_spoof_video_streams_language_title">Език на аудио потока</string>
</patch>
</app>
<app id="twitch">

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