mirror of
https://github.com/SinTan1729/ReVancedBuilder.git
synced 2024-12-26 12:48:36 -06:00
commit
5982b5aa5f
16 changed files with 684 additions and 596 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -7,3 +7,7 @@ build/
|
||||||
.editorconfig
|
.editorconfig
|
||||||
patches.txt
|
patches.txt
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.directory
|
||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
ReVancedBuilder.egg-info/
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
# Customizing ReVanced Builds
|
|
||||||
|
|
||||||
**Please read the following information before beginning.**
|
|
||||||
|
|
||||||
By default the script will build ReVanced with ALL default* patches. Copy `chosen_patches.txt` inside your provided working directory and edit it to customize your build of ReVanced.
|
|
||||||
|
|
||||||
*Default: All patches except those which have to be ***included*** explicitly, i.e, using the `-i` flag while manually using the ReVanced CLI
|
|
||||||
|
|
||||||
## !IMPORTANT!
|
|
||||||
1. Each patch name MUST start from a NEWLINE AND there should be only ONE patch PER LINE
|
|
||||||
2. DO NOT add any other type of symbol or character, it will break the script! You have been warned!
|
|
||||||
3. Anything starting with a hash (`#`) will be ignored. Also, do not add hash or any other character after a patch's name
|
|
||||||
4. Both YT Music ReVanced & YT ReVanced are supported
|
|
||||||
5. DO NOT add `microg-patch` to the list of excluding patches.
|
|
||||||
6. `patches.txt` contains some predefined lines starting with `#`. DO NOT remove them.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
Example content of `patches.txt`:
|
|
||||||
|
|
||||||
- Exclude pure black theme and keep `create` button:
|
|
||||||
```
|
|
||||||
amoled
|
|
||||||
disable-create-button
|
|
||||||
```
|
|
||||||
|
|
||||||
- Exclude patches for both Music & YouTube (order doesn't matter)
|
|
||||||
```
|
|
||||||
amoled
|
|
||||||
exclusive-background-playback
|
|
||||||
disable-create-button
|
|
||||||
premium-heading
|
|
||||||
tasteBuilder-remover
|
|
||||||
```
|
|
||||||
|
|
||||||
- Include patches for both Music & YouTube (order doesn't matter)
|
|
||||||
```
|
|
||||||
compact-header
|
|
||||||
hdr-auto-brightness
|
|
||||||
autorepeat-by-default
|
|
||||||
enable-debugging
|
|
||||||
force-vp9-codec
|
|
||||||
enable-wide-searchbar
|
|
||||||
```
|
|
||||||
|
|
||||||
## List of Available Patches
|
|
||||||
|
|
||||||
Refer to Official ReVanced [list of available patches](https://github.com/revanced/revanced-patches#list-of-available-patches).
|
|
34
README.md
34
README.md
|
@ -1,12 +1,19 @@
|
||||||
# Revanced Builder
|
# Revanced Builder
|
||||||
This repo will allow one to build [ReVanced](https://github.com/revanced/) apps automatically and post it to a telegram channel to access and possibly share the builds with friends. It uses [Gotify](https://gotify.net), [ntfy.sh](https://ntfy.sh) or [telegram.sh](https://github.com/fabianonline/telegram.sh) to send messages and [telegram-upload](https://github.com/Nekmo/telegram-upload) to upload files (optionally, disabled out by default). Make sure that `Java >=17` is installed and selected as default.
|
This repo will allow one to build [ReVanced](https://github.com/revanced/) apps automatically and send notifications and possibly share the builds with friends. It uses [Gotify](https://gotify.net), [ntfy.sh](https://ntfy.sh) or [telegram.sh](https://github.com/fabianonline/telegram.sh) to send messages. Make sure that `Java >=17` is installed and selected as default.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
Recommended way is to use [`pipx`](https://github.com/pypa/pipx) to install the program.
|
||||||
|
```
|
||||||
|
pipx install git+https://github.com/SinTan1729/ReVancedBuilder
|
||||||
|
```
|
||||||
## How to use
|
## How to use
|
||||||
Just run `./build_revanced <working-directory> (force/clean/experimental/checkonly/buildonly)`. Might be a good idea to set it up to run periodically. There are a few ways of doing it.
|
Just run `ReVancedBuilder <working-directory> (force/experimental/checkonly/buildonly)`.
|
||||||
|
|
||||||
|
It might be a good idea to set it up to run periodically. There are a few ways of doing it.
|
||||||
1. Just drop it inside `/etc/cron.daily/`.
|
1. Just drop it inside `/etc/cron.daily/`.
|
||||||
1. To make it run at a specific time (6AM in the example) using `cron`, put this in your `crontab`:
|
1. To make it run at a specific time (6AM in the example) using `cron`, put this in your `crontab`:
|
||||||
```
|
```
|
||||||
0 6 * * * <full-script-location> <full-working-directory-location>
|
0 6 * * * <program-full-location> <full-working-directory-location>
|
||||||
```
|
```
|
||||||
1. The exact same thing as in 2 can be achieved using `systemd` timers instead. Create the following files.
|
1. The exact same thing as in 2 can be achieved using `systemd` timers instead. Create the following files.
|
||||||
```
|
```
|
||||||
|
@ -22,7 +29,7 @@ Just run `./build_revanced <working-directory> (force/clean/experimental/checkon
|
||||||
User=<user>
|
User=<user>
|
||||||
Group=<group>
|
Group=<group>
|
||||||
Environment="_JAVA_OPTIONS=-Xmx512m"
|
Environment="_JAVA_OPTIONS=-Xmx512m"
|
||||||
ExecStart=<full-script-location> <full-working-directory-location>
|
ExecStart=<program-full-location> <full-working-directory-location>
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
/etc/systemd/system/revanced-builder.timer
|
/etc/systemd/system/revanced-builder.timer
|
||||||
|
@ -42,19 +49,12 @@ Just run `./build_revanced <working-directory> (force/clean/experimental/checkon
|
||||||
```
|
```
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
- The following programs are needed to run this script. Make sure that you have them in your `$PATH`.
|
- If you installed it using `pipx`, you can figure out the full location of the program by running `which ReVancedBuilder`.
|
||||||
```
|
- This app needs some config files to run. Download all the config files inside `exampl_configs` directory, namely `build_config`, `chosen_patches` (optional), and `notification_config` (optional, needed only if you want to send notifications) and move them to your working directory. Then, you should modify these files to your liking.
|
||||||
htmlq jq wget java curl
|
- The script will download the **automatically selected compatible version** (unless version is specified in `build_config`) (using compatibility of patches as listed [here](https://github.com/revanced/revanced-patches#list-of-available-patches)) of Youtube on APKPure, **NOT** latest official version on Google Play.
|
||||||
```
|
|
||||||
- To enable build for a particular apk, copy the `build_settings` file to your working directory and modify it to suit your needs.
|
|
||||||
- The script will download the **automatically selected compatible version** (using compatibility of patches as listed [here](https://github.com/revanced/revanced-patches#list-of-available-patches)) of Youtube on APKPure, **NOT** latest official version on Google Play.
|
|
||||||
- Under **NO CIRCUMSTANCES** any APKs will be uploaded to this repository to avoid DMCA.
|
- Under **NO CIRCUMSTANCES** any APKs will be uploaded to this repository to avoid DMCA.
|
||||||
- If you enable the Gotify, ntfy or telegram notifications or uploads, make sure to fill up the config options inside the `build_settings` file. For more information about the config, take at look at the repos of `telegram.sh` and `telegram-upload` provided above.
|
- If you enable telegram notifications, make sure to fill up the config options inside the `build_config` file. For more information about the config, take at look at the repos of `telegram.sh` and `telegram-upload` provided above.
|
||||||
- It can also run a post script (if exists) called `post_script.sh`. The `timestamp` is passed as `$1`.
|
- It can also run a post script (if exists), specified in the `build_config` file. The `timestamp` is passed as `$1`.
|
||||||
- In the current configuration, the script only builds YouTube ReVanced and YouTube Music ReVanced (both nonroot), but it's easy to add support for any other ReVanced app. The code for root builds is included but disabled by default.
|
- In the current configuration, the script only builds YouTube ReVanced and YouTube Music ReVanced (both nonroot), but it's easy to add support for any other ReVanced app using the `build_config` file. The config files are self-explanatory.
|
||||||
- All the packages are pulled from [APKPure](https://apkpure.com) and GitHub (the `revanced/*` repos).
|
- All the packages are pulled from [APKPure](https://apkpure.com) and GitHub (the `revanced/*` repos).
|
||||||
|
|
||||||
## Customize your build
|
|
||||||
If you wish to continue with the default settings, you may skip this step.
|
|
||||||
|
|
||||||
By default this will build ReVanced with ALL available patches. Follow [this guide](PATCHES_GUIDE.md) to exclude/customizing patches for your build.
|
|
||||||
|
|
|
@ -1,324 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Run only one instance of this script at one time
|
|
||||||
[ "${BKLOCKER}" != "running" ] && exec env BKLOCKER="running" flock -en "/tmp/revanced-builder.lock" "$0" "$@" || :
|
|
||||||
|
|
||||||
# Get timestamp
|
|
||||||
timestamp=$(date '+%Y%m%d%H%M%S')
|
|
||||||
|
|
||||||
# Log everything to a logfile inside logs/
|
|
||||||
log_file="$1/logs/$timestamp.log"
|
|
||||||
[ -d "$1" ] && mkdir -p "$1/logs" && exec > >(tee "$log_file") 2>&1
|
|
||||||
|
|
||||||
# Set working directory and current directory
|
|
||||||
if [ -d "$1" ]; then
|
|
||||||
WDIR="$1"
|
|
||||||
else
|
|
||||||
echo "Working directory not provided"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# File containing all patches
|
|
||||||
patch_file="$WDIR/chosen_patches.txt"
|
|
||||||
|
|
||||||
# Returns if $1 is less than $2
|
|
||||||
ver_less_than() {
|
|
||||||
# Strip letters from version name
|
|
||||||
ver1=$(echo $1 | sed 's/[a-zA-Z]*//g')
|
|
||||||
ver2=$(echo $2 | sed 's/[a-zA-Z]*//g')
|
|
||||||
[ $(echo $ver1$'\n'$ver2 | sort -V | tail -n1) != $ver1 ] && echo true || echo false
|
|
||||||
}
|
|
||||||
|
|
||||||
# Make sure to work in the script directory
|
|
||||||
SDIR="$(dirname -- "$(readlink -f -- "$0")")"
|
|
||||||
cd "$SDIR"
|
|
||||||
|
|
||||||
# Read the settings
|
|
||||||
if [ -f "$WDIR/build_settings" ]; then
|
|
||||||
source "$WDIR/build_settings"
|
|
||||||
else
|
|
||||||
if [ -f "./build_settings"]; then
|
|
||||||
cp ./build_settings "$WDIR/build_settings"
|
|
||||||
source ./build_settings
|
|
||||||
else
|
|
||||||
echo "Could not find the build_settings file!"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Get line numbers where included & excluded patches start from.
|
|
||||||
# We rely on the hardcoded messages to get the line numbers using grep
|
|
||||||
excluded_start="$(grep -n -m1 'EXCLUDE PATCHES' "$patch_file" | cut -d':' -f1)"
|
|
||||||
included_start="$(grep -n -m1 'INCLUDE PATCHES' "$patch_file" | cut -d':' -f1)"
|
|
||||||
|
|
||||||
# Get everything but hashes from between the EXCLUDE PATCH & INCLUDE PATCH line
|
|
||||||
# Note: '^[^#[:blank:]]' ignores starting hashes and/or blank characters i.e, whitespace & tab excluding newline
|
|
||||||
excluded_patches="$(tail -n +$excluded_start $patch_file | head -n "$((included_start - excluded_start))" | grep '^[^#[:blank:]]')"
|
|
||||||
|
|
||||||
# Get everything but hashes starting from INCLUDE PATCH line until EOF
|
|
||||||
included_patches="$(tail -n +$included_start $patch_file | grep '^[^#[:blank:]]')"
|
|
||||||
|
|
||||||
# Array for storing patches
|
|
||||||
declare -a patches
|
|
||||||
|
|
||||||
# Required artifacts in the format repository-name_filename
|
|
||||||
artifacts="revanced/revanced-cli:revanced-cli.jar revanced/revanced-integrations:revanced-integrations.apk revanced/revanced-patches:revanced-patches.jar inotia00/VancedMicroG:microg.apk"
|
|
||||||
|
|
||||||
## Functions
|
|
||||||
|
|
||||||
# Function for populating patches array, using a function here reduces redundancy & satisfies DRY principals
|
|
||||||
populate_patches() {
|
|
||||||
# Note: <<< defines a 'here-string'. Meaning, it allows reading from variables just like from a file
|
|
||||||
while read -r patch; do
|
|
||||||
patches+=("$1 $patch")
|
|
||||||
done <<<"$2"
|
|
||||||
}
|
|
||||||
|
|
||||||
## Main
|
|
||||||
|
|
||||||
# cleanup to fetch new revanced on next run
|
|
||||||
if [[ "$2" == "clean" ]]; then
|
|
||||||
rm -f revanced-cli.jar revanced-integrations.apk revanced-patches.jar
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$2" == "experimental" ]]; then
|
|
||||||
EXPERIMENTAL="--experimental"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set flag to determine if a build should happen or not
|
|
||||||
flag=false
|
|
||||||
check_flag=false
|
|
||||||
|
|
||||||
# Get inside the working directory
|
|
||||||
cd "$WDIR"
|
|
||||||
echo "$(date) | Starting check..."
|
|
||||||
|
|
||||||
if [[ $2 != buildonly ]]; then
|
|
||||||
# Create a new versions file, if needed
|
|
||||||
[ -f versions.json ] || echo "{}" >versions.json
|
|
||||||
cp versions.json versions-new.json
|
|
||||||
# Fetch all the dependencies
|
|
||||||
try=0
|
|
||||||
while :; do
|
|
||||||
try=$(($try + 1))
|
|
||||||
[ $try -gt 10 ] && echo "API error!" && exit 2
|
|
||||||
curl -s -X 'GET' 'https://releases.revanced.app/tools' -H 'accept: application/json' -o latest_versions.json
|
|
||||||
cat latest_versions.json | jq -e '.error' >/dev/null || break
|
|
||||||
echo "API failure, trying again. $((10 - $try)) tries left..."
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
|
|
||||||
for artifact in $artifacts; do
|
|
||||||
#Check for updates
|
|
||||||
repo=$(echo $artifact | cut -d ':' -f1)
|
|
||||||
name=$(echo $artifact | cut -d ':' -f2)
|
|
||||||
basename=$(echo $repo | cut -d '/' -f2)
|
|
||||||
echo "Checking $basename"
|
|
||||||
version_present=$(jq -r ".\"$basename\"" versions.json)
|
|
||||||
[[ "$version_present" == "null" ]] && version_present=0
|
|
||||||
data="$(jq -r ".tools[] | select((.repository == \"$repo\") and (.content_type | contains(\"archive\")))" latest_versions.json)"
|
|
||||||
[[ $name == microg.apk ]] && version=$(curl -s "https://api.github.com/repos/$repo/releases/latest" | jq -r '.tag_name') || version=$(echo "$data" | jq -r '.version')
|
|
||||||
if [[ $(ver_less_than $version_present $version) == true || ! -f $name || $2 == force ]]; then
|
|
||||||
if [[ $2 == checkonly ]]; then
|
|
||||||
echo "[checkonly] $basename has an update ($version_present -> $version)"
|
|
||||||
check_flag=true
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
echo "Downloading $name"
|
|
||||||
[[ $name == microg.apk && -f $name && $2 != force ]] && microg_updated=true
|
|
||||||
# shellcheck disable=SC2086,SC2046
|
|
||||||
[[ $name == microg.apk ]] && download_link="https://github.com/$repo/releases/latest/download/$name" || download_link="$(echo "$data" | jq -r '.browser_download_url')"
|
|
||||||
curl -sLo "$name" "$download_link"
|
|
||||||
jq ".\"$basename\" = \"$version\"" versions-new.json >versions-tmp.json && mv versions-tmp.json versions-new.json
|
|
||||||
echo "Upgraded $basename from $version_present to $version"
|
|
||||||
flag=true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
[[ ! -f com.google.android.youtube.apk || ! -f com.google.android.apps.youtube.music.apk ]] && flag=true
|
|
||||||
|
|
||||||
# Exit if no updates happened
|
|
||||||
if [[ $flag == false && $2 != force ]]; then
|
|
||||||
if [[ $check_flag == false ]]; then
|
|
||||||
echo "Nothing to update"
|
|
||||||
else
|
|
||||||
"$SDIR/download_apk.sh" "$WDIR" checkonly
|
|
||||||
fi
|
|
||||||
echo "--------------------"$'\n'"--------------------"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Download required apk files
|
|
||||||
"$SDIR/download_apk.sh" "$WDIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# If the variables are NOT empty, call populate_patches with proper arguments
|
|
||||||
[[ ! -z "$excluded_patches" ]] && populate_patches "-e" "$excluded_patches"
|
|
||||||
[[ ! -z "$included_patches" ]] && populate_patches "-i" "$included_patches"
|
|
||||||
|
|
||||||
# Variable to flag errors
|
|
||||||
error=0
|
|
||||||
|
|
||||||
# Functions for building the APKs
|
|
||||||
|
|
||||||
build_yt_nonroot() {
|
|
||||||
echo "************************************"
|
|
||||||
echo "Building YouTube APK"
|
|
||||||
echo "************************************"
|
|
||||||
if [ -f "com.google.android.youtube.apk" ]; then
|
|
||||||
echo "Building Non-root APK"
|
|
||||||
java -jar revanced-cli.jar -m revanced-integrations.apk -b revanced-patches.jar \
|
|
||||||
${patches[@]} \
|
|
||||||
$EXPERIMENTAL \
|
|
||||||
-a com.google.android.youtube.apk -o revanced-yt-nonroot.apk
|
|
||||||
else
|
|
||||||
echo "Cannot find YouTube APK, skipping build"
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
echo "************************************"
|
|
||||||
|
|
||||||
# Rename files
|
|
||||||
mv revanced-yt-nonroot.apk YouTube_ReVanced_nonroot_$timestamp.apk || error=1
|
|
||||||
}
|
|
||||||
|
|
||||||
build_yt_root() {
|
|
||||||
echo "************************************"
|
|
||||||
echo "Building YouTube APK"
|
|
||||||
echo "************************************"
|
|
||||||
if [ -f "com.google.android.youtube.apk" ]; then
|
|
||||||
echo "Building Root APK"
|
|
||||||
java -jar revanced-cli.jar -m revanced-integrations.apk -b revanced-patches.jar --mount \
|
|
||||||
-e microg-support ${patches[@]} \
|
|
||||||
$EXPERIMENTAL \
|
|
||||||
-a com.google.android.youtube.apk -o revanced-yt-root.apk
|
|
||||||
else
|
|
||||||
echo "Cannot find YouTube APK, skipping build"
|
|
||||||
fi
|
|
||||||
echo ""
|
|
||||||
echo "************************************"
|
|
||||||
|
|
||||||
# Rename files
|
|
||||||
mv revanced-yt-root.apk YouTube_ReVanced_root_$timestamp.apk || error=1
|
|
||||||
}
|
|
||||||
|
|
||||||
build_ytm_nonroot() {
|
|
||||||
echo "Building YouTube Music APK"
|
|
||||||
echo "************************************"
|
|
||||||
if [ -f "com.google.android.apps.youtube.music.apk" ]; then
|
|
||||||
echo "Building Non-root APK"
|
|
||||||
java -jar revanced-cli.jar -m revanced-integrations.apk -b revanced-patches.jar \
|
|
||||||
${patches[@]} \
|
|
||||||
$EXPERIMENTAL \
|
|
||||||
-a com.google.android.apps.youtube.music.apk -o revanced-ytm-nonroot.apk
|
|
||||||
else
|
|
||||||
echo "Cannot find YouTube Music APK, skipping build"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rename files
|
|
||||||
mv revanced-ytm-nonroot.apk YouTube_Music_ReVanced_nonroot_$timestamp.apk || error=1
|
|
||||||
}
|
|
||||||
|
|
||||||
build_ytm_root() {
|
|
||||||
echo "Building YouTube Music APK"
|
|
||||||
echo "************************************"
|
|
||||||
if [ -f "com.google.android.apps.youtube.music.apk" ]; then
|
|
||||||
echo "Building Root APK"
|
|
||||||
java -jar revanced-cli.jar -m revanced-integrations.apk -b revanced-patches.jar --mount \
|
|
||||||
-e microg-support ${patches[@]} \
|
|
||||||
$EXPERIMENTAL \
|
|
||||||
-a com.google.android.apps.youtube.music.apk -o revanced-ytm-root.apk
|
|
||||||
else
|
|
||||||
echo "Cannot find YouTube Music APK, skipping build"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rename files
|
|
||||||
mv revanced-ytm-root.apk YouTube_Music_ReVanced_root_$timestamp.apk || error=1
|
|
||||||
}
|
|
||||||
|
|
||||||
telegram_send_msg() {
|
|
||||||
# telegram.sh uses bot account, but it supports formatted messages
|
|
||||||
[[ "$TELEGRAM_TOKEN" == "" || "$TELEGRAM_CHAT" == "" ]] && echo "Please provide valid channel address in the settings!"
|
|
||||||
./telegram.sh -t "$TELEGRAM_TOKEN" -c "$TELEGRAM_CHAT" -T "⚙⚙⚙ Build Details ⚙⚙⚙" -M "$1"$'\n'"Timestamp: $timestamp"$'\n'"⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯"
|
|
||||||
}
|
|
||||||
|
|
||||||
gotify_send_msg() {
|
|
||||||
curl -s -X POST "$GOTIFY_URL/message?token=$GOTIFY_TOKEN" \
|
|
||||||
-F "title=⚙⚙⚙ Build Details ⚙⚙⚙" -F "message=$1" -F "priority=5"
|
|
||||||
}
|
|
||||||
|
|
||||||
ntfy_send_msg() {
|
|
||||||
curl -s -H "Icon: https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Revanced-logo-round.svg/240px-Revanced-logo-round.svg.png" \
|
|
||||||
-H "Title: ⚙⚙⚙ ReVanced Build ⚙⚙⚙" \
|
|
||||||
-d "$1" \
|
|
||||||
"$NTFY_URL/$NTFY_TOPIC"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check the config and build accordingly
|
|
||||||
$YT_NONROOT && build_yt_nonroot
|
|
||||||
$YT_ROOT && build_yt_root
|
|
||||||
$YTM_NONROOT && build_ytm_nonroot
|
|
||||||
$YTM_ROOT && build_ytm_root
|
|
||||||
|
|
||||||
# Send telegram message about the new build
|
|
||||||
|
|
||||||
if [ $error == 1 ]; then
|
|
||||||
echo "There was an error while building!"
|
|
||||||
msg="There was an error during the build process! Please take a look at the logs."$'\n'"Timestamp: $timestamp"
|
|
||||||
|
|
||||||
$TG_NOTIFICATIONS && telegram_send_msg "$msg"
|
|
||||||
$GOTIFY_NOTIFICATIONS && gotify_send_msg "$msg"
|
|
||||||
$NTFY_NOTIFICATIONS && ntfy_send_msg "$msg"
|
|
||||||
|
|
||||||
[[ $2 != buildonly ]] && mv versions-new.json versions-fail.json || rm versions-new.json
|
|
||||||
exit 4
|
|
||||||
else
|
|
||||||
mv versions.json versions-old.json
|
|
||||||
mv versions-new.json versions.json
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $TG_UPLOAD; then
|
|
||||||
echo "Uploading to telegram"
|
|
||||||
# telegram-upload uses personal account, hence bypassing 50 MB max upload limit of bots
|
|
||||||
[ "$CHANNEL_ADDRESS" == "" ] && echo "Please provide valid channel address in the settings!"
|
|
||||||
/home/sintan/.local/bin/telegram-upload YouTube_ReVanced_nonroot_$timestamp.apk YouTube_Music_ReVanced_nonroot_$timestamp.apk --to "$CHANNEL_ADDRESS" --caption "" && sent=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create the message to be sent
|
|
||||||
msg=$(cat versions.json | tail -n+2 | head -n-1 | cut -c3- | sed "s/\"//g" | sed "s/,//g" | sed "s/com.google.android.apps.youtube.music/YouTube Music/" |
|
|
||||||
sed "s/com.google.android.youtube/YouTube/" | sed "s/VancedMicroG/Vanced microG/" | sed "s/revanced-/ReVanced /g" | sed "s/patches/Patches/" |
|
|
||||||
sed "s/cli/CLI/" | sed "s/integrations/Integrations/" | awk 1 ORS=$'\n') # I know, it's a hacky solution
|
|
||||||
|
|
||||||
if $TG_NOTIFICATIONS; then
|
|
||||||
echo "Sending messages to telegram"
|
|
||||||
telegram_send_msg "$msg"
|
|
||||||
[ $microg_updated ] && telegram_send_msg "_An update of microg was published._"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $GOTIFY_NOTIFICATIONS; then
|
|
||||||
echo "Sending messages to Gotify"
|
|
||||||
MESSAGE="$msg"$'\n'"Timestamp: $timestamp"
|
|
||||||
gotify_send_msg "$MESSAGE"
|
|
||||||
[ $microg_updated ] && gotify_send_msg "An update of microg was published."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if $NTFY_NOTIFICATIONS; then
|
|
||||||
echo "Sending messages to ntfy.sh"
|
|
||||||
MESSAGE="$msg"$'\n'"Timestamp: $timestamp"
|
|
||||||
ntfy_send_msg "$MESSAGE"
|
|
||||||
[ $microg_updated ] && ntfy_send_msg "An update of microg was published."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Do some cleanup, keep only the last 3 build's worth of files and a week worth of logs
|
|
||||||
mkdir -p archive
|
|
||||||
mv *ReVanced_*_$timestamp.apk archive/
|
|
||||||
find ./archive -maxdepth 1 -type f -printf '%Ts\t%P\n' |
|
|
||||||
sort -rn |
|
|
||||||
tail -n +7 |
|
|
||||||
cut -f2- |
|
|
||||||
xargs -r -I {} rm "./archive/{}"
|
|
||||||
find ./logs -mtime +7 -exec rm {} \;
|
|
||||||
|
|
||||||
# Run a custom post script, if available
|
|
||||||
[ -f post_script.sh ] && ./post_script.sh $timestamp
|
|
||||||
|
|
||||||
echo "Done!"$'\n'"************************************"
|
|
|
@ -1,45 +0,0 @@
|
||||||
# These are the currently supported apps that can be built
|
|
||||||
# Make a copy of this to your working directory
|
|
||||||
# Then change the default values to enable/disable building them
|
|
||||||
YT_NONROOT=true
|
|
||||||
YTM_NONROOT=true
|
|
||||||
YT_ROOT=false
|
|
||||||
YTM_ROOT=false
|
|
||||||
|
|
||||||
# You can provide versions of apk for the builds
|
|
||||||
# If anything nonempty is given, automatic version resolution
|
|
||||||
# will be disabled
|
|
||||||
# It's your job to make sure that the version is available
|
|
||||||
# in APKPure
|
|
||||||
YT_VERSION=
|
|
||||||
YTM_VERSION=
|
|
||||||
|
|
||||||
# Settings for sending Telegram notification using telegram.sh
|
|
||||||
# In case you decide to use it, please put valid config in the
|
|
||||||
# TOKEN and CHAT fields
|
|
||||||
# Check out README for instructions
|
|
||||||
TG_NOTIFICATIONS=false
|
|
||||||
TELEGRAM_TOKEN=""
|
|
||||||
TELEGRAM_CHAT=""
|
|
||||||
|
|
||||||
# Settings for uploading the files through telegram-upload
|
|
||||||
# In case you decide to use it, please put valid config in the
|
|
||||||
# CHANNEL_ADDRESS field
|
|
||||||
# Check out README for instructions
|
|
||||||
TG_UPLOAD=false
|
|
||||||
CHANNEL_ADDRESS=""
|
|
||||||
|
|
||||||
# Settings for sending Gotify notifications
|
|
||||||
# In case you decide to use it, please put valid config in the
|
|
||||||
# URL and TOKEN fields
|
|
||||||
# Check out README for instructions
|
|
||||||
GOTIFY_NOTIFICATIONS=false
|
|
||||||
GOTIFY_URL="https://push.example.com"
|
|
||||||
GOTIFY_TOKEN=""
|
|
||||||
|
|
||||||
# Settings for sending ntfy.sh notifications
|
|
||||||
# In case you decide to use it, please put valid config in the
|
|
||||||
# URL and TOPIC fields
|
|
||||||
NTFY_NOTIFICATIONS=false
|
|
||||||
NTFY_URL="https://ntfy.sh"
|
|
||||||
NTFY_TOPIC=""
|
|
|
@ -1,3 +0,0 @@
|
||||||
# EXCLUDE PATCHES FROM BELOW. DO NOT REMOVE THIS LINE
|
|
||||||
|
|
||||||
# INCLUDE PATCHES FROM BELOW. DO NOT REMOVE THIS LINE
|
|
160
download_apk.sh
160
download_apk.sh
|
@ -1,160 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
declare -A apks
|
|
||||||
|
|
||||||
apks["com.google.android.youtube"]=dl_yt
|
|
||||||
apks["com.google.android.apps.youtube.music"]=dl_ytm
|
|
||||||
|
|
||||||
flag=$2
|
|
||||||
|
|
||||||
# Read the settings
|
|
||||||
source "$1/build_settings"
|
|
||||||
|
|
||||||
## Functions
|
|
||||||
|
|
||||||
# Wget user agent
|
|
||||||
WGET_HEADER="User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0"
|
|
||||||
|
|
||||||
# Wget function
|
|
||||||
req() { wget -nv -4 -O "$2" --header="$WGET_HEADER" "$1"; }
|
|
||||||
|
|
||||||
# Returns true if $1 is less than $2
|
|
||||||
ver_less_than() {
|
|
||||||
[[ ${1:0:1} == "v" ]] && var1=${1:1} || var1=$1
|
|
||||||
[[ ${2:0:1} == "v" ]] && var2=${2:1} || var2=$2
|
|
||||||
[[ $(echo $var1$'\n'$var2 | sort -V | tail -n1) != $var1 ]] && echo true || echo false
|
|
||||||
}
|
|
||||||
|
|
||||||
# APKPure Download function
|
|
||||||
dl_apkpure() {
|
|
||||||
version="$1"
|
|
||||||
app="$2"
|
|
||||||
apkpure_appname="$3"
|
|
||||||
$hard_vers && best_match="$version" || best_match="$(apkpure_best_match $version $app $apkpure_appname)"
|
|
||||||
|
|
||||||
# if [[ "$version" == "$best_match" || "$version" == "latest" ]]; then
|
|
||||||
# echo "Downloading version $best_match from APKPure"
|
|
||||||
# else
|
|
||||||
# echo "Unable to get version $version, downloading version $best_match instead"
|
|
||||||
# fi
|
|
||||||
|
|
||||||
vers_code="$(req https://apkpure.com/$apkpure_appname/$app/versions - | htmlq --attribute data-dt-versioncode 'a[data-dt-version="'$version'"][data-dt-apkid^="b\/APK\/"]')"
|
|
||||||
url="https://d.apkpure.com/b/APK/$app?versionCode=$vers_code"
|
|
||||||
|
|
||||||
req "$url" "$app.apk"
|
|
||||||
echo "$url"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the best match even if the desired version isn't there
|
|
||||||
# OUtputs the latest version with supplied version 0
|
|
||||||
apkpure_best_match() {
|
|
||||||
version="$1"
|
|
||||||
app="$2"
|
|
||||||
apkpure_appname="$3"
|
|
||||||
|
|
||||||
vers_list=$(req https://apkpure.com/$apkpure_appname/$app/versions - | htmlq --attribute data-dt-version 'a[data-dt-apkid^="b\/APK\/"]')
|
|
||||||
if [[ "$version" == "latest" ]]; then
|
|
||||||
match="$(echo "$vers_list" | head -1)"
|
|
||||||
elif $(echo "$vers_list" | grep -q "$version"); then
|
|
||||||
match="$version"
|
|
||||||
else
|
|
||||||
match="$(echo "$vers_list"$'\n'"$version" | sort -V | grep -B 1 "$version" | head -1)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$match"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Downloading youtube
|
|
||||||
dl_yt() {
|
|
||||||
appname=com.google.android.youtube
|
|
||||||
$hard_vers || version="$(apkpure_best_match "$version" $appname youtube)"
|
|
||||||
if [[ ! $(ver_less_than "$version_present" "$version") && -f $appname.apk ]]; then
|
|
||||||
echo "Version $version is already present"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $flag == checkonly ]]; then
|
|
||||||
echo "[checkonly] YouTube has an update ($version_present -> $version)"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
echo "Downloading YouTube"
|
|
||||||
|
|
||||||
echo "Choosing version $version"
|
|
||||||
declare -r dl_url=$(dl_apkpure "$version" $appname youtube)
|
|
||||||
echo "YouTube version: $version"
|
|
||||||
echo "downloaded from: [APKMirror - YouTube]($dl_url)"
|
|
||||||
jq ".\"$apk\" = \"$version\"" versions.json >versions-tmp.json && mv versions-tmp.json versions-new.json
|
|
||||||
}
|
|
||||||
|
|
||||||
# Downloading youtube music
|
|
||||||
dl_ytm() {
|
|
||||||
appname=com.google.android.apps.youtube.music
|
|
||||||
$hard_vers || version="$(apkpure_best_match "$version" $appname youtube-music)"
|
|
||||||
if [[ ! $(ver_less_than "$version_present" "$version") && -f $appname.apk ]]; then
|
|
||||||
echo "Version $version is already present"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $flag == checkonly ]]; then
|
|
||||||
echo "[checkonly] YouTube Music has an update ($version_present -> $version)"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
echo "Downloading YouTube Music"
|
|
||||||
|
|
||||||
echo "Choosing version '${version}'"
|
|
||||||
# declare -r dl_url=$(dl_apkpure "$version" $appname youtube-music)
|
|
||||||
dl_apkpure "$version" $appname youtube-music
|
|
||||||
echo "YouTube Music version: $version"
|
|
||||||
echo "downloaded from: [APKMirror - YouTube Music]($dl_url)"
|
|
||||||
jq ".\"$apk\" = \"$version\"" versions.json >versions-tmp.json && mv versions-tmp.json versions-new.json
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get into the build directory
|
|
||||||
|
|
||||||
if [ -d "$1" ]; then
|
|
||||||
cd "$1"
|
|
||||||
else
|
|
||||||
echo "Working directory not provided"
|
|
||||||
exit -1
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Main
|
|
||||||
try=0
|
|
||||||
while :; do
|
|
||||||
try=$(($try + 1))
|
|
||||||
[ $try -gt 10 ] && echo "API error!" && exit 3
|
|
||||||
curl -X 'GET' 'https://releases.revanced.app/patches' -H 'accept: application/json' -o patches.json
|
|
||||||
cat patches.json | jq -e '.error' >/dev/null 2>&1 || break
|
|
||||||
echo "API failure, trying again. $((10 - $try)) tries left..."
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
|
|
||||||
for apk in "${!apks[@]}"; do
|
|
||||||
# Skip if app not specified for build
|
|
||||||
[[ "$apk" == "com.google.android.youtube" && "$YT_NONROOT" == false && "$YT_ROOT" == false ]] && continue
|
|
||||||
[[ "$apk" == "com.google.android.apps.youtube.music" && "$YTM_NONROOT" == false && "$YTM_ROOT" == false ]] && continue
|
|
||||||
echo "Checking $apk"
|
|
||||||
if [[ "$apk" == "com.google.android.youtube" && "$YT_VERSION" != "" ]]; then
|
|
||||||
version="$YT_VERSION"
|
|
||||||
echo "Using version $version for $apk given in build_settings"
|
|
||||||
hard_vers=true
|
|
||||||
elif [[ "$apk" == "com.google.android.apps.youtube.music" && "$YTM_VERSION" != "" ]]; then
|
|
||||||
version="$YTM_VERSION"
|
|
||||||
echo "Using version $version for $apk given in build_settings"
|
|
||||||
hard_vers=true
|
|
||||||
else
|
|
||||||
echo "Figuring out best version for $apk"
|
|
||||||
supported_vers="$(jq -r '.[].compatiblePackages[] | select(.name == "'$apk'") | .versions | last' patches.json)"
|
|
||||||
version=0
|
|
||||||
for vers in $supported_vers; do
|
|
||||||
[ $vers != "null" ] && [[ $(ver_less_than $vers $version) == true || $version == 0 ]] && version=$vers
|
|
||||||
done
|
|
||||||
hard_vers=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
version_present=$(jq -r ".\"$apk\"" versions.json)
|
|
||||||
[[ -z "$version_present" || "$version" == "null" ]] && version_present=0
|
|
||||||
[[ "$version" == "0" ]] && version=latest
|
|
||||||
|
|
||||||
[[ "$version_present" != "$version" || ! -f $apk.apk || $2 == force ]] && ${apks[$apk]} || echo "Recommended version ($version_present) of "$apk" is already present"
|
|
||||||
done
|
|
51
example_configs/build_config
Normal file
51
example_configs/build_config
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# List all the applications to be built in their separate sections.
|
||||||
|
# The version part is optional. If provided, automatic version determination
|
||||||
|
# will be turned off. In that case, you're responsible for ensuring that the
|
||||||
|
# version is present in APKPure.com
|
||||||
|
|
||||||
|
[youtube_nonroot]
|
||||||
|
build = true
|
||||||
|
pretty_name = YouTube
|
||||||
|
apk = com.google.android.youtube
|
||||||
|
apkpure_appname = youtube
|
||||||
|
root = false
|
||||||
|
# Timestamp and extension will be added automatically
|
||||||
|
output_name = YouTube_ReVanced_nonroot
|
||||||
|
keystore = revanced-yt-nonroot.keystore
|
||||||
|
# version = version
|
||||||
|
|
||||||
|
[youtube_root]
|
||||||
|
build = false
|
||||||
|
pretty_name = YouTube (root)
|
||||||
|
apk = com.google.android.youtube
|
||||||
|
apkpure_appname = youtube
|
||||||
|
root = true
|
||||||
|
# Timestamp and extension will be added automatically
|
||||||
|
output_name = YouTube_ReVanced_root
|
||||||
|
keystore = revanced-yt-root.keystore
|
||||||
|
# version = "version"
|
||||||
|
|
||||||
|
[youtube_music]
|
||||||
|
build = true
|
||||||
|
pretty_name = YouTube Music
|
||||||
|
apk = com.google.android.apps.youtube.music
|
||||||
|
apkpure_appname = youtube-music
|
||||||
|
root = false
|
||||||
|
# Timestamp and extension will be added automatically
|
||||||
|
output_name = YouTube_Music_ReVanced_nonroot
|
||||||
|
keystore = revanced-ytm-nonroot.keystore
|
||||||
|
# version = version
|
||||||
|
|
||||||
|
[youtube_music_root]
|
||||||
|
build = false
|
||||||
|
pretty_name = YouTube Music (root)
|
||||||
|
apk = com.google.android.apps.youtube.music
|
||||||
|
apkpure_appname = youtube-music
|
||||||
|
root = true
|
||||||
|
# Timestamp and extension will be added automatically
|
||||||
|
output_name = YouTube_Music_ReVanced_root
|
||||||
|
keystore = revanced-ytm-root.keystore
|
||||||
|
# version = version
|
||||||
|
|
||||||
|
[post_script]
|
||||||
|
# file = post_script.sh
|
4
example_configs/chosen_patches
Normal file
4
example_configs/chosen_patches
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
[patches]
|
||||||
|
# Both have to be comma separated lists of patches
|
||||||
|
included = []
|
||||||
|
excluded = []
|
25
example_configs/notification_config
Normal file
25
example_configs/notification_config
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
[telegram]
|
||||||
|
# Settings for sending Telegram notification using telegram.sh
|
||||||
|
# In case you decide to use it, please put valid config in the
|
||||||
|
# TOKEN and CHAT fields
|
||||||
|
# Check out README for instructions
|
||||||
|
# enabled = true
|
||||||
|
# chat = url
|
||||||
|
# token = token
|
||||||
|
|
||||||
|
[gotify]
|
||||||
|
# Settings for sending Gotify notifications
|
||||||
|
# In case you decide to use it, please put valid config in the
|
||||||
|
# URL and TOKEN fields
|
||||||
|
# Check out README for instructions
|
||||||
|
# enabled = true
|
||||||
|
# URL = url
|
||||||
|
# token = token
|
||||||
|
|
||||||
|
[ntfy]
|
||||||
|
# Settings for sending ntfy.sh notifications
|
||||||
|
# In case you decide to use it, please put valid config in the
|
||||||
|
# URL and TOPIC fields
|
||||||
|
enabled = false
|
||||||
|
url = url
|
||||||
|
topic = topic
|
18
pyproject.toml
Normal file
18
pyproject.toml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "ReVancedBuilder"
|
||||||
|
authors = [{ name = "Sayantan Santra", email = "sayantan.santra689@gmail.com" }]
|
||||||
|
description = "A tool to automatically build latest releases of ReVanced apps"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
keywords = ["revanced", "patch"]
|
||||||
|
license = { file = "LICENSE" }
|
||||||
|
classifiers = ["Programming Language :: Python :: 3"]
|
||||||
|
dependencies = ["requests", "packaging", "bs4"]
|
||||||
|
version = "1.0"
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
ReVancedBuilder = "ReVancedBuilder:ReVancedBuilder"
|
120
src/ReVancedBuilder/APKPure_dl.py
Normal file
120
src/ReVancedBuilder/APKPure_dl.py
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
#!/usr/bin/env python3\
|
||||||
|
|
||||||
|
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
|
from packaging.version import Version
|
||||||
|
import requests as req
|
||||||
|
from bs4 import BeautifulSoup as bs
|
||||||
|
|
||||||
|
from ReVancedBuilder.Cleanup import clean_exit
|
||||||
|
|
||||||
|
# Determine the best version available to download
|
||||||
|
def apkpure_best_match(version, soup):
|
||||||
|
try:
|
||||||
|
vers_list = [Version(x['data-dt-version']) for x in soup.css.select(f"a[data-dt-apkid^=\"b/APK/\"]")]
|
||||||
|
except:
|
||||||
|
clean_exit(f" There was some error getting list of versions of {apk}...", appstate)
|
||||||
|
|
||||||
|
if version != '0':
|
||||||
|
vers_list = filter(lambda x: x <= Version(version), vers_list)
|
||||||
|
|
||||||
|
return str(max(vers_list))
|
||||||
|
|
||||||
|
# Download an apk from APKPure.com
|
||||||
|
def apkpure_dl(apk, appname, version, hard_version, session, present_vers, flag):
|
||||||
|
res = session.get(f"https://apkpure.com/{appname}/{apk}/versions")
|
||||||
|
res.raise_for_status()
|
||||||
|
soup = bs(res.text, 'html.parser')
|
||||||
|
|
||||||
|
if not hard_version:
|
||||||
|
version = apkpure_best_match(version, soup)
|
||||||
|
|
||||||
|
if flag == 'checkonly' and present_vers[apk] != version:
|
||||||
|
print(f"{apk} has an update ({present_vers[apk]} -> {version})")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
if present_vers[apk] == version and flag != 'force' and os.path.isfile(apk+'.apk'):
|
||||||
|
print(f"Recommended version {version} of {apk} is already present.")
|
||||||
|
return
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
print(f" Downloading {apk} version {version}...")
|
||||||
|
|
||||||
|
# Get the version code
|
||||||
|
try:
|
||||||
|
ver_code = soup.css.select(f"a[data-dt-version=\"{version}\"][data-dt-apkid^=\"b/APK/\"]")[0]['data-dt-versioncode']
|
||||||
|
except:
|
||||||
|
clean_exit(f" There was some error while downloading {apk}...", appstate)
|
||||||
|
|
||||||
|
res = session.get(f"https://d.apkpure.com/b/APK/{apk}?versionCode={ver_code}", stream=True)
|
||||||
|
res.raise_for_status()
|
||||||
|
with open(apk+'.apk', 'wb') as f:
|
||||||
|
for chunk in res.iter_content(chunk_size=8192):
|
||||||
|
f.write(chunk)
|
||||||
|
print(" Done!")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Download apk files, if needed
|
||||||
|
def get_apks(appstate):
|
||||||
|
present_vers = appstate['present_vers']
|
||||||
|
build_config = appstate['build_config']
|
||||||
|
flag=appstate['flag']
|
||||||
|
|
||||||
|
print('Downloading required apk files from APKPure...')
|
||||||
|
|
||||||
|
# Get latest patches using the ReVanced API
|
||||||
|
try:
|
||||||
|
patches = req.get('https://releases.revanced.app/patches').json()
|
||||||
|
except req.exceptions.RequestException as e:
|
||||||
|
clean_exit(e, appstate)
|
||||||
|
|
||||||
|
session = req.Session()
|
||||||
|
session.headers.update({'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0'})
|
||||||
|
|
||||||
|
for app in build_config:
|
||||||
|
# Check if we need to build an app
|
||||||
|
if not build_config[app].getboolean('build'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
apk = build_config[app]['apk']
|
||||||
|
pretty_name = build_config[app]['pretty_name']
|
||||||
|
apkpure_appname = build_config[app]['apkpure_appname']
|
||||||
|
except:
|
||||||
|
clean_exit(f"Invalid config for {app} in build_config!", appstate)
|
||||||
|
|
||||||
|
print(f"Checking {pretty_name}...")
|
||||||
|
try:
|
||||||
|
required_ver = build_config[app]['version']
|
||||||
|
required_ver[0]
|
||||||
|
hard_version = True
|
||||||
|
print(f"Using version {required_ver} of {app} from ")
|
||||||
|
except:
|
||||||
|
hard_version = False
|
||||||
|
compatible_vers = []
|
||||||
|
for patch in patches:
|
||||||
|
for pkg in patch['compatiblePackages']:
|
||||||
|
if pkg['name'] == apk:
|
||||||
|
try:
|
||||||
|
compatible_vers.append(pkg['versions'][-1])
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not compatible_vers:
|
||||||
|
required_ver = Version('0')
|
||||||
|
else:
|
||||||
|
required_ver = min(map(lambda x: Version(x), compatible_vers))
|
||||||
|
|
||||||
|
apkpure_dl(apk, apkpure_appname, str(required_ver), hard_version, session, present_vers, flag)
|
||||||
|
|
||||||
|
present_vers.update({apk: str(required_ver)})
|
||||||
|
|
||||||
|
appstate['present_vers'] = present_vers
|
||||||
|
return appstate
|
67
src/ReVancedBuilder/Cleanup.py
Normal file
67
src/ReVancedBuilder/Cleanup.py
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
from ReVancedBuilder.Notifications import send_notif
|
||||||
|
|
||||||
|
# Move apps to proper location
|
||||||
|
def move_apps(appstate):
|
||||||
|
build_config = appstate['build_config']
|
||||||
|
print = appstate['logger'].info
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.mkdir('archive')
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for app in build_config:
|
||||||
|
if not build_config[app].getboolean('build'):
|
||||||
|
continue
|
||||||
|
name = build_config[app]['output_name']
|
||||||
|
final_name = f"{name}_{appstate['timestamp']}.apk"
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.rename(name+'.apk', 'archive/'+final_name)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
# sys.exit('There was an error moving the final apk files!')
|
||||||
|
|
||||||
|
# Do some cleanup, keep only the last 3 build's worth of files and a week worth of logs
|
||||||
|
with os.scandir('archive') as dir:
|
||||||
|
files = []
|
||||||
|
for f in dir:
|
||||||
|
if name in f.name:
|
||||||
|
files.append(f)
|
||||||
|
files.sort(key=lambda f: f.stat().st_ctime)
|
||||||
|
files.reverse()
|
||||||
|
for f in files[3:]:
|
||||||
|
os.remove(f)
|
||||||
|
print('Deleted old build '+f.name)
|
||||||
|
|
||||||
|
# Delete logs older than 7 days
|
||||||
|
with os.scandir('logs') as dir:
|
||||||
|
now = time.time()
|
||||||
|
for f in dir:
|
||||||
|
if f.stat().st_ctime < now - 7 * 86400:
|
||||||
|
os.remove(f)
|
||||||
|
|
||||||
|
def clean_exit(msg, appstate, code=1):
|
||||||
|
print = appstate['logger'].info
|
||||||
|
|
||||||
|
try:
|
||||||
|
appstate['notification_config']
|
||||||
|
send_notif(appstate, error=True)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if msg:
|
||||||
|
print(msg)
|
||||||
|
|
||||||
|
# Delete the lockfile
|
||||||
|
os.remove('lockfile')
|
||||||
|
sys.exit(code)
|
91
src/ReVancedBuilder/JAVABuilder.py
Normal file
91
src/ReVancedBuilder/JAVABuilder.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import configparser as cp
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from ReVancedBuilder.Cleanup import clean_exit
|
||||||
|
|
||||||
|
# Build the revanced apps
|
||||||
|
def build_apps(appstate):
|
||||||
|
build_config = appstate['build_config']
|
||||||
|
flag = appstate['flag']
|
||||||
|
print = appstate['logger'].info
|
||||||
|
|
||||||
|
chosen_patches = cp.ConfigParser()
|
||||||
|
chosen_patches.read('chosen_patches')
|
||||||
|
|
||||||
|
try:
|
||||||
|
included_patches = json.loads(chosen_patches['patches']['included'])
|
||||||
|
except:
|
||||||
|
included_patches = []
|
||||||
|
try:
|
||||||
|
excluded_patches = json.loads(chosen_patches['patches']['excluded'])
|
||||||
|
except Exception as e:
|
||||||
|
excluded_patches = []
|
||||||
|
|
||||||
|
for app in build_config:
|
||||||
|
# Check if we need to build an app
|
||||||
|
if not build_config[app].getboolean('build'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Build the command to be run
|
||||||
|
cmd = 'java -jar revanced-cli.jar -m revanced-integrations.apk -b revanced-patches.jar'
|
||||||
|
|
||||||
|
try:
|
||||||
|
root = build_config[app].getboolean('root')
|
||||||
|
except:
|
||||||
|
root = False
|
||||||
|
|
||||||
|
if root:
|
||||||
|
cmd += ' --mount -e microg-support'
|
||||||
|
|
||||||
|
for item in included_patches:
|
||||||
|
cmd += f" -i {item}"
|
||||||
|
for item in excluded_patches:
|
||||||
|
cmd += f" -e {item}"
|
||||||
|
|
||||||
|
if flag == 'experimental':
|
||||||
|
cmd += ' --experimental'
|
||||||
|
|
||||||
|
try:
|
||||||
|
keystore = build_config[app]['keystore']
|
||||||
|
if not root:
|
||||||
|
cmd += f" --keystore {keystore}"
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
apk = build_config[app]['apk']
|
||||||
|
pretty_name = build_config[app]['pretty_name']
|
||||||
|
apkpure_appname = build_config[app]['apkpure_appname']
|
||||||
|
output_name = build_config[app]['output_name']
|
||||||
|
except:
|
||||||
|
clean_exit(f"Invalid config for {app} in build_config!", appstate)
|
||||||
|
|
||||||
|
cmd += f" -a {apk}.apk -o {output_name}.apk"
|
||||||
|
|
||||||
|
if root:
|
||||||
|
print(f"Building {pretty_name} (root)...")
|
||||||
|
else:
|
||||||
|
print(f"Building {pretty_name} (nonroot)...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with subprocess.Popen(cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout as output:
|
||||||
|
for line in output:
|
||||||
|
line_utf = line.decode('utf-8').strip('\n')
|
||||||
|
if line_utf:
|
||||||
|
print(line_utf)
|
||||||
|
except Exception as e:
|
||||||
|
clean_exit(f"There was an error while building {pretty_name}!\n{e}", appstate)
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.rename(output_name+'.apk', output_name+'.apk')
|
||||||
|
except FileNotFoundError:
|
||||||
|
clean_exit(f"There was an error while building {pretty_name}!", appstate)
|
||||||
|
|
96
src/ReVancedBuilder/Notifications.py
Normal file
96
src/ReVancedBuilder/Notifications.py
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import requests as req
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
def send_notif(appstate, error=False):
|
||||||
|
print = appstate['logger'].info
|
||||||
|
timestamp = appstate['timestamp']
|
||||||
|
|
||||||
|
if error:
|
||||||
|
msg = f"There was an error during build! Please check the logs.\nTimestamp: {timestamp}"
|
||||||
|
else:
|
||||||
|
notification_config = appstate['notification_config']
|
||||||
|
build_config = appstate['build_config']
|
||||||
|
present_vers = appstate['present_vers']
|
||||||
|
flag = appstate['flag']
|
||||||
|
|
||||||
|
msg = json.dumps(present_vers, indent=0)
|
||||||
|
msg = re.sub('("|\{|\}|,)', '', msg).strip('\n')
|
||||||
|
|
||||||
|
msg = msg.replace('revanced-', 'ReVanced ')
|
||||||
|
msg = msg.replace('cli', 'CLI')
|
||||||
|
msg = msg.replace('integrations', 'Integrations')
|
||||||
|
msg = msg.replace('patches', 'Patches')
|
||||||
|
msg = msg.replace('VancedMicroG', 'Vanced microG')
|
||||||
|
|
||||||
|
for app in build_config:
|
||||||
|
if not build_config[app].getboolean('build'):
|
||||||
|
continue
|
||||||
|
msg = msg.replace(build_config[app]['apk'], build_config[app]['pretty_name'])
|
||||||
|
|
||||||
|
msg += '\nTimestamp: ' + timestamp
|
||||||
|
if appstate['microg_updated']:
|
||||||
|
msg += '\nVanced microG was updated.'
|
||||||
|
|
||||||
|
config = appstate['notification_config']
|
||||||
|
for entry in config:
|
||||||
|
if not config[entry].getboolean('enabled'):
|
||||||
|
continue
|
||||||
|
encoded_title = '⚙⚙⚙ ReVanced Build ⚙⚙⚙'.encode('utf-8')
|
||||||
|
|
||||||
|
match entry:
|
||||||
|
case 'ntfy':
|
||||||
|
print('Sending notification through ntfy.sh...')
|
||||||
|
try:
|
||||||
|
url = config[entry]['url']
|
||||||
|
topic = config[entry]['topic']
|
||||||
|
except:
|
||||||
|
print('URL or TOPIC not provided!')
|
||||||
|
continue
|
||||||
|
headers = {'Icon': 'https://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Revanced-logo-round.svg/240px-Revanced-logo-round.svg.png',
|
||||||
|
'Title': encoded_title}
|
||||||
|
try:
|
||||||
|
req.post(f"{url}/{topic}", msg, headers=headers)
|
||||||
|
except Exception as e:
|
||||||
|
print('Failed!' + str(e))
|
||||||
|
|
||||||
|
case 'gotify':
|
||||||
|
print('Sending notification through Gotify...')
|
||||||
|
try:
|
||||||
|
url = config[entry]['url']
|
||||||
|
token = config[entry]['token']
|
||||||
|
except:
|
||||||
|
print('URL or TOKEN not provided!')
|
||||||
|
continue
|
||||||
|
data = {'Title': encoded_title, 'message': msg, 'priority': '5'}
|
||||||
|
try:
|
||||||
|
req.post(f"{url}/message?token={token}", data)
|
||||||
|
except Exception as e:
|
||||||
|
print('Failed!' + str(e))
|
||||||
|
|
||||||
|
case 'telegram':
|
||||||
|
print('Sending notification through Telegram...')
|
||||||
|
try:
|
||||||
|
chat = config[entry]['chat']
|
||||||
|
token = config[entry]['token']
|
||||||
|
except:
|
||||||
|
print('CHAT or TOKEN not provided!')
|
||||||
|
continue
|
||||||
|
cmd = f"./telegram.sh -t {token} -c {chat} -T {encoded_title} -M \"{msg}\""
|
||||||
|
try:
|
||||||
|
with subprocess.Popen(cmd, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout as output:
|
||||||
|
for line in output:
|
||||||
|
line_utf = line.decode('utf-8').strip('\n')
|
||||||
|
if line_utf:
|
||||||
|
print(line_utf)
|
||||||
|
except Exception as e:
|
||||||
|
clean_exit(f"Failed!\n{e}", appstate)
|
||||||
|
|
||||||
|
case _:
|
||||||
|
print('Don\'t know how to send notifications to ' + entry)
|
191
src/ReVancedBuilder/ReVancedBuilder.py
Executable file
191
src/ReVancedBuilder/ReVancedBuilder.py
Executable file
|
@ -0,0 +1,191 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# SPDX-FileCopyrightText: 2023 Sayantan Santra <sayantan.santra@ou.edu>
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import configparser as cp
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import requests as req
|
||||||
|
from packaging.version import Version
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from ReVancedBuilder.APKPure_dl import apkpure_best_match, apkpure_dl, get_apks
|
||||||
|
from ReVancedBuilder.JAVABuilder import build_apps
|
||||||
|
from ReVancedBuilder.Notifications import send_notif
|
||||||
|
from ReVancedBuilder.Cleanup import move_apps, clean_exit
|
||||||
|
|
||||||
|
# Update the ReVanced tools, if needed
|
||||||
|
def update_tools(appstate):
|
||||||
|
for item in ['revanced-cli', 'revanced-integrations', 'revanced-patches']:
|
||||||
|
*_, tool = filter(lambda x: x['repository'] == 'revanced/'+item, tools) # Get the last result
|
||||||
|
latest_ver = Version(tool['version'])
|
||||||
|
|
||||||
|
try:
|
||||||
|
present_ver = Version(appstate['present_vers'][item])
|
||||||
|
except KeyError:
|
||||||
|
present_ver = Version('0')
|
||||||
|
|
||||||
|
output_file = item+os.path.splitext(tool['name'])[1]
|
||||||
|
if flag == 'force' or not os.path.isfile(output_file) or present_ver < latest_ver:
|
||||||
|
appstate['up-to-date'] = False
|
||||||
|
print(f"{item} has an update ({str(present_ver)} -> {str(latest_ver)})")
|
||||||
|
if flag != 'checkonly':
|
||||||
|
print(f"Downloading {output_file}...")
|
||||||
|
res = req.get(tool['browser_download_url'], stream=True)
|
||||||
|
res.raise_for_status()
|
||||||
|
with open(output_file, 'wb') as f:
|
||||||
|
for chunk in res.iter_content(chunk_size=8192):
|
||||||
|
f.write(chunk)
|
||||||
|
appstate['present_vers'].update({item: str(latest_ver)})
|
||||||
|
print("Done!")
|
||||||
|
|
||||||
|
return appstate
|
||||||
|
|
||||||
|
# Update microG, if needed
|
||||||
|
def update_microg(appstate):
|
||||||
|
try:
|
||||||
|
data = req.get('https://api.github.com/repos/inotia00/VancedMicroG/releases/latest').json()['tag_name']
|
||||||
|
latest_ver = Version(data)
|
||||||
|
except req.exceptions.RequestException as e:
|
||||||
|
clean_exit(e, appstate)
|
||||||
|
|
||||||
|
try:
|
||||||
|
present_ver = Version(appstate['present_vers']['VancedMicroG'])
|
||||||
|
except KeyError:
|
||||||
|
present_ver = Version('0')
|
||||||
|
|
||||||
|
if flag == 'force' or not os.path.isfile('microg.apk') or present_ver < latest_ver:
|
||||||
|
appstate['up-to-date'] = False
|
||||||
|
print(f"Vanced microG has an update ({str(present_ver)} -> {str(latest_ver)})")
|
||||||
|
if flag != 'checkonly':
|
||||||
|
print(f"Downloading vanced-microg.apk...")
|
||||||
|
res = req.get('https://github.com/inotia00/VancedMicroG/releases/latest/download/microg.apk', stream=True)
|
||||||
|
res.raise_for_status()
|
||||||
|
with open('microg.apk', 'wb') as f:
|
||||||
|
for chunk in res.iter_content(chunk_size=8192):
|
||||||
|
f.write(chunk)
|
||||||
|
appstate['present_vers'].update({'VancedMicroG': str(latest_ver)})
|
||||||
|
print("Done!")
|
||||||
|
appstate['microg_updated'] = True
|
||||||
|
|
||||||
|
return appstate
|
||||||
|
|
||||||
|
# ------------------------------
|
||||||
|
# The main function starts here
|
||||||
|
# ------------------------------
|
||||||
|
|
||||||
|
# Create a dict for storing important data
|
||||||
|
appstate = {}
|
||||||
|
|
||||||
|
# Get a timestamp
|
||||||
|
time = datetime.now()
|
||||||
|
appstate['timestamp'] = time.strftime('%Y%m%d%H%M%S')
|
||||||
|
|
||||||
|
# Read arguments
|
||||||
|
try:
|
||||||
|
os.chdir(sys.argv[1])
|
||||||
|
except IndexError:
|
||||||
|
sys.exit('Please provide a working directory as argument!')
|
||||||
|
except FileNotFoundError:
|
||||||
|
sys.exit('Invalid working directory provided!')
|
||||||
|
|
||||||
|
# Try to make sure only one instance is running in a given working directory
|
||||||
|
try:
|
||||||
|
if os.path.exists('lockfile'):
|
||||||
|
raise FileExistsError
|
||||||
|
with open('tmplockfile', 'x') as f:
|
||||||
|
f.flush()
|
||||||
|
os.fsync(f.fileno())
|
||||||
|
os.replace('tmplockfile', 'lockfile')
|
||||||
|
except FileExistsError:
|
||||||
|
sys.exit('Another instance is already running in the same working directory!')
|
||||||
|
|
||||||
|
# Set up logging
|
||||||
|
try:
|
||||||
|
os.mkdir('logs')
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.addHandler(logging.FileHandler(f"logs/{appstate['timestamp']}.log", 'w'))
|
||||||
|
print = logger.info
|
||||||
|
appstate['logger'] = logger
|
||||||
|
|
||||||
|
# Get the flag
|
||||||
|
try:
|
||||||
|
flag = sys.argv[2]
|
||||||
|
except:
|
||||||
|
flag = None
|
||||||
|
|
||||||
|
if flag not in ['buildonly', 'checkonly', 'force', 'experimental', None]:
|
||||||
|
clean_exit(f"Unknown flag: {flag}", appstate)
|
||||||
|
|
||||||
|
appstate['flag'] = flag
|
||||||
|
appstate['microg_updated'] = False
|
||||||
|
|
||||||
|
print(f"Started building ReVanced apps at {time.strftime('%d %B, %Y %H:%M:%S')}")
|
||||||
|
print('----------------------------------------------------------------------')
|
||||||
|
|
||||||
|
# Read configs
|
||||||
|
try:
|
||||||
|
appstate['build_config']=cp.ConfigParser()
|
||||||
|
appstate['build_config'].read_file(open('build_config', 'r'))
|
||||||
|
except FileNotFoundError:
|
||||||
|
clean_exit('No build config provided, exiting. Please look at the GitHub page for more information:\n https://github.com/SinTan1729/ReVancedBuilder', appstate)
|
||||||
|
|
||||||
|
appstate['notification_config'] = cp.ConfigParser()
|
||||||
|
appstate['notification_config'].read('notification_config')
|
||||||
|
|
||||||
|
# Pull the latest information using the ReVanced API
|
||||||
|
try:
|
||||||
|
tools = req.get('https://releases.revanced.app/tools').json()['tools']
|
||||||
|
except req.exceptions.RequestException as e:
|
||||||
|
clean_exit(e, appstate)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open('versions.json', 'r') as f:
|
||||||
|
appstate['present_vers'] = json.load(f)
|
||||||
|
except:
|
||||||
|
# We'll treat empty as 0 later
|
||||||
|
appstate['present_vers'] = json.loads('{}')
|
||||||
|
|
||||||
|
appstate['up-to-date'] = True
|
||||||
|
# send_notif(appstate, error=False) # <,,,,,,,,<,,,,,,,,,,,,,
|
||||||
|
if flag != 'buildonly':
|
||||||
|
appstate = update_tools(appstate)
|
||||||
|
appstate = update_microg(appstate)
|
||||||
|
if not appstate['up-to-date'] or flag == 'force':
|
||||||
|
appstate = get_apks(appstate)
|
||||||
|
|
||||||
|
if (flag != 'checkonly' and not appstate['up-to-date']) or flag in ['force', 'buildonly']:
|
||||||
|
build_apps(appstate)
|
||||||
|
move_apps(appstate)
|
||||||
|
|
||||||
|
# Update version numbers in the versions.json file
|
||||||
|
if appstate['up-to-date'] and flag != 'buildonly':
|
||||||
|
print('There\'s nothing to do.')
|
||||||
|
elif flag != ['checkonly']:
|
||||||
|
send_notif(appstate)
|
||||||
|
try:
|
||||||
|
os.rename('versions.json', 'versions-old.json')
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if flag != 'buildonly':
|
||||||
|
with open('versions.json', 'w') as f:
|
||||||
|
json.dump(appstate['present_vers'], f, indent=4)
|
||||||
|
try:
|
||||||
|
subprocess.run(f"{appstate['build_config']['post_script']['file']} {timestamp}", shell=True)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Delete the lockfile
|
||||||
|
os.remove('lockfile')
|
||||||
|
|
||||||
|
sys.exit(0)
|
Loading…
Reference in a new issue