mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 09:53:55 +01:00
Compare commits
67 Commits
v5.5.0-dev
...
v5.7.1-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39542ddf55 | ||
|
|
e1741130af | ||
|
|
e54eb3ce87 | ||
|
|
0ae756b0fc | ||
|
|
77a0ac5c9c | ||
|
|
899121b9de | ||
|
|
838edb48e7 | ||
|
|
b2665c916a | ||
|
|
4b81f7009b | ||
|
|
1a4c39a2ee | ||
|
|
99334d1e53 | ||
|
|
2850a6ed4e | ||
|
|
f28eb5105b | ||
|
|
69bed4d9fa | ||
|
|
a5f1efac27 | ||
|
|
b51be82cff | ||
|
|
b8635d0b88 | ||
|
|
78699c8bbf | ||
|
|
aeedec7fed | ||
|
|
32b614696b | ||
|
|
a0b63dfa23 | ||
|
|
f0f53cf72f | ||
|
|
cdb68209d1 | ||
|
|
7369f7b8d5 | ||
|
|
db521b940b | ||
|
|
25d7cc68ae | ||
|
|
9495064e6e | ||
|
|
64864c2cdb | ||
|
|
ad0ffb3328 | ||
|
|
06800324aa | ||
|
|
ec746cb05a | ||
|
|
67c5530ea6 | ||
|
|
cd08717783 | ||
|
|
7bac023ea5 | ||
|
|
1d0ec98bec | ||
|
|
3c603fac2d | ||
|
|
20a7ad4715 | ||
|
|
25a60e305e | ||
|
|
c7f42d9a3c | ||
|
|
670f100a29 | ||
|
|
19140e5918 | ||
|
|
1dde485013 | ||
|
|
5efcdd31c8 | ||
|
|
e6529837cb | ||
|
|
fe07033444 | ||
|
|
246333f3dc | ||
|
|
d82b02e4f5 | ||
|
|
44995a9f15 | ||
|
|
c87c788a26 | ||
|
|
4ef30618d1 | ||
|
|
b23e6c39fc | ||
|
|
de26766543 | ||
|
|
9168b5eaaf | ||
|
|
c43b9b3b03 | ||
|
|
5e8dfed3e8 | ||
|
|
d67dbba76f | ||
|
|
5dc93156e0 | ||
|
|
5275413ab7 | ||
|
|
248c05b670 | ||
|
|
9e6669d962 | ||
|
|
9c81d01cc8 | ||
|
|
59654788fc | ||
|
|
4c44982cde | ||
|
|
a7aab9aeca | ||
|
|
7a8486f562 | ||
|
|
ccb6a7f161 | ||
|
|
c792edfb77 |
20
.github/workflows/pull_strings.yml
vendored
20
.github/workflows/pull_strings.yml
vendored
@@ -1,6 +1,8 @@
|
||||
name: Pull strings
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 */8 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -21,16 +23,20 @@ jobs:
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
config: crowdin.yml
|
||||
upload_sources: false
|
||||
download_translations: true
|
||||
localization_branch_name: feat/translations
|
||||
create_pull_request: true
|
||||
pull_request_title: "chore: Sync translations"
|
||||
pull_request_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
|
||||
pull_request_base_branch_name: "dev"
|
||||
commit_message: "chore: Sync translations"
|
||||
github_user_name: revanced-bot
|
||||
github_user_email: github@revanced.app
|
||||
create_pull_request: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||
|
||||
- name: Open pull request
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
uses: repo-sync/pull-request@v2
|
||||
with:
|
||||
source_branch: feat/translations
|
||||
destination_branch: dev
|
||||
pr_title: "chore: Sync translations"
|
||||
pr_body: "Sync translations from [crowdin.com/project/revanced](https://crowdin.com/project/revanced)"
|
||||
|
||||
219
CHANGELOG.md
219
CHANGELOG.md
@@ -1,3 +1,222 @@
|
||||
## [5.7.1-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.4...v5.7.1-dev.5) (2024-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Use 2 letter device language code ([33ff997](https://github.com/ReVanced/revanced-patches/commit/33ff9972000581aca92262f984efb114eeeb9537))
|
||||
|
||||
## [5.7.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.3...v5.7.1-dev.4) (2024-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Do not reset playback speed to 1.0x after closing comment thread (Fixes stock YouTube bug) ([#4195](https://github.com/ReVanced/revanced-patches/issues/4195)) ([dda788c](https://github.com/ReVanced/revanced-patches/commit/dda788c58c789d4f91646ea8e8a8077f590ab6b3))
|
||||
|
||||
## [5.7.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.2...v5.7.1-dev.3) (2024-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - SponsorBlock:** Show a toast and not a dialog if segment submitted successfully ([134b189](https://github.com/ReVanced/revanced-patches/commit/134b189791113dcf1a1cb7c87b8a0954f432730c))
|
||||
|
||||
## [5.7.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.7.1-dev.1...v5.7.1-dev.2) (2024-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Theme:** Use dark theme color for status and navigation bar ([0240efe](https://github.com/ReVanced/revanced-patches/commit/0240efe33e5444625ca2b760c861c9046d3dc836))
|
||||
|
||||
## [5.7.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.7.0...v5.7.1-dev.1) (2024-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Use Android VR authentication if using default audio language ([#4191](https://github.com/ReVanced/revanced-patches/issues/4191)) ([98773cc](https://github.com/ReVanced/revanced-patches/commit/98773cc7d46e5c9c7715b82c8006f1ccbcc5443c))
|
||||
|
||||
# [5.7.0](https://github.com/ReVanced/revanced-patches/compare/v5.6.0...v5.7.0) (2024-12-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Force original audio:** Use correct availability for settings UI ([a7eedcb](https://github.com/ReVanced/revanced-patches/commit/a7eedcb4cca6b7b12629c478c24c0899c80e3615))
|
||||
* **YouTube - Spoof video stream:** Remove UI client type setting. Allow setting default audio language. ([#4184](https://github.com/ReVanced/revanced-patches/issues/4184)) ([99f3f29](https://github.com/ReVanced/revanced-patches/commit/99f3f29c649bf7693c05bbce2bb49bd53e05f050))
|
||||
* **YouTube - Spoof video streams:** Remove iOS, add clients Android TV and Android Creator ([#4180](https://github.com/ReVanced/revanced-patches/issues/4180)) ([86abfb2](https://github.com/ReVanced/revanced-patches/commit/86abfb2b0d4675f0a1cb9ab244783075bfe89281))
|
||||
* **YouTube:** Change fingerprints to support a wider range of target versions ([8a09174](https://github.com/ReVanced/revanced-patches/commit/8a09174def205a26ce49cb7815097e235069526a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Support version `19.47.53` ([#4182](https://github.com/ReVanced/revanced-patches/issues/4182)) ([2089e61](https://github.com/ReVanced/revanced-patches/commit/2089e613d36c45352db7d852aaee0087b1c3e1a4))
|
||||
|
||||
# [5.7.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.4...v5.7.0-dev.1) (2024-12-21)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Support version `19.47.53` ([#4182](https://github.com/ReVanced/revanced-patches/issues/4182)) ([2089e61](https://github.com/ReVanced/revanced-patches/commit/2089e613d36c45352db7d852aaee0087b1c3e1a4))
|
||||
|
||||
## [5.6.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.3...v5.6.1-dev.4) (2024-12-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video stream:** Remove UI client type setting. Allow setting default audio language. ([#4184](https://github.com/ReVanced/revanced-patches/issues/4184)) ([99f3f29](https://github.com/ReVanced/revanced-patches/commit/99f3f29c649bf7693c05bbce2bb49bd53e05f050))
|
||||
|
||||
## [5.6.1-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.2...v5.6.1-dev.3) (2024-12-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Force original audio:** Use correct availability for settings UI ([a7eedcb](https://github.com/ReVanced/revanced-patches/commit/a7eedcb4cca6b7b12629c478c24c0899c80e3615))
|
||||
|
||||
## [5.6.1-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.6.1-dev.1...v5.6.1-dev.2) (2024-12-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Remove iOS, add clients Android TV and Android Creator ([#4180](https://github.com/ReVanced/revanced-patches/issues/4180)) ([86abfb2](https://github.com/ReVanced/revanced-patches/commit/86abfb2b0d4675f0a1cb9ab244783075bfe89281))
|
||||
|
||||
## [5.6.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.6.0...v5.6.1-dev.1) (2024-12-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Change fingerprints to support a wider range of target versions ([8a09174](https://github.com/ReVanced/revanced-patches/commit/8a09174def205a26ce49cb7815097e235069526a))
|
||||
|
||||
# [5.6.0](https://github.com/ReVanced/revanced-patches/compare/v5.5.1...v5.6.0) (2024-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Twitter - Change link sharing domain:** Use correct extension package ([ad7fab6](https://github.com/ReVanced/revanced-patches/commit/ad7fab67319ba23f267d27da9b74266965fc4be3))
|
||||
* **YouTube - Force original audio:** Use correct original audio stream if app language is not English ([0d20171](https://github.com/ReVanced/revanced-patches/commit/0d2017133efac230887b5c2a331d87159df8af11))
|
||||
* **YouTube - Hide layout components:** Hide new kind of community post ([#4155](https://github.com/ReVanced/revanced-patches/issues/4155)) ([08f68cb](https://github.com/ReVanced/revanced-patches/commit/08f68cb5d33f2cfe656d2f93d159c69981f31418))
|
||||
* **YouTube - Miniplayer:** Use estimated maximum on screen size for devices with low density screens ([#4150](https://github.com/ReVanced/revanced-patches/issues/4150)) ([2694158](https://github.com/ReVanced/revanced-patches/commit/2694158c3c9935ede21c96832533222f850068df))
|
||||
* **YouTube - Open Shorts in regular player:** Do not show the miniplayer after opening a Short while a video is playing ([894e366](https://github.com/ReVanced/revanced-patches/commit/894e36665d17d5a3a5728961d424dffc55faa50b))
|
||||
* **YouTube - SponsorBlock:** Show create new segment error messages using a dialog ([#4148](https://github.com/ReVanced/revanced-patches/issues/4148)) ([5870906](https://github.com/ReVanced/revanced-patches/commit/587090636dfff0b358b15026cf7d47c65a4296dc))
|
||||
* **YouTube - Spoof video streams:** Change default spoofing to iOS, allow setting a default language with Android VR ([#4171](https://github.com/ReVanced/revanced-patches/issues/4171)) ([171b4e7](https://github.com/ReVanced/revanced-patches/commit/171b4e7e40066e38fba773b7a6525e9a038779ef))
|
||||
* **YouTube - Spoof video streams:** Update iOS client version ([df3aeed](https://github.com/ReVanced/revanced-patches/commit/df3aeed3b173e408fad80197a89ec5d003a2b328))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Add `Open Shorts in regular player` patch ([#4153](https://github.com/ReVanced/revanced-patches/issues/4153)) ([c7c5e5b](https://github.com/ReVanced/revanced-patches/commit/c7c5e5b2b9cf63d8225bb6bd5e735ddf945b6c29))
|
||||
|
||||
# [5.6.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.5...v5.6.0-dev.6) (2024-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Update iOS client version ([df3aeed](https://github.com/ReVanced/revanced-patches/commit/df3aeed3b173e408fad80197a89ec5d003a2b328))
|
||||
|
||||
# [5.6.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.4...v5.6.0-dev.5) (2024-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Change default spoofing to iOS, allow setting a default language with Android VR ([#4171](https://github.com/ReVanced/revanced-patches/issues/4171)) ([171b4e7](https://github.com/ReVanced/revanced-patches/commit/171b4e7e40066e38fba773b7a6525e9a038779ef))
|
||||
|
||||
# [5.6.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.3...v5.6.0-dev.4) (2024-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Force original audio:** Use correct original audio stream if app language is not English ([0d20171](https://github.com/ReVanced/revanced-patches/commit/0d2017133efac230887b5c2a331d87159df8af11))
|
||||
|
||||
# [5.6.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.2...v5.6.0-dev.3) (2024-12-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Twitter - Change link sharing domain:** Use correct extension package ([ad7fab6](https://github.com/ReVanced/revanced-patches/commit/ad7fab67319ba23f267d27da9b74266965fc4be3))
|
||||
|
||||
# [5.6.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.6.0-dev.1...v5.6.0-dev.2) (2024-12-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Open Shorts in regular player:** Do not show the miniplayer after opening a Short while a video is playing ([894e366](https://github.com/ReVanced/revanced-patches/commit/894e36665d17d5a3a5728961d424dffc55faa50b))
|
||||
|
||||
# [5.6.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.2...v5.6.0-dev.1) (2024-12-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube:** Add `Open Shorts in regular player` patch ([#4153](https://github.com/ReVanced/revanced-patches/issues/4153)) ([c7c5e5b](https://github.com/ReVanced/revanced-patches/commit/c7c5e5b2b9cf63d8225bb6bd5e735ddf945b6c29))
|
||||
|
||||
## [5.5.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.2-dev.1...v5.5.2-dev.2) (2024-12-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Hide layout components:** Hide new kind of community post ([#4155](https://github.com/ReVanced/revanced-patches/issues/4155)) ([08f68cb](https://github.com/ReVanced/revanced-patches/commit/08f68cb5d33f2cfe656d2f93d159c69981f31418))
|
||||
|
||||
## [5.5.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.1...v5.5.2-dev.1) (2024-12-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Miniplayer:** Use estimated maximum on screen size for devices with low density screens ([#4150](https://github.com/ReVanced/revanced-patches/issues/4150)) ([2694158](https://github.com/ReVanced/revanced-patches/commit/2694158c3c9935ede21c96832533222f850068df))
|
||||
* **YouTube - SponsorBlock:** Show create new segment error messages using a dialog ([#4148](https://github.com/ReVanced/revanced-patches/issues/4148)) ([5870906](https://github.com/ReVanced/revanced-patches/commit/587090636dfff0b358b15026cf7d47c65a4296dc))
|
||||
|
||||
## [5.5.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1) (2024-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Fix string translations ([52e04d3](https://github.com/ReVanced/revanced-patches/commit/52e04d340c1a85f3d683c67a15ae96529432d5fe))
|
||||
|
||||
## [5.5.1-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.5.0...v5.5.1-dev.1) (2024-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube:** Fix string translations ([52e04d3](https://github.com/ReVanced/revanced-patches/commit/52e04d340c1a85f3d683c67a15ae96529432d5fe))
|
||||
|
||||
# [5.5.0](https://github.com/ReVanced/revanced-patches/compare/v5.4.0...v5.5.0) (2024-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **Twitch:** Change recommended target to the latest app version ([fb32972](https://github.com/ReVanced/revanced-patches/commit/fb32972f4de92dac1fc5d73f56a392a671c4e94b))
|
||||
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([140f484](https://github.com/ReVanced/revanced-patches/commit/140f484b4b251b0dfa94163a63f61f45f5302052))
|
||||
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
|
||||
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([a2d2141](https://github.com/ReVanced/revanced-patches/commit/a2d2141cec9b0b4929e07a8010889b21c324b229))
|
||||
* **YouTube:** Add `Force original audio` patch ([#4122](https://github.com/ReVanced/revanced-patches/issues/4122)) ([f4aa440](https://github.com/ReVanced/revanced-patches/commit/f4aa4406080b91f01d623e54b11b99ea849ddcdf))
|
||||
|
||||
# [5.5.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.4...v5.5.0-dev.5) (2024-12-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Navigation buttons:** Add options to disable translucent status bar and navigation bar ([#4133](https://github.com/ReVanced/revanced-patches/issues/4133)) ([a2d2141](https://github.com/ReVanced/revanced-patches/commit/a2d2141cec9b0b4929e07a8010889b21c324b229))
|
||||
|
||||
# [5.5.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.3...v5.5.0-dev.4) (2024-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube - Spoof video streams:** Make livestreams start at the current time when using iOS client ([#4137](https://github.com/ReVanced/revanced-patches/issues/4137)) ([140f484](https://github.com/ReVanced/revanced-patches/commit/140f484b4b251b0dfa94163a63f61f45f5302052))
|
||||
|
||||
# [5.5.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.2...v5.5.0-dev.3) (2024-12-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **YouTube - Hide feed components:** Remove obsolete `Hide search result shelf header` option ([#4134](https://github.com/ReVanced/revanced-patches/issues/4134)) ([c71443a](https://github.com/ReVanced/revanced-patches/commit/c71443a08883ab10ef2553213c03b00e7c580a43))
|
||||
|
||||
# [5.5.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.5.0-dev.1...v5.5.0-dev.2) (2024-12-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **YouTube Music:** Add `Spoof client patch` to fix playback ([#4132](https://github.com/ReVanced/revanced-patches/issues/4132)) ([b092508](https://github.com/ReVanced/revanced-patches/commit/b0925088e8b41636e285cb234593d545604ce461))
|
||||
|
||||
# [5.5.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.4.1-dev.1...v5.5.0-dev.1) (2024-12-15)
|
||||
|
||||
|
||||
|
||||
1
extensions/music/build.gradle.kts
Normal file
1
extensions/music/build.gradle.kts
Normal file
@@ -0,0 +1 @@
|
||||
// Do not remove. Necessary for the extension plugin to be applied to the project.
|
||||
1
extensions/music/src/main/AndroidManifest.xml
Normal file
1
extensions/music/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
||||
<manifest/>
|
||||
@@ -0,0 +1,27 @@
|
||||
package app.revanced.extension.music.spoof;
|
||||
|
||||
/**
|
||||
* @noinspection unused
|
||||
*/
|
||||
public class SpoofClientPatch {
|
||||
private static final int CLIENT_TYPE_ID = 26;
|
||||
private static final String CLIENT_VERSION = "6.21";
|
||||
private static final String DEVICE_MODEL = "iPhone16,2";
|
||||
private static final String OS_VERSION = "17.7.2.21H221";
|
||||
|
||||
public static int getClientId() {
|
||||
return CLIENT_TYPE_ID;
|
||||
}
|
||||
|
||||
public static String getClientVersion() {
|
||||
return CLIENT_VERSION;
|
||||
}
|
||||
|
||||
public static String getClientModel() {
|
||||
return DEVICE_MODEL;
|
||||
}
|
||||
|
||||
public static String getOsVersion() {
|
||||
return OS_VERSION;
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,8 @@ package app.revanced.extension.shared.settings;
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.SpoofiOSAvailability;
|
||||
|
||||
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
|
||||
import app.revanced.extension.shared.spoof.ClientType;
|
||||
|
||||
/**
|
||||
* Settings shared across multiple apps.
|
||||
@@ -22,9 +20,5 @@ public class BaseSettings {
|
||||
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
|
||||
|
||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
|
||||
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, new SpoofiOSAvailability());
|
||||
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
|
||||
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new SpoofiOSAvailability());
|
||||
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
|
||||
|
||||
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, parent(SPOOF_VIDEO_STREAMS));
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package app.revanced.extension.shared.spoof;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import app.revanced.extension.shared.Utils;
|
||||
|
||||
public enum AudioStreamLanguage {
|
||||
/**
|
||||
* YouTube default.
|
||||
@@ -65,8 +67,6 @@ public enum AudioStreamLanguage {
|
||||
OR,
|
||||
PA,
|
||||
PL,
|
||||
PT_BR,
|
||||
PT_PT,
|
||||
RO,
|
||||
RU,
|
||||
SI,
|
||||
@@ -88,26 +88,19 @@ public enum AudioStreamLanguage {
|
||||
ZH,
|
||||
ZU;
|
||||
|
||||
private final String iso639_1;
|
||||
private final String language;
|
||||
|
||||
AudioStreamLanguage() {
|
||||
String name = name();
|
||||
final int regionSeparatorIndex = name.indexOf('_');
|
||||
if (regionSeparatorIndex >= 0) {
|
||||
iso639_1 = name.substring(0, regionSeparatorIndex).toLowerCase(Locale.US)
|
||||
+ name.substring(regionSeparatorIndex);
|
||||
} else {
|
||||
iso639_1 = name().toLowerCase(Locale.US);
|
||||
}
|
||||
language = name().toLowerCase(Locale.US);
|
||||
}
|
||||
|
||||
public String getIso639_1() {
|
||||
public String getLanguage() {
|
||||
// Changing the app language does not force the app to completely restart,
|
||||
// so the default needs to be the current language and not a static field.
|
||||
if (this == DEFAULT) {
|
||||
return Locale.getDefault().toLanguageTag();
|
||||
return Locale.getDefault().getLanguage();
|
||||
}
|
||||
|
||||
return iso639_1;
|
||||
return language;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,48 +4,36 @@ import android.os.Build;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
|
||||
public enum ClientType {
|
||||
// Specific purpose for age restricted, or private videos, because the iOS client is not logged in.
|
||||
// https://dumps.tadiphone.dev/dumps/oculus/eureka
|
||||
ANDROID_VR(28,
|
||||
ANDROID_VR_NO_AUTH( // Must be first so a default audio language can be set.
|
||||
28,
|
||||
"ANDROID_VR",
|
||||
"Quest 3",
|
||||
"12",
|
||||
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
|
||||
"32", // Android 12.1
|
||||
"1.56.21",
|
||||
true,
|
||||
false),
|
||||
// Specific for kids videos.
|
||||
IOS(5,
|
||||
"IOS",
|
||||
forceAVC()
|
||||
? "iPhone12,5" // 11 Pro Max (last device with iOS 13)
|
||||
: "iPhone16,2", // 15 Pro Max
|
||||
// iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1.
|
||||
forceAVC()
|
||||
? "13.7.17H35" // Last release of iOS 13.
|
||||
: "17.5.1.21F90",
|
||||
forceAVC()
|
||||
? "com.google.ios.youtube/17.40.5 (iPhone; U; CPU iOS 13_7 like Mac OS X)"
|
||||
: "com.google.ios.youtube/19.47.7 (iPhone; U; CPU iOS 17_5_1 like Mac OS X)",
|
||||
null,
|
||||
// Version number should be a valid iOS release.
|
||||
// https://www.ipa4fun.com/history/185230
|
||||
forceAVC()
|
||||
// Some newer versions can also force AVC,
|
||||
// but 17.40 is the last version that supports iOS 13.
|
||||
? "17.40.5"
|
||||
: "19.47.7",
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
private static boolean forceAVC() {
|
||||
return BaseSettings.SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC.get();
|
||||
}
|
||||
// Fall over to authenticated ('hl' is ignored and audio is same as language set in users Google account).
|
||||
ANDROID_VR(
|
||||
ANDROID_VR_NO_AUTH.id,
|
||||
ANDROID_VR_NO_AUTH.clientName,
|
||||
ANDROID_VR_NO_AUTH.deviceModel,
|
||||
ANDROID_VR_NO_AUTH.osVersion,
|
||||
ANDROID_VR_NO_AUTH.userAgent,
|
||||
ANDROID_VR_NO_AUTH.androidSdkVersion,
|
||||
ANDROID_VR_NO_AUTH.clientVersion,
|
||||
true),
|
||||
ANDROID_UNPLUGGED(
|
||||
29,
|
||||
"ANDROID_UNPLUGGED",
|
||||
"Google TV Streamer",
|
||||
"14",
|
||||
"com.google.android.apps.youtube.unplugged/8.49.0 (Linux; U; Android 14; GB) gzip",
|
||||
"34",
|
||||
"8.49.0",
|
||||
true); // Requires login.
|
||||
|
||||
/**
|
||||
* YouTube
|
||||
@@ -87,11 +75,6 @@ public enum ClientType {
|
||||
*/
|
||||
public final boolean canLogin;
|
||||
|
||||
/**
|
||||
* If a language code should be used.
|
||||
*/
|
||||
public final boolean useLanguageCode;
|
||||
|
||||
ClientType(int id,
|
||||
String clientName,
|
||||
String deviceModel,
|
||||
@@ -99,8 +82,7 @@ public enum ClientType {
|
||||
String userAgent,
|
||||
@Nullable String androidSdkVersion,
|
||||
String clientVersion,
|
||||
boolean canLogin,
|
||||
boolean useLanguageCode) {
|
||||
boolean canLogin) {
|
||||
this.id = id;
|
||||
this.clientName = clientName;
|
||||
this.deviceModel = deviceModel;
|
||||
@@ -109,6 +91,5 @@ public enum ClientType {
|
||||
this.androidSdkVersion = androidSdkVersion;
|
||||
this.clientVersion = clientVersion;
|
||||
this.canLogin = canLogin;
|
||||
this.useLanguageCode = useLanguageCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,31 @@ import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
|
||||
@SuppressWarnings("unused")
|
||||
public class SpoofVideoStreamsPatch {
|
||||
private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_VIDEO_STREAMS.get();
|
||||
|
||||
/**
|
||||
* Any unreachable ip address. Used to intentionally fail requests.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* @return If this patch was included during patching.
|
||||
*/
|
||||
private static boolean isPatchIncluded() {
|
||||
return false; // Modified during patching.
|
||||
}
|
||||
|
||||
public static final class NotSpoofingAndroidAvailability implements Setting.Availability {
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
if (SpoofVideoStreamsPatch.isPatchIncluded()) {
|
||||
return !BaseSettings.SPOOF_VIDEO_STREAMS.get();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
* Blocks /get_watch requests by returning an unreachable URI.
|
||||
@@ -164,12 +183,4 @@ public class SpoofVideoStreamsPatch {
|
||||
|
||||
return postData;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,9 +37,7 @@ final class PlayerRoutes {
|
||||
JSONObject context = new JSONObject();
|
||||
|
||||
JSONObject client = new JSONObject();
|
||||
if (clientType.useLanguageCode) {
|
||||
client.put("hl", BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getIso639_1());
|
||||
}
|
||||
client.put("hl", BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getLanguage());
|
||||
client.put("clientName", clientType.clientName);
|
||||
client.put("clientVersion", clientType.clientVersion);
|
||||
client.put("deviceModel", clientType.deviceModel);
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.concurrent.TimeoutException;
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
|
||||
import app.revanced.extension.shared.spoof.ClientType;
|
||||
|
||||
/**
|
||||
@@ -35,21 +36,26 @@ import app.revanced.extension.shared.spoof.ClientType;
|
||||
*/
|
||||
public class StreamingDataRequest {
|
||||
|
||||
private static final ClientType[] CLIENT_ORDER_TO_USE;
|
||||
private static final ClientType[] CLIENT_ORDER_TO_USE = ClientType.values();
|
||||
|
||||
private static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
private static final String[] REQUEST_HEADER_KEYS = {
|
||||
AUTHORIZATION_HEADER, // Available only to logged-in users.
|
||||
"X-GOOG-API-FORMAT-VERSION",
|
||||
"X-Goog-Visitor-Id"
|
||||
};
|
||||
|
||||
/**
|
||||
* TCP connection and HTTP read timeout.
|
||||
*/
|
||||
private static final int HTTP_TIMEOUT_MILLISECONDS = 10 * 1000;
|
||||
|
||||
/**
|
||||
* Any arbitrarily large value, but must be at least twice {@link #HTTP_TIMEOUT_MILLISECONDS}
|
||||
*/
|
||||
private static final int MAX_MILLISECONDS_TO_WAIT_FOR_FETCH = 20 * 1000;
|
||||
|
||||
private static final Map<String, StreamingDataRequest> cache = Collections.synchronizedMap(
|
||||
new LinkedHashMap<>(100) {
|
||||
/**
|
||||
@@ -67,22 +73,8 @@ public class StreamingDataRequest {
|
||||
}
|
||||
});
|
||||
|
||||
static {
|
||||
ClientType[] allClientTypes = ClientType.values();
|
||||
ClientType preferredClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
|
||||
|
||||
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
|
||||
CLIENT_ORDER_TO_USE[0] = preferredClient;
|
||||
|
||||
int i = 1;
|
||||
for (ClientType c : allClientTypes) {
|
||||
if (c != preferredClient) {
|
||||
CLIENT_ORDER_TO_USE[i++] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final String videoId;
|
||||
|
||||
private final Future<ByteBuffer> future;
|
||||
|
||||
private StreamingDataRequest(String videoId, Map<String, String> playerHeaders) {
|
||||
@@ -172,13 +164,21 @@ public class StreamingDataRequest {
|
||||
// Show an error if the last client type fails, or if the debug is enabled then show for all attempts.
|
||||
final boolean showErrorToast = (++i == CLIENT_ORDER_TO_USE.length) || debugEnabled;
|
||||
|
||||
if (clientType == ClientType.ANDROID_VR_NO_AUTH
|
||||
&& BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get() == AudioStreamLanguage.DEFAULT) {
|
||||
// Only use no auth Android VR if a non default audio language is selected.
|
||||
continue;
|
||||
}
|
||||
|
||||
HttpURLConnection connection = send(clientType, videoId, playerHeaders, showErrorToast);
|
||||
if (connection != null) {
|
||||
try {
|
||||
// gzip encoding doesn't response with content length (-1),
|
||||
// but empty response body does.
|
||||
if (connection.getContentLength() == 0) {
|
||||
Logger.printDebug(() -> "Received empty response for video: " + videoId);
|
||||
if (BaseSettings.DEBUG.get()) {
|
||||
Logger.printException(() -> "Ignoring empty client response: " + clientType);
|
||||
}
|
||||
} else {
|
||||
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream());
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.youtube.shared.PlayerType;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class FixPlaybackSpeedWhilePlayingPatch {
|
||||
|
||||
private static final float DEFAULT_YOUTUBE_PLAYBACK_SPEED = 1.0f;
|
||||
|
||||
public static boolean playbackSpeedChanged(float playbackSpeed) {
|
||||
if (playbackSpeed == DEFAULT_YOUTUBE_PLAYBACK_SPEED &&
|
||||
PlayerType.getCurrent().isMaximizedOrFullscreen()) {
|
||||
|
||||
Logger.printDebug(() -> "Blocking call to change playback speed to 1.0x");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import app.revanced.extension.youtube.settings.Settings;
|
||||
@SuppressWarnings("unused")
|
||||
public class ForceOriginalAudioPatch {
|
||||
|
||||
private static final String DEFAULT_AUDIO_TRACKS_IDENTIFIER = "original";
|
||||
private static final String DEFAULT_AUDIO_TRACKS_SUFFIX = ".4";
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
@@ -17,7 +17,7 @@ public class ForceOriginalAudioPatch {
|
||||
return isDefault;
|
||||
}
|
||||
|
||||
if (audioTrackDisplayName.isEmpty()) {
|
||||
if (audioTrackId.isEmpty()) {
|
||||
// Older app targets can have empty audio tracks and these might be placeholders.
|
||||
// The real audio tracks are called after these.
|
||||
return isDefault;
|
||||
@@ -26,7 +26,7 @@ public class ForceOriginalAudioPatch {
|
||||
Logger.printDebug(() -> "default: " + String.format("%-5s", isDefault) + " id: "
|
||||
+ String.format("%-8s", audioTrackId) + " name:" + audioTrackDisplayName);
|
||||
|
||||
final boolean isOriginal = audioTrackDisplayName.contains(DEFAULT_AUDIO_TRACKS_IDENTIFIER);
|
||||
final boolean isOriginal = audioTrackId.endsWith(DEFAULT_AUDIO_TRACKS_SUFFIX);
|
||||
if (isOriginal) {
|
||||
Logger.printDebug(() -> "Using audio: " + audioTrackId);
|
||||
}
|
||||
@@ -34,8 +34,8 @@ public class ForceOriginalAudioPatch {
|
||||
return isOriginal;
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "isDefaultAudioStream failure", ex);
|
||||
}
|
||||
|
||||
return isDefault;
|
||||
return isDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,13 @@ public final class MiniplayerPatch {
|
||||
final int WIDTH_DIP_MIN = 170; // Seems to be the smallest that works.
|
||||
final int HORIZONTAL_PADDING_DIP = 15; // Estimated padding.
|
||||
// Round down to the nearest 5 pixels, to keep any error toasts easier to read.
|
||||
final int WIDTH_DIP_MAX = 5 * ((deviceDipWidth - HORIZONTAL_PADDING_DIP) / 5);
|
||||
final int estimatedWidthDipMax = 5 * ((deviceDipWidth - HORIZONTAL_PADDING_DIP) / 5);
|
||||
// On some ultra low end devices the pixel width and density are the same number,
|
||||
// which causes the estimate to always give a value of 1.
|
||||
// Fix this by using a fixed size of double the min width.
|
||||
final int WIDTH_DIP_MAX = estimatedWidthDipMax <= WIDTH_DIP_MIN
|
||||
? 2 * WIDTH_DIP_MIN
|
||||
: estimatedWidthDipMax;
|
||||
Logger.printDebug(() -> "Screen dip width: " + deviceDipWidth + " maxWidth: " + WIDTH_DIP_MAX);
|
||||
|
||||
int dipWidth = Settings.MINIPLAYER_WIDTH_DIP.get();
|
||||
@@ -127,8 +133,10 @@ public final class MiniplayerPatch {
|
||||
private static final boolean HIDE_SUBTEXT_ENABLED =
|
||||
(CURRENT_TYPE == MODERN_1 || CURRENT_TYPE == MODERN_3) && Settings.MINIPLAYER_HIDE_SUBTEXT.get();
|
||||
|
||||
private static final boolean HIDE_REWIND_FORWARD_ENABLED =
|
||||
CURRENT_TYPE == MODERN_1 && Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get();
|
||||
// 19.25 is last version that has forward/back buttons for phones,
|
||||
// but buttons still show for tablets/foldable devices and they don't work well so always hide.
|
||||
private static final boolean HIDE_REWIND_FORWARD_ENABLED = CURRENT_TYPE == MODERN_1
|
||||
&& (VersionCheckPatch.IS_19_34_OR_GREATER || Settings.MINIPLAYER_HIDE_REWIND_FORWARD.get());
|
||||
|
||||
private static final boolean MINIPLAYER_ROUNDED_CORNERS_ENABLED =
|
||||
Settings.MINIPLAYER_ROUNDED_CORNERS.get();
|
||||
@@ -145,6 +153,18 @@ public final class MiniplayerPatch {
|
||||
|
||||
private static final int OPACITY_LEVEL;
|
||||
|
||||
static {
|
||||
int opacity = Settings.MINIPLAYER_OPACITY.get();
|
||||
|
||||
if (opacity < 0 || opacity > 100) {
|
||||
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
|
||||
Settings.MINIPLAYER_OPACITY.resetToDefault();
|
||||
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
|
||||
}
|
||||
|
||||
OPACITY_LEVEL = (opacity * 255) / 100;
|
||||
}
|
||||
|
||||
public static final class MiniplayerHorizontalDragAvailability implements Setting.Availability {
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
@@ -163,18 +183,6 @@ public final class MiniplayerPatch {
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
int opacity = Settings.MINIPLAYER_OPACITY.get();
|
||||
|
||||
if (opacity < 0 || opacity > 100) {
|
||||
Utils.showToastLong(str("revanced_miniplayer_opacity_invalid_toast"));
|
||||
Settings.MINIPLAYER_OPACITY.resetToDefault();
|
||||
opacity = Settings.MINIPLAYER_OPACITY.defaultValue;
|
||||
}
|
||||
|
||||
OPACITY_LEVEL = (opacity * 255) / 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*
|
||||
|
||||
@@ -3,12 +3,15 @@ package app.revanced.extension.youtube.patches;
|
||||
import static app.revanced.extension.shared.Utils.hideViewUnderCondition;
|
||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.widget.TextView;
|
||||
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -26,6 +29,15 @@ public final class NavigationButtonsPatch {
|
||||
private static final boolean SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON
|
||||
= Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get();
|
||||
|
||||
private static final Boolean DISABLE_TRANSLUCENT_STATUS_BAR
|
||||
= Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get();
|
||||
|
||||
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT
|
||||
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT.get();
|
||||
|
||||
private static final Boolean DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
|
||||
= Settings.DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK.get();
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
@@ -48,4 +60,42 @@ public final class NavigationButtonsPatch {
|
||||
public static void hideNavigationButtonLabels(TextView navigationLabelsView) {
|
||||
hideViewUnderCondition(Settings.HIDE_NAVIGATION_BUTTON_LABELS, navigationLabelsView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean useTranslucentNavigationStatusBar(boolean original) {
|
||||
// Must check Android version, as forcing this on Android 11 or lower causes app hang and crash.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
return original;
|
||||
}
|
||||
|
||||
if (DISABLE_TRANSLUCENT_STATUS_BAR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean useTranslucentNavigationButtons(boolean original) {
|
||||
// Feature requires Android 13+
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
return original;
|
||||
}
|
||||
|
||||
if (!DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
|
||||
return original;
|
||||
}
|
||||
|
||||
if (DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK && DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Utils.isDarkModeEnabled(Utils.getContext())
|
||||
? !DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK
|
||||
: !DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import static app.revanced.extension.youtube.shared.NavigationBar.NavigationButton;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class OpenShortsInRegularPlayerPatch {
|
||||
|
||||
public enum ShortsPlayerType {
|
||||
SHORTS_PLAYER,
|
||||
REGULAR_PLAYER,
|
||||
REGULAR_PLAYER_FULLSCREEN
|
||||
}
|
||||
|
||||
static {
|
||||
if (!VersionCheckPatch.IS_19_46_OR_GREATER
|
||||
&& Settings.SHORTS_PLAYER_TYPE.get() == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN) {
|
||||
// User imported newer settings to an older app target.
|
||||
Logger.printInfo(() -> "Resetting " + Settings.SHORTS_PLAYER_TYPE);
|
||||
Settings.SHORTS_PLAYER_TYPE.resetToDefault();
|
||||
}
|
||||
}
|
||||
|
||||
private static WeakReference<Activity> mainActivityRef = new WeakReference<>(null);
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static void setMainActivity(Activity activity) {
|
||||
mainActivityRef = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean openShort(String videoID) {
|
||||
try {
|
||||
ShortsPlayerType type = Settings.SHORTS_PLAYER_TYPE.get();
|
||||
if (type == ShortsPlayerType.SHORTS_PLAYER) {
|
||||
return false; // Default unpatched behavior.
|
||||
}
|
||||
|
||||
if (videoID.isEmpty()) {
|
||||
// Shorts was opened using launcher app shortcut.
|
||||
//
|
||||
// This check will not detect if the Shorts app shortcut is used
|
||||
// while the app is running in the background (instead the regular player is opened).
|
||||
// To detect that the hooked method map parameter can be checked
|
||||
// if integer key 'com.google.android.apps.youtube.app.endpoint.flags'
|
||||
// has bitmask 16 set.
|
||||
//
|
||||
// This use case seems unlikely if the user has the Shorts
|
||||
// set to open in the regular player, so it's ignored as
|
||||
// checking the map makes the patch more complicated.
|
||||
Logger.printDebug(() -> "Ignoring Short with no videoId");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NavigationButton.getSelectedNavigationButton() == NavigationButton.SHORTS) {
|
||||
return false; // Always use Shorts player for the Shorts nav button.
|
||||
}
|
||||
|
||||
final boolean forceFullScreen = (type == ShortsPlayerType.REGULAR_PLAYER_FULLSCREEN);
|
||||
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(forceFullScreen);
|
||||
|
||||
// Can use the application context and add intent flags of
|
||||
// FLAG_ACTIVITY_NEW_TASK and FLAG_ACTIVITY_CLEAR_TOP
|
||||
// But the activity context seems to fix random app crashes
|
||||
// if Shorts urls are opened outside the app.
|
||||
var context = mainActivityRef.get();
|
||||
|
||||
Intent videoPlayerIntent = new Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse("https://youtube.com/watch?v=" + videoID)
|
||||
);
|
||||
videoPlayerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
videoPlayerIntent.setPackage(context.getPackageName());
|
||||
|
||||
context.startActivity(videoPlayerIntent);
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
OpenVideosFullscreenHookPatch.setOpenNextVideoFullscreen(null);
|
||||
Logger.printException(() -> "openShort failure", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class OpenVideosFullscreen {
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean openVideoFullscreenPortrait(boolean original) {
|
||||
return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package app.revanced.extension.youtube.patches;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class OpenVideosFullscreenHookPatch {
|
||||
|
||||
@Nullable
|
||||
private static volatile Boolean openNextVideoFullscreen;
|
||||
|
||||
public static void setOpenNextVideoFullscreen(@Nullable Boolean forceFullScreen) {
|
||||
openNextVideoFullscreen = forceFullScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changed during patching since this class is also
|
||||
* used by {@link OpenVideosFullscreenHookPatch}.
|
||||
*/
|
||||
private static boolean isFullScreenPatchIncluded() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Injection point.
|
||||
*/
|
||||
public static boolean openVideoFullscreenPortrait(boolean original) {
|
||||
Boolean openFullscreen = openNextVideoFullscreen;
|
||||
if (openFullscreen != null) {
|
||||
openNextVideoFullscreen = null;
|
||||
return openFullscreen;
|
||||
}
|
||||
|
||||
if (!isFullScreenPatchIncluded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Settings.OPEN_VIDEOS_FULLSCREEN_PORTRAIT.get();
|
||||
}
|
||||
}
|
||||
@@ -9,4 +9,5 @@ public class VersionCheckPatch {
|
||||
public static final boolean IS_19_26_OR_GREATER = Utils.getAppVersionName().compareTo("19.26.00") >= 0;
|
||||
public static final boolean IS_19_29_OR_GREATER = Utils.getAppVersionName().compareTo("19.29.00") >= 0;
|
||||
public static final boolean IS_19_34_OR_GREATER = Utils.getAppVersionName().compareTo("19.34.00") >= 0;
|
||||
public static final boolean IS_19_46_OR_GREATER = Utils.getAppVersionName().compareTo("19.46.00") >= 0;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
);
|
||||
|
||||
private final StringTrieSearch exceptions = new StringTrieSearch();
|
||||
private final StringFilterGroup searchResultShelfHeader;
|
||||
private final StringFilterGroup inFeedSurvey;
|
||||
private final StringFilterGroup notifyMe;
|
||||
private final StringFilterGroup expandableMetadata;
|
||||
@@ -74,13 +73,14 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
|
||||
final var communityPosts = new StringFilterGroup(
|
||||
Settings.HIDE_COMMUNITY_POSTS,
|
||||
"post_base_wrapper",
|
||||
"post_base_wrapper", // may be obsolete and no longer needed.
|
||||
"text_post_root.eml",
|
||||
"images_post_root.eml",
|
||||
"images_post_slim.eml",
|
||||
"images_post_slim.eml", // may be obsolete and no longer needed.
|
||||
"images_post_root_slim.eml",
|
||||
"text_post_root_slim.eml",
|
||||
"post_base_wrapper_slim.eml"
|
||||
"post_base_wrapper_slim.eml",
|
||||
"poll_post_root.eml"
|
||||
);
|
||||
|
||||
final var communityGuidelines = new StringFilterGroup(
|
||||
@@ -194,11 +194,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
"timed_reaction"
|
||||
);
|
||||
|
||||
searchResultShelfHeader = new StringFilterGroup(
|
||||
Settings.HIDE_SEARCH_RESULT_SHELF_HEADER,
|
||||
"shelf_header.eml"
|
||||
);
|
||||
|
||||
notifyMe = new StringFilterGroup(
|
||||
Settings.HIDE_NOTIFY_ME_BUTTON,
|
||||
"set_reminder_button"
|
||||
@@ -324,9 +319,6 @@ public final class LayoutComponentsFilter extends Filter {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: This also hides the feed Shorts shelf header
|
||||
if (matchedGroup == searchResultShelfHeader && contentIndex != 0) return false;
|
||||
|
||||
if (matchedGroup == horizontalShelves) {
|
||||
if (contentIndex == 0 && hideShelves()) {
|
||||
return super.isFiltered(path, identifier, protobufBufferArray, matchedGroup, contentType, contentIndex);
|
||||
|
||||
@@ -7,6 +7,7 @@ import static app.revanced.extension.shared.settings.Setting.migrateFromOldPrefe
|
||||
import static app.revanced.extension.shared.settings.Setting.migrateOldSettingToNew;
|
||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||
import static app.revanced.extension.shared.settings.Setting.parentsAny;
|
||||
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.NotSpoofingAndroidAvailability;
|
||||
import static app.revanced.extension.youtube.patches.ChangeStartPagePatch.StartPage;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHideExpandCloseAvailability;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerHorizontalDragAvailability;
|
||||
@@ -16,7 +17,7 @@ import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerT
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_2;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_3;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.MODERN_4;
|
||||
import static app.revanced.extension.youtube.patches.MiniplayerPatch.MiniplayerType.PHONE;
|
||||
import static app.revanced.extension.youtube.patches.OpenShortsInRegularPlayerPatch.ShortsPlayerType;
|
||||
import static app.revanced.extension.youtube.patches.SeekbarThumbnailsPatch.SeekbarThumbnailsHighQualityAvailability;
|
||||
import static app.revanced.extension.youtube.patches.VersionCheckPatch.IS_19_17_OR_GREATER;
|
||||
import static app.revanced.extension.youtube.sponsorblock.objects.CategoryBehaviour.IGNORE;
|
||||
@@ -53,7 +54,7 @@ public class Settings extends BaseSettings {
|
||||
public static final StringSetting CUSTOM_PLAYBACK_SPEEDS = new StringSetting("revanced_custom_playback_speeds",
|
||||
"0.25\n0.5\n0.75\n0.9\n0.95\n1.0\n1.05\n1.1\n1.25\n1.5\n1.75\n2.0\n3.0\n4.0\n5.0", true);
|
||||
// Audio
|
||||
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE);
|
||||
public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", FALSE, new NotSpoofingAndroidAvailability());
|
||||
|
||||
// Ads
|
||||
public static final BooleanSetting HIDE_BUTTONED_ADS = new BooleanSetting("revanced_hide_buttoned_ads", TRUE);
|
||||
@@ -93,7 +94,6 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_NOTIFY_ME_BUTTON = new BooleanSetting("revanced_hide_notify_me_button", TRUE);
|
||||
public static final BooleanSetting HIDE_PLAYABLES = new BooleanSetting("revanced_hide_playables", TRUE);
|
||||
public static final BooleanSetting HIDE_SEARCH_RESULT_RECOMMENDATIONS = new BooleanSetting("revanced_hide_search_result_recommendations", TRUE);
|
||||
public static final BooleanSetting HIDE_SEARCH_RESULT_SHELF_HEADER = new BooleanSetting("revanced_hide_search_result_shelf_header", FALSE);
|
||||
public static final BooleanSetting HIDE_SHOW_MORE_BUTTON = new BooleanSetting("revanced_hide_show_more_button", TRUE, true);
|
||||
// Alternative thumbnails
|
||||
public static final EnumSetting<ThumbnailOption> ALT_THUMBNAIL_HOME = new EnumSetting<>("revanced_alt_thumbnail_home", ThumbnailOption.ORIGINAL);
|
||||
@@ -151,7 +151,7 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting MINIPLAYER_HORIZONTAL_DRAG = new BooleanSetting("revanced_miniplayer_horizontal_drag", FALSE, true, new MiniplayerHorizontalDragAvailability());
|
||||
public static final BooleanSetting MINIPLAYER_HIDE_EXPAND_CLOSE = new BooleanSetting("revanced_miniplayer_hide_expand_close", FALSE, true, new MiniplayerHideExpandCloseAvailability());
|
||||
public static final BooleanSetting MINIPLAYER_HIDE_SUBTEXT = new BooleanSetting("revanced_miniplayer_hide_subtext", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1, MODERN_3));
|
||||
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", FALSE, true, MINIPLAYER_TYPE.availability(MODERN_1));
|
||||
public static final BooleanSetting MINIPLAYER_HIDE_REWIND_FORWARD = new BooleanSetting("revanced_miniplayer_hide_rewind_forward", TRUE, true, MINIPLAYER_TYPE.availability(MODERN_1));
|
||||
public static final BooleanSetting MINIPLAYER_ROUNDED_CORNERS = new BooleanSetting("revanced_miniplayer_rounded_corners", TRUE, true, MINIPLAYER_ANY_MODERN);
|
||||
public static final IntegerSetting MINIPLAYER_WIDTH_DIP = new IntegerSetting("revanced_miniplayer_width_dip", 192, true, MINIPLAYER_ANY_MODERN);
|
||||
public static final IntegerSetting MINIPLAYER_OPACITY = new IntegerSetting("revanced_miniplayer_opacity", 100, true, MINIPLAYER_TYPE.availability(MODERN_1));
|
||||
@@ -219,10 +219,14 @@ public class Settings extends BaseSettings {
|
||||
public static final BooleanSetting HIDE_SUBSCRIPTIONS_BUTTON = new BooleanSetting("revanced_hide_subscriptions_button", FALSE, true);
|
||||
public static final BooleanSetting HIDE_NAVIGATION_BUTTON_LABELS = new BooleanSetting("revanced_hide_navigation_button_labels", FALSE, true);
|
||||
public static final BooleanSetting SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON = new BooleanSetting("revanced_switch_create_with_notifications_button", TRUE, true);
|
||||
public static final BooleanSetting DISABLE_TRANSLUCENT_STATUS_BAR = new BooleanSetting("revanced_disable_translucent_status_bar", FALSE, true);
|
||||
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_LIGHT = new BooleanSetting("revanced_disable_translucent_navigation_bar_light", FALSE, true);
|
||||
public static final BooleanSetting DISABLE_TRANSLUCENT_NAVIGATION_BAR_DARK = new BooleanSetting("revanced_disable_translucent_navigation_bar_dark", FALSE, true);
|
||||
|
||||
// Shorts
|
||||
public static final BooleanSetting DISABLE_RESUMING_SHORTS_PLAYER = new BooleanSetting("revanced_disable_resuming_shorts_player", FALSE);
|
||||
public static final BooleanSetting DISABLE_SHORTS_BACKGROUND_PLAYBACK = new BooleanSetting("revanced_shorts_disable_background_playback", FALSE);
|
||||
public static final EnumSetting<ShortsPlayerType> SHORTS_PLAYER_TYPE = new EnumSetting<>("revanced_shorts_player_type", ShortsPlayerType.SHORTS_PLAYER);
|
||||
public static final BooleanSetting HIDE_SHORTS_CHANNEL_BAR = new BooleanSetting("revanced_hide_shorts_channel_bar", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_COMMENTS_BUTTON = new BooleanSetting("revanced_hide_shorts_comments_button", FALSE);
|
||||
public static final BooleanSetting HIDE_SHORTS_DISLIKE_BUTTON = new BooleanSetting("revanced_hide_shorts_dislike_button", FALSE);
|
||||
@@ -390,7 +394,8 @@ public class Settings extends BaseSettings {
|
||||
}
|
||||
|
||||
// Migrate renamed enum.
|
||||
if (MINIPLAYER_TYPE.get() == PHONE) {
|
||||
//noinspection deprecation
|
||||
if (MINIPLAYER_TYPE.get() == MiniplayerType.PHONE) {
|
||||
MINIPLAYER_TYPE.save(MINIMAL);
|
||||
}
|
||||
|
||||
|
||||
@@ -507,7 +507,7 @@ public class SponsorBlockPreferenceFragment extends PreferenceFragment {
|
||||
Utils.showToastLong(str("revanced_sb_stats_username_changed"));
|
||||
} else {
|
||||
preference.setText(userName); // revert to previous
|
||||
Utils.showToastLong(errorMessage);
|
||||
SponsorBlockUtils.showErrorDialog(errorMessage);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
package app.revanced.extension.youtube.settings.preference;
|
||||
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import app.revanced.extension.shared.Logger;
|
||||
import app.revanced.extension.shared.Utils;
|
||||
import app.revanced.extension.shared.settings.BaseSettings;
|
||||
import app.revanced.extension.shared.settings.Setting;
|
||||
import app.revanced.extension.shared.spoof.ClientType;
|
||||
|
||||
@SuppressWarnings({"deprecation", "unused"})
|
||||
public class SpoofStreamingDataSideEffectsPreference extends Preference {
|
||||
|
||||
@Nullable
|
||||
private ClientType currentClientType;
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener listener = (sharedPreferences, str) -> {
|
||||
// Because this listener may run before the ReVanced settings fragment updates Settings,
|
||||
// this could show the prior config and not the current.
|
||||
//
|
||||
// Push this call to the end of the main run queue,
|
||||
// so all other listeners are done and Settings is up to date.
|
||||
Utils.runOnMainThread(this::updateUI);
|
||||
};
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SpoofStreamingDataSideEffectsPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
private void addChangeListener() {
|
||||
Setting.preferences.preferences.registerOnSharedPreferenceChangeListener(listener);
|
||||
}
|
||||
|
||||
private void removeChangeListener() {
|
||||
Setting.preferences.preferences.unregisterOnSharedPreferenceChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
|
||||
super.onAttachedToHierarchy(preferenceManager);
|
||||
updateUI();
|
||||
addChangeListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareForRemoval() {
|
||||
super.onPrepareForRemoval();
|
||||
removeChangeListener();
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
ClientType clientType = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
|
||||
if (currentClientType == clientType) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.printDebug(() -> "Updating spoof stream side effects preference");
|
||||
setEnabled(BaseSettings.SPOOF_VIDEO_STREAMS.get());
|
||||
|
||||
String key = "revanced_spoof_video_streams_about_"
|
||||
+ clientType.name().toLowerCase();
|
||||
setTitle(str(key + "_title"));
|
||||
setSummary(str(key + "_summary"));
|
||||
}
|
||||
}
|
||||
@@ -363,6 +363,16 @@ public class SponsorBlockUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void showErrorDialog(String dialogMessage) {
|
||||
Utils.runOnMainThreadNowOrLater(() ->
|
||||
new AlertDialog.Builder(SponsorBlockViewController.getOverLaysViewGroupContext())
|
||||
.setMessage(dialogMessage)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
);
|
||||
}
|
||||
|
||||
public static void onEditByHandClicked() {
|
||||
try {
|
||||
Utils.verifyOnMainThread();
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import app.revanced.extension.youtube.settings.Settings;
|
||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockUtils;
|
||||
import app.revanced.extension.youtube.sponsorblock.objects.SegmentCategory;
|
||||
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment;
|
||||
import app.revanced.extension.youtube.sponsorblock.objects.SponsorSegment.SegmentVote;
|
||||
@@ -142,44 +143,44 @@ public class SBRequester {
|
||||
public static void submitSegments(@NonNull String videoId, @NonNull String category,
|
||||
long startTime, long endTime, long videoLength) {
|
||||
Utils.verifyOffMainThread();
|
||||
|
||||
try {
|
||||
String privateUserId = SponsorBlockSettings.getSBPrivateUserID();
|
||||
String start = String.format(Locale.US, TIME_TEMPLATE, startTime / 1000f);
|
||||
String end = String.format(Locale.US, TIME_TEMPLATE, endTime / 1000f);
|
||||
String duration = String.format(Locale.US, TIME_TEMPLATE, videoLength / 1000f);
|
||||
|
||||
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS, privateUserId, videoId, category, start, end, duration);
|
||||
HttpURLConnection connection = getConnectionFromRoute(SBRoutes.SUBMIT_SEGMENTS,
|
||||
privateUserId, videoId, category, start, end, duration);
|
||||
final int responseCode = connection.getResponseCode();
|
||||
|
||||
final String messageToToast;
|
||||
switch (responseCode) {
|
||||
case HTTP_STATUS_CODE_SUCCESS:
|
||||
messageToToast = str("revanced_sb_submit_succeeded");
|
||||
break;
|
||||
case 409:
|
||||
messageToToast = str("revanced_sb_submit_failed_duplicate");
|
||||
break;
|
||||
case 403:
|
||||
messageToToast = str("revanced_sb_submit_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection));
|
||||
break;
|
||||
case 429:
|
||||
messageToToast = str("revanced_sb_submit_failed_rate_limit");
|
||||
break;
|
||||
case 400:
|
||||
messageToToast = str("revanced_sb_submit_failed_invalid", Requester.parseErrorStringAndDisconnect(connection));
|
||||
break;
|
||||
default:
|
||||
messageToToast = str("revanced_sb_submit_failed_unknown_error", responseCode, connection.getResponseMessage());
|
||||
break;
|
||||
if (responseCode == HTTP_STATUS_CODE_SUCCESS) {
|
||||
Utils.showToastLong(str("revanced_sb_submit_succeeded"));
|
||||
return;
|
||||
}
|
||||
Utils.showToastLong(messageToToast);
|
||||
|
||||
String userErrorMessage = switch (responseCode) {
|
||||
case 409 -> str("revanced_sb_submit_failed_duplicate");
|
||||
case 403 -> str("revanced_sb_submit_failed_forbidden",
|
||||
Requester.parseErrorStringAndDisconnect(connection));
|
||||
case 429 -> str("revanced_sb_submit_failed_rate_limit");
|
||||
case 400 -> str("revanced_sb_submit_failed_invalid",
|
||||
Requester.parseErrorStringAndDisconnect(connection));
|
||||
default -> str("revanced_sb_submit_failed_unknown_error",
|
||||
responseCode, connection.getResponseMessage());
|
||||
};
|
||||
|
||||
// Message might be about the users account or an error too large to show in a toast.
|
||||
// Use a dialog instead.
|
||||
SponsorBlockUtils.showErrorDialog(userErrorMessage);
|
||||
} catch (SocketTimeoutException ex) {
|
||||
// Always show, even if show connection toasts is turned off
|
||||
Logger.printDebug(() -> "Timeout", ex);
|
||||
Utils.showToastLong(str("revanced_sb_submit_failed_timeout"));
|
||||
} catch (IOException ex) {
|
||||
Logger.printDebug(() -> "IOException", ex);
|
||||
Utils.showToastLong(str("revanced_sb_submit_failed_unknown_error", 0, ex.getMessage()));
|
||||
} catch (Exception ex) {
|
||||
Logger.printException(() -> "failed to submit segments", ex);
|
||||
Logger.printException(() -> "failed to submit segments", ex); // Should never happen.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,19 +219,22 @@ public class SBRequester {
|
||||
: getConnectionFromRoute(SBRoutes.VOTE_ON_SEGMENT_QUALITY, uuid, segmentUuid, String.valueOf(voteOption.apiVoteType));
|
||||
final int responseCode = connection.getResponseCode();
|
||||
|
||||
String userMessage;
|
||||
switch (responseCode) {
|
||||
case HTTP_STATUS_CODE_SUCCESS:
|
||||
Logger.printDebug(() -> "Vote success for segment: " + segment);
|
||||
break;
|
||||
return;
|
||||
case 403:
|
||||
Utils.showToastLong(
|
||||
str("revanced_sb_vote_failed_forbidden", Requester.parseErrorStringAndDisconnect(connection)));
|
||||
userMessage = str("revanced_sb_vote_failed_forbidden",
|
||||
Requester.parseErrorStringAndDisconnect(connection));
|
||||
break;
|
||||
default:
|
||||
Utils.showToastLong(
|
||||
str("revanced_sb_vote_failed_unknown_error", responseCode, connection.getResponseMessage()));
|
||||
userMessage = str("revanced_sb_vote_failed_unknown_error",
|
||||
responseCode, connection.getResponseMessage());
|
||||
break;
|
||||
}
|
||||
|
||||
SponsorBlockUtils.showErrorDialog(userMessage);
|
||||
} catch (SocketTimeoutException ex) {
|
||||
Utils.showToastShort(str("revanced_sb_vote_failed_timeout"));
|
||||
} catch (IOException ex) {
|
||||
|
||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
||||
org.gradle.parallel = true
|
||||
android.useAndroidX = true
|
||||
kotlin.code.style = official
|
||||
version = 5.5.0-dev.1
|
||||
version = 5.7.1-dev.5
|
||||
|
||||
@@ -324,8 +324,12 @@ public final class app/revanced/patches/music/misc/gms/GmsCoreSupportPatchKt {
|
||||
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/misc/spoof/SpoofVideoStreamsPatchKt {
|
||||
public static final fun getSpoofVideoStreamsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
public final class app/revanced/patches/music/misc/spoof/SpoofClientPatchKt {
|
||||
public static final fun getSpoofClientPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/music/misc/spoof/UserAgentClientSpoofPatchKt {
|
||||
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/myexpenses/misc/pro/UnlockProPatchKt {
|
||||
@@ -766,6 +770,10 @@ public final class app/revanced/patches/shared/misc/spoof/SpoofVideoStreamsPatch
|
||||
public static synthetic fun spoofVideoStreamsPatch$default (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/shared/misc/spoof/UserAgentClientSpoofPatchKt {
|
||||
public static final fun userAgentClientSpoofPatch (Ljava/lang/String;)Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/solidexplorer2/functionality/filesize/RemoveFileSizeLimitPatchKt {
|
||||
public static final fun getRemoveFileSizeLimitPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1169,6 +1177,10 @@ public final class app/revanced/patches/youtube/layout/player/background/PlayerC
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenKt {
|
||||
public static final fun getOpenVideosFullscreen ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/player/fullscreen/OpenVideosFullscreenPatchKt {
|
||||
public static final fun getOpenVideosFullscreenPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
@@ -1202,6 +1214,10 @@ public final class app/revanced/patches/youtube/layout/shortsautoplay/ShortsAuto
|
||||
public static final fun getShortsAutoplayPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/shortsplayer/OpenShortsInRegularPlayerPatchKt {
|
||||
public static final fun getOpenShortsInRegularPlayerPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/layout/sponsorblock/SponsorBlockPatchKt {
|
||||
public static final fun getSponsorBlockPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
@@ -1280,6 +1296,10 @@ public final class app/revanced/patches/youtube/misc/fix/playback/UserAgentClien
|
||||
public static final fun getUserAgentClientSpoofPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/fix/playbackspeed/FIxPlaybackSpeedWhilePlayingPatchKt {
|
||||
public static final fun getFixPlaybackSpeedWhilePlayingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
public final class app/revanced/patches/youtube/misc/gms/GmsCoreSupportPatchKt {
|
||||
public static final fun getGmsCoreSupportPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@ import app.revanced.patches.music.misc.extension.hooks.applicationInitHook
|
||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||
|
||||
val sharedExtensionPatch = sharedExtensionPatch(
|
||||
"music",
|
||||
applicationInitHook,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import app.revanced.patcher.patch.Option
|
||||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
|
||||
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
||||
import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch
|
||||
import app.revanced.patches.music.misc.spoof.spoofClientPatch
|
||||
import app.revanced.patches.shared.castContextFetchFingerprint
|
||||
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
|
||||
import app.revanced.patches.shared.primeMethodFingerprint
|
||||
@@ -21,7 +21,7 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
|
||||
extensionPatch = sharedExtensionPatch,
|
||||
gmsCoreSupportResourcePatchFactory = ::gmsCoreSupportResourcePatch,
|
||||
) {
|
||||
dependsOn(spoofVideoStreamsPatch)
|
||||
dependsOn(spoofClientPatch)
|
||||
|
||||
compatibleWith(MUSIC_PACKAGE_NAME)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package app.revanced.patches.music.misc.spoof
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val playerRequestConstructorFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
|
||||
strings("player")
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches using the class found in [playerRequestConstructorFingerprint].
|
||||
*/
|
||||
internal val createPlayerRequestBodyFingerprint = fingerprint {
|
||||
parameters("L")
|
||||
returns("V")
|
||||
opcodes(
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.IGET,
|
||||
Opcode.AND_INT_LIT16,
|
||||
)
|
||||
strings("ms")
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get a reference to other clientInfo fields.
|
||||
*/
|
||||
internal val setClientInfoFieldsFingerprint = fingerprint {
|
||||
returns("L")
|
||||
strings("Google Inc.")
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get a reference to the clientInfo and clientInfo.clientVersion field.
|
||||
*/
|
||||
internal val setClientInfoClientVersionFingerprint = fingerprint {
|
||||
strings("10.29")
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package app.revanced.patches.music.misc.spoof
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.getReference
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
|
||||
import com.android.tools.smali.dexlib2.immutable.ImmutableMethodParameter
|
||||
|
||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/music/spoof/SpoofClientPatch;"
|
||||
|
||||
// TODO: Replace this patch with spoofVideoStreamsPatch once possible.
|
||||
val spoofClientPatch = bytecodePatch(
|
||||
name = "Spoof client",
|
||||
description = "Spoofs the client to fix playback.",
|
||||
) {
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
// TODO: Add settingsPatch
|
||||
userAgentClientSpoofPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
val playerRequestClass = playerRequestConstructorFingerprint.classDef
|
||||
|
||||
val createPlayerRequestBodyMatch = createPlayerRequestBodyFingerprint.match(playerRequestClass)
|
||||
|
||||
val clientInfoContainerClass = createPlayerRequestBodyMatch.method
|
||||
.getInstruction(createPlayerRequestBodyMatch.patternMatch!!.startIndex)
|
||||
.getReference<TypeReference>()!!.type
|
||||
|
||||
val clientInfoField = setClientInfoClientVersionFingerprint.method.instructions.first {
|
||||
it.opcode == Opcode.IPUT_OBJECT && it.getReference<FieldReference>()!!.definingClass == clientInfoContainerClass
|
||||
}.getReference<FieldReference>()!!
|
||||
|
||||
val setClientInfoFieldInstructions = setClientInfoFieldsFingerprint.method.instructions.filter {
|
||||
(it.opcode == Opcode.IPUT_OBJECT || it.opcode == Opcode.IPUT) &&
|
||||
it.getReference<FieldReference>()!!.definingClass == clientInfoField.type
|
||||
}.map { it.getReference<FieldReference>()!! }
|
||||
|
||||
// Offsets are known for the fields in the clientInfo object.
|
||||
val clientIdField = setClientInfoFieldInstructions[0]
|
||||
val clientModelField = setClientInfoFieldInstructions[5]
|
||||
val osVersionField = setClientInfoFieldInstructions[7]
|
||||
val clientVersionField = setClientInfoClientVersionFingerprint.method
|
||||
.getInstruction(setClientInfoClientVersionFingerprint.stringMatches!!.first().index + 1)
|
||||
.getReference<FieldReference>()
|
||||
|
||||
// Helper method to spoof the client info.
|
||||
val spoofClientInfoMethod = ImmutableMethod(
|
||||
playerRequestClass.type,
|
||||
"spoofClientInfo",
|
||||
listOf(ImmutableMethodParameter(clientInfoContainerClass, null, null)),
|
||||
"V",
|
||||
AccessFlags.PRIVATE.value or AccessFlags.STATIC.value,
|
||||
null,
|
||||
null,
|
||||
MutableMethodImplementation(3),
|
||||
).toMutable().also(playerRequestClass.methods::add).apply {
|
||||
addInstructions(
|
||||
"""
|
||||
iget-object v0, p0, $clientInfoField
|
||||
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientId()I
|
||||
move-result v1
|
||||
iput v1, v0, $clientIdField
|
||||
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientModel()Ljava/lang/String;
|
||||
move-result-object v1
|
||||
iput-object v1, v0, $clientModelField
|
||||
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getClientVersion()Ljava/lang/String;
|
||||
move-result-object v1
|
||||
iput-object v1, v0, $clientVersionField
|
||||
|
||||
invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->getOsVersion()Ljava/lang/String;
|
||||
move-result-object v1
|
||||
iput-object v1, v0, $osVersionField
|
||||
|
||||
return-void
|
||||
""",
|
||||
)
|
||||
}
|
||||
|
||||
createPlayerRequestBodyMatch.method.apply {
|
||||
val checkCastIndex = createPlayerRequestBodyMatch.patternMatch!!.startIndex
|
||||
val clientInfoContainerRegister = getInstruction<OneRegisterInstruction>(checkCastIndex).registerA
|
||||
|
||||
addInstruction(checkCastIndex + 1, "invoke-static {v$clientInfoContainerRegister}, $spoofClientInfoMethod")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package app.revanced.patches.music.misc.spoof
|
||||
|
||||
import app.revanced.patches.shared.misc.spoof.spoofVideoStreamsPatch
|
||||
|
||||
val spoofVideoStreamsPatch = spoofVideoStreamsPatch({
|
||||
compatibleWith("com.google.android.apps.youtube.music")
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
package app.revanced.patches.music.misc.spoof
|
||||
|
||||
import app.revanced.patches.shared.misc.spoof.userAgentClientSpoofPatch
|
||||
|
||||
val userAgentClientSpoofPatch = userAgentClientSpoofPatch("com.google.android.apps.youtube.music")
|
||||
@@ -2,7 +2,7 @@ package app.revanced.patches.shared.misc.checks
|
||||
|
||||
import android.os.Build.*
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.patch.Patch
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.encodedValue.MutableEncodedValue
|
||||
@@ -82,7 +82,7 @@ fun checkEnvironmentPatch(
|
||||
}
|
||||
}
|
||||
|
||||
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstructions(
|
||||
fun invokeCheck() = mainActivityOnCreateFingerprint.method.addInstruction(
|
||||
0,
|
||||
"invoke-static/range { p0 .. p0 },$EXTENSION_CLASS_DESCRIPTOR->check(Landroid/app/Activity;)V",
|
||||
)
|
||||
|
||||
@@ -110,3 +110,12 @@ internal val buildMediaDataSourceFingerprint = fingerprint {
|
||||
"Ljava/lang/Object;",
|
||||
)
|
||||
}
|
||||
|
||||
internal val patchIncludedExtensionMethodFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returns("Z")
|
||||
parameters()
|
||||
custom { method, classDef ->
|
||||
classDef.type == EXTENSION_CLASS_DESCRIPTOR && method.name == "isPatchIncluded"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMu
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.returnEarly
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
|
||||
@@ -38,6 +39,12 @@ fun spoofVideoStreamsPatch(
|
||||
dependsOn(addResourcesPatch)
|
||||
|
||||
execute {
|
||||
// region Enable extension helper method used by other patches
|
||||
|
||||
patchIncludedExtensionMethodFingerprint.method.returnEarly(true)
|
||||
|
||||
// endregion
|
||||
|
||||
// region Block /initplayback requests to fall back to /get_watch requests.
|
||||
|
||||
val moveUriStringIndex = buildInitPlaybackRequestFingerprint.patternMatch!!.startIndex
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package app.revanced.patches.shared.misc.spoof
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patches.all.misc.transformation.IMethodCall
|
||||
import app.revanced.patches.all.misc.transformation.filterMapInstruction35c
|
||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstruction
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||
|
||||
private const val USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE =
|
||||
"Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;"
|
||||
|
||||
fun userAgentClientSpoofPatch(originalPackageName: String) = transformInstructionsPatch(
|
||||
filterMap = { classDef, _, instruction, instructionIndex ->
|
||||
filterMapInstruction35c<MethodCall>(
|
||||
"Lapp/revanced/extension",
|
||||
classDef,
|
||||
instruction,
|
||||
instructionIndex,
|
||||
)
|
||||
},
|
||||
transform = transform@{ mutableMethod, entry ->
|
||||
val (_, _, instructionIndex) = entry
|
||||
|
||||
// Replace the result of context.getPackageName(), if it is used in a user agent string.
|
||||
mutableMethod.apply {
|
||||
// After context.getPackageName() the result is moved to a register.
|
||||
val targetRegister = (
|
||||
getInstruction(instructionIndex + 1)
|
||||
as? OneRegisterInstruction ?: return@transform
|
||||
).registerA
|
||||
|
||||
// IndexOutOfBoundsException is technically possible here,
|
||||
// but no such occurrences are present in the app.
|
||||
val referee = getInstruction(instructionIndex + 2).getReference<MethodReference>()?.toString()
|
||||
|
||||
// Only replace string builder usage.
|
||||
if (referee != USER_AGENT_STRING_BUILDER_APPEND_METHOD_REFERENCE) {
|
||||
return@transform
|
||||
}
|
||||
|
||||
// Do not change the package name in methods that use resources, or for methods that use GmsCore.
|
||||
// Changing these package names will result in playback limitations,
|
||||
// particularly Android VR background audio only playback.
|
||||
val resourceOrGmsStringInstructionIndex = indexOfFirstInstruction {
|
||||
val reference = getReference<StringReference>()
|
||||
opcode == Opcode.CONST_STRING &&
|
||||
(reference?.string == "android.resource://" || reference?.string == "gcore_")
|
||||
}
|
||||
if (resourceOrGmsStringInstructionIndex >= 0) {
|
||||
return@transform
|
||||
}
|
||||
|
||||
// Overwrite the result of context.getPackageName() with the original package name.
|
||||
replaceInstruction(
|
||||
instructionIndex + 1,
|
||||
"const-string v$targetRegister, \"$originalPackageName\"",
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@Suppress("unused")
|
||||
private enum class MethodCall(
|
||||
override val definedClassName: String,
|
||||
override val methodName: String,
|
||||
override val methodParams: Array<String>,
|
||||
override val returnType: String,
|
||||
) : IMethodCall {
|
||||
GetPackageName(
|
||||
"Landroid/content/Context;",
|
||||
"getPackageName",
|
||||
emptyArray(),
|
||||
"Ljava/lang/String;",
|
||||
),
|
||||
}
|
||||
@@ -11,8 +11,8 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
||||
import app.revanced.patches.twitter.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.indexOfFirstLiteralInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
||||
|
||||
internal var tweetShareLinkTemplateId = -1L
|
||||
private set
|
||||
@@ -25,15 +25,7 @@ internal val changeLinkSharingDomainResourcePatch = resourcePatch {
|
||||
}
|
||||
}
|
||||
|
||||
// This method is used to build the link that is shared when the "Share via..." button is pressed.
|
||||
private const val FORMAT_METHOD_RESOURCE_REFERENCE =
|
||||
"Lapp/revanced/extension/twitter/patches/links/ChangeLinkSharingDomainPatch;->" +
|
||||
"formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;"
|
||||
|
||||
// This method is used to build the link that is shared when the "Copy link" button is pressed.
|
||||
private const val FORMAT_METHOD_REFERENCE =
|
||||
"Lapp/revanced/extension/twitter/patches/links/ChangeLinkSharingDomainPatch;->" +
|
||||
"formatLink(JLjava/lang/String;)Ljava/lang/String;"
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/twitter/patches/links/ChangeLinkSharingDomainPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val changeLinkSharingDomainPatch = bytecodePatch(
|
||||
@@ -71,7 +63,7 @@ val changeLinkSharingDomainPatch = bytecodePatch(
|
||||
addInstructions(
|
||||
0,
|
||||
"""
|
||||
invoke-static { p0, p1, p2 }, $FORMAT_METHOD_REFERENCE
|
||||
invoke-static { p0, p1, p2 }, $EXTENSION_CLASS_DESCRIPTOR->formatLink(JLjava/lang/String;)Ljava/lang/String;
|
||||
move-result-object p0
|
||||
return-object p0
|
||||
""",
|
||||
@@ -84,12 +76,12 @@ val changeLinkSharingDomainPatch = bytecodePatch(
|
||||
|
||||
// Format the link with the new domain name register (1 instruction below the const).
|
||||
val formatLinkCallIndex = templateIdConstIndex + 1
|
||||
val formatLinkCall = getInstruction<Instruction35c>(formatLinkCallIndex)
|
||||
val register = getInstruction<FiveRegisterInstruction>(formatLinkCallIndex).registerE
|
||||
|
||||
// Replace the original method call with the new method call.
|
||||
replaceInstruction(
|
||||
formatLinkCallIndex,
|
||||
"invoke-static { v${formatLinkCall.registerE} }, $FORMAT_METHOD_RESOURCE_REFERENCE",
|
||||
"invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->formatResourceLink([Ljava/lang/Object;)Ljava/lang/String;",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ val hideAdsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ val hideGetPremiumPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ val videoAdsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ val copyVideoUrlPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ val removeViewerDiscretionDialogPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -76,6 +76,7 @@ val downloadsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ val disablePreciseSeekingGesturePatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ val enableSeekbarTappingPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ val enableSlideToSeekPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ val seekbarThumbnailsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ val swipeControlsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ val autoCaptionsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ val customBrandingPatch = resourcePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ val changeHeaderPatch = resourcePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ val hideButtonsPatch = resourcePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package app.revanced.patches.youtube.layout.buttons.navigation
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.literal
|
||||
|
||||
internal const val ANDROID_AUTOMOTIVE_STRING = "Android Automotive"
|
||||
|
||||
@@ -22,4 +23,31 @@ internal val createPivotBarFingerprint = fingerprint {
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.RETURN_VOID,
|
||||
)
|
||||
}
|
||||
|
||||
internal const val TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG = 45400535L
|
||||
|
||||
internal val translucentNavigationStatusBarFeatureFlagFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("Z")
|
||||
literal { TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG }
|
||||
}
|
||||
|
||||
internal const val TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG = 45630927L
|
||||
|
||||
internal val translucentNavigationButtonsFeatureFlagFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
literal { TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG }
|
||||
}
|
||||
|
||||
/**
|
||||
* The device on screen back/home/recent buttons.
|
||||
*/
|
||||
internal const val TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG = 45632194L
|
||||
|
||||
internal val translucentNavigationButtonsSystemFeatureFlagFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("Z")
|
||||
literal { TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG }
|
||||
}
|
||||
@@ -12,10 +12,13 @@ import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.hookNavigationButtonCreated
|
||||
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
@@ -32,6 +35,7 @@ val navigationButtonsPatch = bytecodePatch(
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
navigationBarHookPatch,
|
||||
versionCheckPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
@@ -44,25 +48,34 @@ val navigationButtonsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "layout.buttons.navigation.navigationButtonsPatch")
|
||||
|
||||
val preferences = mutableSetOf(
|
||||
SwitchPreference("revanced_hide_home_button"),
|
||||
SwitchPreference("revanced_hide_shorts_button"),
|
||||
SwitchPreference("revanced_hide_create_button"),
|
||||
SwitchPreference("revanced_hide_subscriptions_button"),
|
||||
SwitchPreference("revanced_switch_create_with_notifications_button"),
|
||||
SwitchPreference("revanced_hide_navigation_button_labels"),
|
||||
)
|
||||
|
||||
if (is_19_25_or_greater) {
|
||||
preferences += SwitchPreference("revanced_disable_translucent_status_bar")
|
||||
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_light")
|
||||
preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_dark")
|
||||
}
|
||||
|
||||
PreferenceScreen.GENERAL_LAYOUT.addPreferences(
|
||||
PreferenceScreenPreference(
|
||||
key = "revanced_navigation_buttons_screen",
|
||||
sorting = Sorting.UNSORTED,
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_hide_home_button"),
|
||||
SwitchPreference("revanced_hide_shorts_button"),
|
||||
SwitchPreference("revanced_hide_create_button"),
|
||||
SwitchPreference("revanced_hide_subscriptions_button"),
|
||||
SwitchPreference("revanced_switch_create_with_notifications_button"),
|
||||
SwitchPreference("revanced_hide_navigation_button_labels"),
|
||||
),
|
||||
),
|
||||
preferences = preferences
|
||||
)
|
||||
)
|
||||
|
||||
// Switch create with notifications button.
|
||||
@@ -101,5 +114,24 @@ val navigationButtonsPatch = bytecodePatch(
|
||||
|
||||
// Hook navigation button created, in order to hide them.
|
||||
hookNavigationButtonCreated(EXTENSION_CLASS_DESCRIPTOR)
|
||||
|
||||
|
||||
// Force on/off translucent effect on status bar and navigation buttons.
|
||||
if (is_19_25_or_greater) {
|
||||
translucentNavigationStatusBarFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
TRANSLUCENT_NAVIGATION_STATUS_BAR_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationStatusBar(Z)Z",
|
||||
)
|
||||
|
||||
translucentNavigationButtonsFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
TRANSLUCENT_NAVIGATION_BUTTONS_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||
)
|
||||
|
||||
translucentNavigationButtonsSystemFeatureFlagFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
TRANSLUCENT_NAVIGATION_BUTTONS_SYSTEM_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->useTranslucentNavigationButtons(Z)Z",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ val hidePlayerOverlayButtonsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@ val hideEndscreenCardsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ val disableFullscreenAmbientModePatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -222,7 +223,6 @@ val hideLayoutComponentsPatch = bytecodePatch(
|
||||
SwitchPreference("revanced_hide_notify_me_button"),
|
||||
SwitchPreference("revanced_hide_playables"),
|
||||
SwitchPreference("revanced_hide_search_result_recommendations"),
|
||||
SwitchPreference("revanced_hide_search_result_shelf_header"),
|
||||
SwitchPreference("revanced_hide_show_more_button"),
|
||||
SwitchPreference("revanced_hide_doodles"),
|
||||
)
|
||||
|
||||
@@ -65,6 +65,7 @@ val hideInfoCardsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ val hidePlayerFlyoutMenuPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ val disableRollingNumberAnimationPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ val hideSeekbarPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -190,6 +190,7 @@ val hideShortsComponentsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ val disableSuggestedVideoEndScreenPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ val hideTimestampPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -170,6 +170,7 @@ val miniplayerPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ val playerPopupPanelsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ val playerControlsBackgroundPatch = resourcePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -14,3 +14,15 @@ internal val openVideosFullscreenPortraitFingerprint = fingerprint {
|
||||
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to enable opening regular videos fullscreen.
|
||||
*/
|
||||
internal val openVideosFullscreenHookPatchExtensionFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returns("Z")
|
||||
parameters()
|
||||
custom { methodDef, classDef ->
|
||||
methodDef.name == "isFullScreenPatchIncluded" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,9 @@
|
||||
package app.revanced.patches.youtube.layout.player.fullscreen
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreen;"
|
||||
|
||||
@Suppress("unused")
|
||||
val openVideosFullscreenPatch = bytecodePatch(
|
||||
name = "Open videos fullscreen",
|
||||
description = "Adds an option to open videos in full screen portrait mode.",
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.46.42",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
|
||||
)
|
||||
|
||||
// Add resources and setting last, in case the user force patches an old incompatible version.
|
||||
|
||||
addResources("youtube", "layout.player.fullscreen.openVideosFullscreen")
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
SwitchPreference("revanced_open_videos_fullscreen_portrait")
|
||||
)
|
||||
}
|
||||
}
|
||||
@Deprecated("Renamed to openVideosFullscreenPatch", ReplaceWith("openVideosFullscreenPatch"))
|
||||
val openVideosFullscreen = bytecodePatch{
|
||||
dependsOn(openVideosFullscreenPatch)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package app.revanced.patches.youtube.layout.player.fullscreen
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.youtube.layout.shortsplayer.openShortsInRegularPlayerPatch
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.util.insertFeatureFlagBooleanOverride
|
||||
|
||||
internal const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/OpenVideosFullscreenHookPatch;"
|
||||
|
||||
/**
|
||||
* Used by both [openVideosFullscreenPatch] and [openShortsInRegularPlayerPatch].
|
||||
*/
|
||||
internal val openVideosFullscreenHookPatch = bytecodePatch {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
versionCheckPatch
|
||||
)
|
||||
|
||||
execute {
|
||||
if (!is_19_46_or_greater) {
|
||||
return@execute
|
||||
}
|
||||
|
||||
openVideosFullscreenPortraitFingerprint.method.insertFeatureFlagBooleanOverride(
|
||||
OPEN_VIDEOS_FULLSCREEN_PORTRAIT_FEATURE_FLAG,
|
||||
"$EXTENSION_CLASS_DESCRIPTOR->openVideoFullscreenPortrait(Z)Z"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package app.revanced.patches.youtube.layout.player.fullscreen
|
||||
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.util.returnEarly
|
||||
|
||||
@Suppress("unused")
|
||||
val openVideosFullscreenPatch = bytecodePatch(
|
||||
name = "Open videos fullscreen",
|
||||
description = "Adds an option to open videos in full screen portrait mode.",
|
||||
) {
|
||||
dependsOn(
|
||||
openVideosFullscreenHookPatch,
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
versionCheckPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
)
|
||||
)
|
||||
|
||||
execute {
|
||||
if (!is_19_46_or_greater) {
|
||||
throw PatchException("'Open videos fullscreen' requires 19.46.42 or greater")
|
||||
}
|
||||
|
||||
addResources("youtube", "layout.player.fullscreen.openVideosFullscreen")
|
||||
|
||||
PreferenceScreen.PLAYER.addPreferences(
|
||||
SwitchPreference("revanced_open_videos_fullscreen_portrait")
|
||||
)
|
||||
|
||||
// Enable the logic for the user Setting to open regular videos fullscreen.
|
||||
openVideosFullscreenHookPatchExtensionFingerprint.method.returnEarly(true)
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@ val customPlayerOverlayOpacityPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ val returnYouTubeDislikePatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ val wideSearchbarPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package app.revanced.patches.youtube.layout.shortsautoplay
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
@@ -39,6 +40,7 @@ val shortsAutoplayPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -56,7 +58,7 @@ val shortsAutoplayPatch = bytecodePatch(
|
||||
}
|
||||
|
||||
// Main activity is used to check if app is in pip mode.
|
||||
mainActivityOnCreateFingerprint.method.addInstructions(
|
||||
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||
1,
|
||||
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||
"setMainActivity(Landroid/app/Activity;)V",
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package app.revanced.patches.youtube.layout.shortsplayer
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.literal
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
|
||||
/**
|
||||
* Purpose of this method is not clear, and it's only used to identify
|
||||
* the obfuscated name of the videoId() method in PlaybackStartDescriptor.
|
||||
*/
|
||||
internal val playbackStartFeatureFlagFingerprint = fingerprint {
|
||||
returns("Z")
|
||||
parameters(
|
||||
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
|
||||
)
|
||||
literal {
|
||||
45380134L
|
||||
}
|
||||
}
|
||||
|
||||
// Pre 19.25
|
||||
internal val shortsPlaybackIntentLegacyFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters(
|
||||
"L",
|
||||
"Ljava/util/Map;",
|
||||
"J",
|
||||
"Ljava/lang/String;",
|
||||
"Z",
|
||||
"Ljava/util/Map;"
|
||||
)
|
||||
strings(
|
||||
// None of these strings are unique.
|
||||
"com.google.android.apps.youtube.app.endpoint.flags",
|
||||
"ReelWatchFragmentArgs",
|
||||
"reels_fragment_descriptor"
|
||||
)
|
||||
}
|
||||
|
||||
internal val shortsPlaybackIntentFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters(
|
||||
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;",
|
||||
"Ljava/util/Map;",
|
||||
"J",
|
||||
"Ljava/lang/String;"
|
||||
)
|
||||
strings(
|
||||
// None of these strings are unique.
|
||||
"com.google.android.apps.youtube.app.endpoint.flags",
|
||||
"ReelWatchFragmentArgs",
|
||||
"reels_fragment_descriptor"
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package app.revanced.patches.youtube.layout.shortsplayer
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||
import app.revanced.patches.youtube.layout.player.fullscreen.openVideosFullscreenHookPatch
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.navigation.navigationBarHookPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_25_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_46_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/OpenShortsInRegularPlayerPatch;"
|
||||
|
||||
@Suppress("unused")
|
||||
val openShortsInRegularPlayerPatch = bytecodePatch(
|
||||
name = "Open Shorts in regular player",
|
||||
description = "Adds options to open Shorts in the regular video player.",
|
||||
) {
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
settingsPatch,
|
||||
addResourcesPatch,
|
||||
openVideosFullscreenHookPatch,
|
||||
navigationBarHookPatch,
|
||||
versionCheckPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
"18.38.44",
|
||||
"18.49.37",
|
||||
"19.16.39",
|
||||
"19.25.37",
|
||||
"19.34.42",
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "layout.shortsplayer.shortsPlayerTypePatch")
|
||||
|
||||
PreferenceScreen.SHORTS.addPreferences(
|
||||
if (is_19_46_or_greater) {
|
||||
ListPreference(
|
||||
key = "revanced_shorts_player_type",
|
||||
summaryKey = null,
|
||||
)
|
||||
} else {
|
||||
ListPreference(
|
||||
key = "revanced_shorts_player_type",
|
||||
summaryKey = null,
|
||||
entriesKey = "revanced_shorts_player_type_legacy_entries",
|
||||
entryValuesKey = "revanced_shorts_player_type_legacy_entry_values"
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
// Activity is used as the context to launch an Intent.
|
||||
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||
1,
|
||||
"invoke-static/range { p0 .. p0 }, ${EXTENSION_CLASS_DESCRIPTOR}->" +
|
||||
"setMainActivity(Landroid/app/Activity;)V",
|
||||
)
|
||||
|
||||
// Find the obfuscated method name for PlaybackStartDescriptor.videoId()
|
||||
val playbackStartVideoIdMethodName = playbackStartFeatureFlagFingerprint.method.let {
|
||||
val stringMethodIndex = it.indexOfFirstInstructionOrThrow {
|
||||
val reference = getReference<MethodReference>()
|
||||
reference?.definingClass == "Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
|
||||
&& reference.returnType == "Ljava/lang/String;"
|
||||
}
|
||||
|
||||
navigate(it).to(stringMethodIndex).stop().name
|
||||
}
|
||||
|
||||
fun extensionInstructions(playbackStartRegister: Int, freeRegister: Int) =
|
||||
"""
|
||||
invoke-virtual { v$playbackStartRegister }, Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;->$playbackStartVideoIdMethodName()Ljava/lang/String;
|
||||
move-result-object v$freeRegister
|
||||
invoke-static { v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->openShort(Ljava/lang/String;)Z
|
||||
move-result v$freeRegister
|
||||
if-eqz v$freeRegister, :disabled
|
||||
return-void
|
||||
|
||||
:disabled
|
||||
nop
|
||||
"""
|
||||
|
||||
if (!is_19_25_or_greater) {
|
||||
shortsPlaybackIntentLegacyFingerprint.method.apply {
|
||||
val index = indexOfFirstInstructionOrThrow {
|
||||
getReference<MethodReference>()?.returnType ==
|
||||
"Lcom/google/android/libraries/youtube/player/model/PlaybackStartDescriptor;"
|
||||
}
|
||||
val freeRegister = getInstruction<FiveRegisterInstruction>(index).registerC
|
||||
val playbackStartRegister = getInstruction<OneRegisterInstruction>(index + 1).registerA
|
||||
|
||||
addInstructionsWithLabels(
|
||||
index + 2,
|
||||
extensionInstructions(playbackStartRegister, freeRegister)
|
||||
)
|
||||
}
|
||||
|
||||
return@execute
|
||||
}
|
||||
|
||||
shortsPlaybackIntentFingerprint.method.addInstructionsWithLabels(
|
||||
0,
|
||||
"""
|
||||
move-object/from16 v0, p1
|
||||
${extensionInstructions(0, 1)}
|
||||
"""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -119,6 +119,7 @@ val sponsorBlockPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ val spoofAppVersionPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ val changeStartPagePatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ val disableResumingShortsOnStartupPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ val enableTabletLayoutPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -163,22 +163,31 @@ val themePatch = bytecodePatch(
|
||||
}
|
||||
|
||||
// Fix the splash screen dark mode background color.
|
||||
// In earlier versions of the app this is white and makes no sense for dark mode.
|
||||
// This is only required for 19.32 and greater, but is applied to all targets.
|
||||
// Only dark mode needs this fix as light mode correctly uses the custom color.
|
||||
// In 19.32+ the dark mode splash screen is white and fades to black.
|
||||
// Maybe it's a bug in YT, or maybe it intentionally. Who knows.
|
||||
document("res/values-night/styles.xml").use { document ->
|
||||
// Create a night mode specific override for the splash screen background.
|
||||
val style = document.createElement("style")
|
||||
style.setAttribute("name", "Theme.YouTube.Home")
|
||||
style.setAttribute("parent", "@style/Base.V23.Theme.YouTube.Home")
|
||||
|
||||
val windowItem = document.createElement("item")
|
||||
windowItem.setAttribute("name", "android:windowBackground")
|
||||
windowItem.textContent = "@color/$splashBackgroundColor"
|
||||
style.appendChild(windowItem)
|
||||
|
||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||
resourcesNode.appendChild(style)
|
||||
val childNodes = resourcesNode.childNodes
|
||||
|
||||
for (i in 0 until childNodes.length) {
|
||||
val node = childNodes.item(i) as? Element ?: continue
|
||||
val nodeAttributeName = node.getAttribute("name")
|
||||
if (nodeAttributeName.startsWith("Theme.YouTube.Launcher")) {
|
||||
val nodeAttributeParent = node.getAttribute("parent")
|
||||
|
||||
val style = document.createElement("style")
|
||||
style.setAttribute("name", "Theme.YouTube.Home")
|
||||
style.setAttribute("parent", nodeAttributeParent)
|
||||
|
||||
val windowItem = document.createElement("item")
|
||||
windowItem.setAttribute("name", "android:windowBackground")
|
||||
windowItem.textContent = "@color/$splashBackgroundColor"
|
||||
style.appendChild(windowItem)
|
||||
|
||||
resourcesNode.removeChild(node)
|
||||
resourcesNode.appendChild(style)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,6 +207,7 @@ val themePatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ val alternativeThumbnailsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ val bypassImageRegionRestrictionsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package app.revanced.patches.youtube.misc.announcements
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
@@ -31,6 +31,7 @@ val announcementsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -41,7 +42,7 @@ val announcementsPatch = bytecodePatch(
|
||||
SwitchPreference("revanced_announcements"),
|
||||
)
|
||||
|
||||
mainActivityOnCreateFingerprint.method.addInstructions(
|
||||
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||
// Insert index must be greater than the insert index used by GmsCoreSupport,
|
||||
// as both patch the same method and GmsCore check should be first.
|
||||
1,
|
||||
|
||||
@@ -32,6 +32,7 @@ val autoRepeatPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ val backgroundPlaybackPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ val enableDebuggingPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ val spoofDeviceDimensionsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package app.revanced.patches.youtube.misc.dns
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.shared.mainActivityOnCreateFingerprint
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
@@ -13,7 +14,10 @@ val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch(
|
||||
name = "Check watch history domain name resolution",
|
||||
description = "Checks if the device DNS server is preventing user watch history from being saved.",
|
||||
) {
|
||||
dependsOn(addResourcesPatch)
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
addResourcesPatch
|
||||
)
|
||||
|
||||
compatibleWith(
|
||||
"com.google.android.youtube"(
|
||||
@@ -25,13 +29,14 @@ val checkWatchHistoryDomainNameResolutionPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
execute {
|
||||
addResources("youtube", "misc.dns.checkWatchHistoryDomainNameResolutionPatch")
|
||||
|
||||
mainActivityOnCreateFingerprint.method.addInstructions(
|
||||
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||
// FIXME: Insert index must be greater than the insert index used by GmsCoreSupport,
|
||||
// as both patch the same method and GmsCoreSupport check should be first,
|
||||
// but the patch does not depend on GmsCoreSupport, so it should not be possible to enforce this
|
||||
|
||||
@@ -15,22 +15,16 @@ internal val onBackPressedFingerprint = fingerprint {
|
||||
}
|
||||
}
|
||||
|
||||
internal val recyclerViewScrollingFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL)
|
||||
internal val scrollPositionFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PROTECTED, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters()
|
||||
parameters("L")
|
||||
opcodes(
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CHECK_CAST,
|
||||
Opcode.INVOKE_VIRTUAL,
|
||||
Opcode.MOVE_RESULT,
|
||||
Opcode.IF_LEZ,
|
||||
Opcode.IGET_OBJECT,
|
||||
Opcode.CONST_4,
|
||||
Opcode.IF_NEZ,
|
||||
Opcode.INVOKE_DIRECT,
|
||||
Opcode.RETURN_VOID
|
||||
)
|
||||
strings("scroll_position")
|
||||
}
|
||||
|
||||
internal val recyclerViewTopScrollingFingerprint = fingerprint {
|
||||
|
||||
@@ -1,54 +1,49 @@
|
||||
package app.revanced.patches.youtube.misc.fix.backtoexitgesture
|
||||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/FixBackToExitGesturePatch;"
|
||||
|
||||
internal val fixBackToExitGesturePatch = bytecodePatch(
|
||||
description = "Fixes the swipe back to exit gesture.",
|
||||
) {
|
||||
|
||||
execute {
|
||||
/**
|
||||
* Inject a call to a method from the extension.
|
||||
*
|
||||
* @param targetMethod The target method to call.
|
||||
*/
|
||||
fun Fingerprint.injectCall(targetMethod: ExtensionMethod) = method.addInstruction(
|
||||
patternMatch!!.endIndex,
|
||||
targetMethod.toString(),
|
||||
)
|
||||
recyclerViewTopScrollingFingerprint.match(recyclerViewTopScrollingParentFingerprint.originalClassDef)
|
||||
.let {
|
||||
it.method.addInstruction(
|
||||
it.patternMatch!!.endIndex,
|
||||
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onTopView()V"
|
||||
)
|
||||
}
|
||||
|
||||
mapOf(
|
||||
recyclerViewTopScrollingFingerprint.also {
|
||||
it.match(recyclerViewTopScrollingParentFingerprint.originalClassDef)
|
||||
} to ExtensionMethod(
|
||||
methodName = "onTopView",
|
||||
),
|
||||
recyclerViewScrollingFingerprint to ExtensionMethod(
|
||||
methodName = "onScrollingViews",
|
||||
),
|
||||
onBackPressedFingerprint to ExtensionMethod(
|
||||
"p0",
|
||||
"onBackPressed",
|
||||
"Landroid/app/Activity;",
|
||||
),
|
||||
).forEach { (fingerprint, target) -> fingerprint.injectCall(target) }
|
||||
scrollPositionFingerprint.let {
|
||||
navigate(it.originalMethod)
|
||||
.to(it.patternMatch!!.startIndex + 1)
|
||||
.stop().apply {
|
||||
val index = indexOfFirstInstructionOrThrow {
|
||||
opcode == Opcode.INVOKE_VIRTUAL && getReference<MethodReference>()?.definingClass ==
|
||||
"Landroid/support/v7/widget/RecyclerView;"
|
||||
}
|
||||
|
||||
addInstruction(
|
||||
index,
|
||||
"invoke-static { }, $EXTENSION_CLASS_DESCRIPTOR->onScrollingViews()V"
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onBackPressedFingerprint.let {
|
||||
it.method.addInstruction(
|
||||
it.patternMatch!!.endIndex,
|
||||
"invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR->onBackPressed(Landroid/app/Activity;)V"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to a method from the extension for [fixBackToExitGesturePatch].
|
||||
*
|
||||
* @param register The method registers.
|
||||
* @param methodName The method name.
|
||||
* @param parameterTypes The parameters of the method.
|
||||
*/
|
||||
private class ExtensionMethod(
|
||||
val register: String = "",
|
||||
val methodName: String,
|
||||
val parameterTypes: String = "",
|
||||
) {
|
||||
override fun toString() =
|
||||
"invoke-static {$register}, Lapp/revanced/extension/youtube/patches/FixBackToExitGesturePatch;->$methodName($parameterTypes)V"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playbackspeed
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.smali.ExternalLabel
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.patches.youtube.misc.playertype.playerTypeHookPatch
|
||||
import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
|
||||
import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
|
||||
import app.revanced.util.indexOfFirstInstructionOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"Lapp/revanced/extension/youtube/patches/FixPlaybackSpeedWhilePlayingPatch;"
|
||||
|
||||
/**
|
||||
* Fixes a bug in YouTube 19.34+ where the playback speed
|
||||
* can incorrectly reset to 1.0x under certain conditions.
|
||||
*
|
||||
* Reproduction steps using 19.34+
|
||||
* 1. Open a video and start playback
|
||||
* 2. Change the speed to any value that is not 1.0x.
|
||||
* 3. Open the comments panel.
|
||||
* 4. Tap any "N more replies" link at the bottom of a comment, or tap on a timestamp of a comment.
|
||||
* 5. Pause the video
|
||||
* 6. Resume the video
|
||||
* 7. Playback speed will incorrectly change to 1.0x.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
val fixPlaybackSpeedWhilePlayingPatch = bytecodePatch{
|
||||
dependsOn(
|
||||
sharedExtensionPatch,
|
||||
playerTypeHookPatch,
|
||||
versionCheckPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
if (!is_19_34_or_greater) {
|
||||
return@execute
|
||||
}
|
||||
|
||||
playbackSpeedInFeedsFingerprint.method.apply {
|
||||
val freeRegister = implementation!!.registerCount - parameters.size - 2
|
||||
val playbackSpeedIndex = indexOfGetPlaybackSpeedInstruction(this)
|
||||
val playbackSpeedRegister = getInstruction<TwoRegisterInstruction>(playbackSpeedIndex).registerA
|
||||
val returnIndex = indexOfFirstInstructionOrThrow(playbackSpeedIndex, Opcode.RETURN_VOID)
|
||||
|
||||
addInstructionsWithLabels(
|
||||
playbackSpeedIndex + 1,
|
||||
"""
|
||||
invoke-static { v$playbackSpeedRegister }, $EXTENSION_CLASS_DESCRIPTOR->playbackSpeedChanged(F)Z
|
||||
move-result v$freeRegister
|
||||
if-nez v$freeRegister, :do_not_change
|
||||
""",
|
||||
ExternalLabel("do_not_change", getInstruction(returnIndex))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package app.revanced.patches.youtube.misc.fix.playbackspeed
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.indexOfFirstInstructionReversed
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.Method
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
|
||||
/**
|
||||
* This method is usually used to set the initial speed (1.0x) when playback starts from the feed.
|
||||
* For some reason, in the latest YouTube, it is invoked even after the video has already started.
|
||||
*/
|
||||
internal val playbackSpeedInFeedsFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
||||
returns("V")
|
||||
parameters("L")
|
||||
opcodes(
|
||||
Opcode.IGET,
|
||||
Opcode.MUL_INT_LIT16,
|
||||
Opcode.IGET_WIDE,
|
||||
Opcode.CONST_WIDE_16,
|
||||
Opcode.CMP_LONG,
|
||||
Opcode.IF_EQZ,
|
||||
Opcode.IF_LEZ,
|
||||
Opcode.SUB_LONG_2ADDR,
|
||||
)
|
||||
custom { method, _ ->
|
||||
indexOfGetPlaybackSpeedInstruction(method) >= 0
|
||||
}
|
||||
}
|
||||
|
||||
internal fun indexOfGetPlaybackSpeedInstruction(method: Method) =
|
||||
method.indexOfFirstInstructionReversed {
|
||||
opcode == Opcode.IGET &&
|
||||
getReference<FieldReference>()?.type == "F"
|
||||
}
|
||||
@@ -43,6 +43,7 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ val bypassURLRedirectsPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ val openLinksExternallyPatch = bytecodePatch(
|
||||
"19.43.41",
|
||||
"19.45.38",
|
||||
"19.46.42",
|
||||
"19.47.53",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user