From 66d1e8f627956052aa66dd771183827b66603fb1 Mon Sep 17 00:00:00 2001 From: tretrauit Date: Fri, 4 Feb 2022 22:34:21 +0700 Subject: [PATCH] Refactor the script Add auto start adb (so you don't have to go developer settings anymore), Termux android notifications, and lastly you don't have to rerun script everytime you change IP anymore, the script will automatically reconnect for you. --- Apps/ws-scrcpy/README.md | 37 ++-- Apps/ws-scrcpy/ws-scrcpy-launcher.py | 258 +++++++++++++++++++-------- 2 files changed, 210 insertions(+), 85 deletions(-) diff --git a/Apps/ws-scrcpy/README.md b/Apps/ws-scrcpy/README.md index 2eb75b1..7168c57 100644 --- a/Apps/ws-scrcpy/README.md +++ b/Apps/ws-scrcpy/README.md @@ -1,18 +1,31 @@ # ws-scrcpy scripts + ## ws-scrcpy-launcher.py for Termux -### YOU NEED TO HAVE WS-SCRCPY INSTALLED, ALONG WITH ROOT ACCESS AND DEPENDENCIES -+ **YOU ALSO NEED TO OPEN ADB WIRELESS IN DEVELOPER SETTINGS FOR THIS TO WORK** -+ root maybe not needed but **i haven't tested it yet** so do it with your own risk (the script won't fail even if it can't have root access btw) -+ dependencies: `root-repo` `tsu` `moreutils` `build-essential` `nodejs` `python3` `android-tools` `git` -+ you also need to downgrade npm to version 6 to fix npm on termux: `npm install -g npm@6` -> ignore the vulnerability please if you care about it please don't use this script -+ download ws-scrcpy-launcher.py: + +### Notes + ++ **YOU NEED TO HAVE WS-SCRCPY INSTALLED, ALONG WITH ROOT ACCESS AND DEPENDENCIES** ++ ~~YOU ALSO NEED TO OPEN ADB WIRELESS IN DEVELOPER SETTINGS FOR THIS TO WORK~~ (The script can use `su` to start ADB wireless now) ++ Rootless mode is available, although it **will not** work in most cases. ++ Dependencies: `root-repo` `tsu` `moreutils` `build-essential` `nodejs` `python3` `android-tools` `git` `termux-api` + +> Or execute `pkg install root-repo tsu moreutils build-essential nodejs python3 android-tools git termux-api` + ++ You also need to downgrade npm to version 6 to fix Termux problem: `npm install -g npm@6` + +> Please ignore the vulnerability message, if you care about it then please don't use this script. + ++ Download ws-scrcpy-launcher.py: + ```bash curl -OL https://gitlab.com/tretrauit/scripts/-/raw/main/Apps/ws-scrcpy/ws-scrcpy-launcher.py chmod +x ws-scrcpy-launcher.py ``` -> after this use `./ws-scrcpy-launcher.py` to launch ws-scrcpy with scrcpy server for local device. -+ full script for lazy people (including install ws-scrcpy): + +> Execute `./ws-scrcpy-launcher.py` to launch ws-scrcpy with scrcpy server for local device. + ++ Full script for lazy people (including install ws-scrcpy steps): + ```bash pkg update pkg install root-repo moreutils build-essential nodejs python3 android-tools git @@ -25,5 +38,7 @@ npm install curl -OL https://gitlab.com/tretrauit/scripts/-/raw/main/Apps/ws-scrcpy/ws-scrcpy-launcher.py chmod +x ws-scrcpy-launcher.py ``` -> after this you need to use `adb pair` to pair termux with your device adb server, then you can launch ws-scrcpy as explained above. -#### After this, the script will tell you to wait for ws-scrcpy to start, and when it starts it'll show the started message with the ip address and the port to access using browsers + ++ The script will tell you to wait for ws-scrcpy to start, and when it starts it'll show the started message with the ip address and the port to access using browsers + +> You need to use `adb pair` to pair termux with your device adb server, then you can launch ws-scrcpy as explained above. diff --git a/Apps/ws-scrcpy/ws-scrcpy-launcher.py b/Apps/ws-scrcpy/ws-scrcpy-launcher.py index 64a8611..9518a98 100644 --- a/Apps/ws-scrcpy/ws-scrcpy-launcher.py +++ b/Apps/ws-scrcpy/ws-scrcpy-launcher.py @@ -1,106 +1,216 @@ #!/usr/bin/python3 -# poorly written for quick and dirty use +# Rewrite import subprocess import os import time import threading +import random +from pathlib import Path +from shutil import which + +APP_UUID="a6db95c2-27db-4b88-a687-c8107d1bc9d6" + +def Popen(args: list | str, root=False, shell=False, cwd=Path.cwd()): + if root: + if shell: + args = "sudo " + args + else: + args = ["sudo"] + args + result = subprocess.Popen(args, shell=shell, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + return result + +def run(args: list | str, root=False, shell=False, cwd=Path.cwd()): + if root: + if shell: + args = "sudo " + args + else: + args = ["sudo"] + args + result = subprocess.run(args, shell=shell, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + result.check_returncode() + return result + +def show_toast(message): + run(["termux-toast", f'{message}']) + +def show_notification(title, message=None, vibration: list[int] | str=None, sound=False, ongoing=True): + if not which("termux-notification"): + raise FileNotFoundError("termux-notification not found, please install termux-api package and Termux:API app.") + args = ["termux-notification", "-i", APP_UUID, "--priority", "max", "-t", f'{title}'] + if message: + args += ["-c", f'{message}'] + if vibration: + args += ["--vibrate", ",".join(str(x) for x in vibration) if isinstance(vibration, list) else vibration] + if sound: + args += ["--sound"] + if ongoing: + args += ["--ongoing"] + run(args) + +def get_device_private_ip(): + return subprocess.check_output(['ifdata', '-pa', 'wlan0']).decode("utf-8").strip() + +def get_port_from_process_name(name: str, state: str="LISTEN"): + lsof_output = subprocess.check_output(["sudo", "lsof", "-i", "-P", "-n"]).decode("utf-8") + for line in lsof_output.split("\n"): + if name in line and f"({state})" in line: + line = ' '.join(line.split()) + return int(line.strip().split(" ")[8].split(":")[1]) + return None + +def get_pid_from_port(port: int, state: str="LISTEN"): + lsof_output = subprocess.check_output(["sudo", "lsof", "-i", "-P", "-n"]).decode("utf-8") + for line in lsof_output.split("\n"): + if str(port) in line and f"({state})" in line: + line = ' '.join(line.split()) + return line.strip().split(" ")[1] + return None + +def start_adbd(): + free_port = random.randint(0, 65535) + while get_pid_from_port(free_port): + free_port = random.randint(0, 65535) + print("Starting adbd on port " + str(free_port)) + show_notification("Starting adbd", "Starting adbd on port " + str(free_port)) + try: + run(["setprop", "service.adb.tcp.port", str(free_port)], root=True) + except subprocess.CalledProcessError: + print("Failed to set adb port") + show_notification("Failed to set adb port to " + str(free_port), "You'll need to start adb manually through Developer Settings") + return + try: + run(["stop", "adbd"], root=True) + except subprocess.CalledProcessError: + print("Failed to stop adbd, maybe adbd was not running?") + try: + run(["start", "adbd"], root=True) + except subprocess.CalledProcessError: + print("Failed to start adbd") + show_notification("Failed to start adbd", "You'll need to start adb manually through Developer Settings") + return + return free_port + +def connect_adb(ip, port): + run(["adb", "connect", f"{ip}:{port}"]) def main(): - print("ws-scrcpy launcher for termux (YOU NEED TO HAVE WS-SCRCPY INSTALLED IN ~/ws-scrcpy)") - print("THIS SCRIPT REQUIRES ROOT AND TSU, THANK YOU :(") - print("checking for adb port...") - adb_lsof_output = subprocess.check_output(["sudo", "lsof", "-i", "-P", "-n"]).decode("utf-8") + print("ws-scrcpy launcher for Termux") + print("Checking for ws-scrcpy...") + if not Path("./ws-scrcpy").exists(): + print("ws-scrcpy not found, please install ws-scrcpy.") + return + print("Checking for current adb port...") + show_notification("Checking for current adb port...") adb_port = None try: - for line in adb_lsof_output.split("\n"): - if "adbd" in line and "(LISTEN)" in line: - line = ' '.join(line.split()) - print(line.strip()) - adb_port = line.strip().split(" ")[8].split(":")[1] - break + adb_port = get_port_from_process_name("adbd") except: - print("error occured while getting adb port.") pass - if adb_port == None: - adb_port = input("couldn't find adb port please type manually:") + if adb_port is None: + print("Failed to get adb port, starting new adbd...") + adb_port = start_adbd() + if adb_port is None: + print("Failed to start adbd...") + return - device_ip = subprocess.check_output(['ifdata', '-pa', 'wlan0']).decode("utf-8").strip() + device_ip = get_device_private_ip() - print("adb port:", adb_port) - print("ip:", device_ip) - print("connecting to device through adb...") - connect_result = subprocess.call(["adb", "connect", f"{device_ip}:{adb_port}"]) - if connect_result != 0: - print("connection failed.") - exit() - print("changing directory to home") - os.chdir("/data/data/com.termux/files/home/") - print("starting ws-scrcpy server...") - ws_scrcpy = subprocess.Popen(["npm", "start"], cwd="./ws-scrcpy", stdout=subprocess.PIPE, stderr=subprocess.PIPE) + print("Adb server port:", adb_port) + print("Device IP:", device_ip) + print("Connecting to device through adb...") + show_notification("Connecting to device through adb...") + try: + connect_adb(device_ip, adb_port) + except subprocess.CalledProcessError: + print("Failed to connect to device through adb") + show_notification("Failed to connect to device through adb", "You'll need to connect manually in Termux.") + return + + print("Starting ws-scrcpy server...") + show_notification("Starting ws-scrcpy server...", "You may need to wait for 5 minutes for ws-scrcpy to start.") + ws_scrcpy = Popen(["npm", "start"], cwd="./ws-scrcpy") def print_ws_scrcpy(): for line in ws_scrcpy.stdout: - if line.decode("utf-8").strip() is None: + if line.decode("utf-8").strip() == None: continue if "Listening on:" in line.decode("utf-8").strip(): - device_ip = subprocess.check_output(['ifdata', '-pa', 'wlan0']).decode("utf-8").strip() + device_ip = get_device_private_ip() print("=========================================") - print(f"ws-scrcpy STARTED ON {device_ip}:8000") + print(f"ws-scrcpy started on {device_ip}:8000") print("=========================================") + show_toast(f"ws-scrcpy: {device_ip}:8000") + show_notification(f"ws-scrcpy: {device_ip}:8000", f"Access {device_ip}:8000 in browser to control the device.", [2000, 1000, 500], True) print("[ws-scrcpy]:", line.decode("utf-8").strip()) ws_scrcpy_thread = threading.Thread(target=print_ws_scrcpy) ws_scrcpy_thread.daemon = True ws_scrcpy_thread.start() - print("starting scrcpy server on local device...") - scrcpy = subprocess.Popen("adb shell su -c 'CLASSPATH=/data/data/com.termux/files/home/ws-scrcpy/vendor/Genymobile/scrcpy/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 1.19-ws2 web ERROR 8886'", - shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - def print_scrcpy(): - for line in scrcpy.stdout: - print("[scrcpy patch]:", line.decode("utf-8").strip()) - scrcpy_thread = threading.Thread(target=print_scrcpy) - scrcpy_thread.daemon = True - scrcpy_thread.start() + # print("starting scrcpy server on local device...") + # scrcpy = subprocess.Popen("adb shell su -c 'CLASSPATH=/data/data/com.termux/files/home/ws-scrcpy/vendor/Genymobile/scrcpy/scrcpy-server.jar app_process / com.genymobile.scrcpy.Server 1.19-ws2 web ERROR 8886'", + # shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + # def print_scrcpy(): + # for line in scrcpy.stdout: + # print("[scrcpy patch]:", line.decode("utf-8").strip()) + # scrcpy_thread = threading.Thread(target=print_scrcpy) + # scrcpy_thread.daemon = True + # scrcpy_thread.start() print("PLEASE WAIT UNTIL WS-SCRCPY FULLY STARTS (ABOUT 5 MINS), IT TAKES A WHILE TO START THE SERVER.") try: while True: time.sleep(5) - curr_ip = subprocess.check_output(['ifdata', '-pa', 'wlan0']).decode("utf-8").strip() + curr_ip = get_device_private_ip() if curr_ip != device_ip: - print("!!!DEVICE IP ADDRESS CHANGED, PLEASE RESTART SERVER MANUALLY!!!") - print("!!!SCRCPY WILL NOT WORK UNTIL YOU RESTART THE SERVER!!!") - except: # lazy - if ws_scrcpy.poll() == None: - print("stopping ws-scrcpy server...") - ws_scrcpy.terminate() - try: - # if this returns, the process completed - ws_scrcpy.wait(timeout=15) - except subprocess.TimeoutExpired: - print("ws_scrcpy doesn't exit after 15 seconds, killing process...") - ws_scrcpy.kill() - - print("stopping scrcpy server...") - if scrcpy.poll() == None: - scrcpy.terminate() - try: - # if this returns, the process completed - scrcpy.wait(timeout=15) - except subprocess.TimeoutExpired: - print("scrcpy doesn't exit after 15 seconds, killing process...") - scrcpy.kill() - - # kill old scrcpy-server to ensure we can start a new one after this. - try: - lsof_output = subprocess.check_output(["sudo", "lsof", "-i", "-P", "-n"]).decode("utf-8") - for line in lsof_output.split("\n"): - if "8886" in line and "(LISTEN)" in line: - line = ' '.join(line.split()) - print(line.strip()) - pid = line.strip().split(" ")[1] - subprocess.call(["sudo", "kill", "-9", pid]) + print("Device IP changed, reconnecting adb...") + show_notification("Reconnecting adb...", "This may take a while, take a cup of coffee.") + try: + connect_adb(curr_ip, adb_port) + except subprocess.CalledProcessError: + print("Failed to connect to device through adb") + show_notification("Failed to connect to device through adb", "ws-scrcpy will be stopped.") break - except: - print("failed to stop scrcpy server") - print("stopped.") + device_ip = curr_ip + print("=========================================") + print(f"ws-scrcpy started on {device_ip}:8000") + print("=========================================") + show_toast(f"ws-scrcpy: {device_ip}:8000") + show_notification(f"ws-scrcpy: {device_ip}:8000", f"Access {device_ip}:8000 in browser to control the device.", [2000, 1000, 500], True) + + except: + pass + + if ws_scrcpy.poll() == None: + print("Stopping ws-scrcpy server...") + show_notification("Stopping ws-scrcpy server...") + ws_scrcpy.terminate() + try: + # if this returns, the process completed + ws_scrcpy.wait(timeout=15) + except subprocess.TimeoutExpired: + print("ws_scrcpy doesn't exit after 15 seconds, killing process...") + ws_scrcpy.kill() + + # print("stopping scrcpy server...") + # if scrcpy.poll() == None: + # scrcpy.terminate() + # try: + # # if this returns, the process completed + # scrcpy.wait(timeout=15) + # except subprocess.TimeoutExpired: + # print("scrcpy doesn't exit after 15 seconds, killing process...") + # scrcpy.kill() + + # # kill old scrcpy-server to ensure we can start a new one after this. + # try: + # lsof_output = subprocess.check_output(["sudo", "lsof", "-i", "-P", "-n"]).decode("utf-8") + # for line in lsof_output.split("\n"): + # if "8886" in line and "(LISTEN)" in line: + # line = ' '.join(line.split()) + # print(line.strip()) + # pid = line.strip().split(" ")[1] + # subprocess.call(["sudo", "kill", "-9", pid]) + # break + # except: + # print("failed to stop scrcpy server") + print("ws-scrcpy has been stopped.") + show_notification("ws-scrcpy has been stopped.", ongoing=False) if __name__ == '__main__': main()