2022-02-02 07:01:23 +00:00
|
|
|
#!/usr/bin/python3
|
2022-02-04 15:34:21 +00:00
|
|
|
# Rewrite
|
2022-02-05 07:54:29 +00:00
|
|
|
import sys
|
2022-02-02 07:01:23 +00:00
|
|
|
import subprocess
|
2022-02-05 07:29:47 +00:00
|
|
|
import json
|
2022-02-02 07:01:23 +00:00
|
|
|
import time
|
|
|
|
import threading
|
2022-02-04 15:34:21 +00:00
|
|
|
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}'])
|
|
|
|
|
2022-02-05 07:54:29 +00:00
|
|
|
def show_notification(title, message=None, vibration: list[int] | str=None, sound=False, ongoing=True, buttons: list[dict] = None):
|
2022-02-04 15:34:21 +00:00
|
|
|
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"]
|
2022-02-05 07:54:29 +00:00
|
|
|
if buttons:
|
|
|
|
count = 1
|
|
|
|
for btn in buttons:
|
|
|
|
args += [f"--button{count}", f'{btn["label"]}', f"--button{count}-action", f'{btn["action"]}']
|
|
|
|
if count >= 3:
|
|
|
|
break
|
|
|
|
count += 1
|
|
|
|
|
2022-02-04 15:34:21 +00:00
|
|
|
run(args)
|
|
|
|
|
2022-02-04 16:02:27 +00:00
|
|
|
def get_wifi():
|
|
|
|
if not which("termux-wifi-connectioninfo"):
|
|
|
|
raise FileNotFoundError("termux-wifi-connectioninfo not found, please install termux-api package and Termux:API app.")
|
2022-02-05 07:26:49 +00:00
|
|
|
return json.loads(subprocess.check_output(["termux-wifi-connectioninfo"]).decode("utf-8"))
|
2022-02-04 16:02:27 +00:00
|
|
|
|
|
|
|
def get_wifi_name():
|
|
|
|
return get_wifi()["ssid"]
|
|
|
|
|
2022-02-04 15:34:21 +00:00
|
|
|
def get_device_private_ip():
|
2022-02-05 07:54:29 +00:00
|
|
|
# We don't use info from get_wifi() because Termux:API may not work if user doesn't grant it all permissions.
|
2022-02-05 07:28:31 +00:00
|
|
|
return subprocess.check_output(['ifdata', '-pa', 'wlan0']).decode("utf-8").strip()
|
2022-02-04 15:34:21 +00:00
|
|
|
|
|
|
|
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")
|
2022-02-05 07:54:29 +00:00
|
|
|
show_notification("Failed to set adb port to " + str(free_port), "You'll need to start adb manually through Developer Settings", ongoing=False)
|
2022-02-04 15:34:21 +00:00
|
|
|
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")
|
2022-02-05 07:54:29 +00:00
|
|
|
show_notification("Failed to start adbd", "You'll need to start adb manually through Developer Settings", ongoing=False)
|
2022-02-04 15:34:21 +00:00
|
|
|
return
|
|
|
|
return free_port
|
|
|
|
|
|
|
|
def connect_adb(ip, port):
|
|
|
|
run(["adb", "connect", f"{ip}:{port}"])
|
2022-02-02 07:01:23 +00:00
|
|
|
|
|
|
|
def main():
|
2022-02-04 15:34:21 +00:00
|
|
|
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...")
|
2022-02-02 07:01:23 +00:00
|
|
|
adb_port = None
|
|
|
|
try:
|
2022-02-04 15:34:21 +00:00
|
|
|
adb_port = get_port_from_process_name("adbd")
|
2022-02-02 07:01:23 +00:00
|
|
|
except:
|
|
|
|
pass
|
2022-02-04 15:34:21 +00:00
|
|
|
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 = get_device_private_ip()
|
2022-02-04 16:10:35 +00:00
|
|
|
wifi_ssid = get_wifi_name()
|
2022-02-04 15:34:21 +00:00
|
|
|
|
|
|
|
print("Adb server port:", adb_port)
|
|
|
|
print("Device IP:", device_ip)
|
|
|
|
print("Connecting to device through adb...")
|
2022-02-05 07:54:29 +00:00
|
|
|
show_notification("Connecting to device through adb...", "This may take a while...")
|
2022-02-04 15:34:21 +00:00
|
|
|
try:
|
|
|
|
connect_adb(device_ip, adb_port)
|
|
|
|
except subprocess.CalledProcessError:
|
|
|
|
print("Failed to connect to device through adb")
|
2022-02-05 07:54:29 +00:00
|
|
|
show_notification("Failed to connect to device through adb", "You'll need to connect manually in Termux.", ongoing=False)
|
2022-02-04 15:34:21 +00:00
|
|
|
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")
|
2022-02-02 07:01:23 +00:00
|
|
|
def print_ws_scrcpy():
|
|
|
|
for line in ws_scrcpy.stdout:
|
2022-02-04 15:34:21 +00:00
|
|
|
if line.decode("utf-8").strip() == None:
|
2022-02-02 07:01:23 +00:00
|
|
|
continue
|
|
|
|
if "Listening on:" in line.decode("utf-8").strip():
|
2022-02-04 15:34:21 +00:00
|
|
|
device_ip = get_device_private_ip()
|
2022-02-04 16:10:35 +00:00
|
|
|
wifi_ssid = get_wifi_name()
|
2022-02-02 07:01:23 +00:00
|
|
|
print("=========================================")
|
2022-02-04 16:10:35 +00:00
|
|
|
print(f"ws-scrcpy started on {device_ip}:8000 on {wifi_ssid}")
|
2022-02-02 07:01:23 +00:00
|
|
|
print("=========================================")
|
2022-02-04 16:10:35 +00:00
|
|
|
show_toast(f"ws-scrcpy: {device_ip}:8000 on {wifi_ssid}")
|
2022-02-05 07:54:29 +00:00
|
|
|
show_notification(f"ws-scrcpy: {device_ip}:8000",
|
|
|
|
f"Wifi: {wifi_ssid}, access {device_ip}:8000 in browser to control the device.",
|
|
|
|
[2000, 1000, 500], True,
|
|
|
|
buttons = [
|
|
|
|
{
|
|
|
|
"label": "Open in browser",
|
|
|
|
"action": f"termux-open-url http://{device_ip}:8000"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"label": "Stop",
|
|
|
|
"action": f"kill -15 {ws_scrcpy.pid}"
|
|
|
|
}
|
|
|
|
])
|
2022-02-02 07:01:23 +00:00
|
|
|
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()
|
2022-02-05 07:29:47 +00:00
|
|
|
|
2022-02-05 07:28:31 +00:00
|
|
|
print("PLEASE WAIT UNTIL WS-SCRCPY FULLY STARTS (ABOUT 5 MINS), IT TAKES A WHILE TO START THE SERVER.")
|
2022-02-02 07:01:23 +00:00
|
|
|
try:
|
2022-02-05 07:54:29 +00:00
|
|
|
while True and ws_scrcpy.poll() is None:
|
2022-02-05 07:26:49 +00:00
|
|
|
time.sleep(2.5)
|
2022-02-04 15:34:21 +00:00
|
|
|
curr_ip = get_device_private_ip()
|
2022-02-05 07:26:49 +00:00
|
|
|
# print("ip:", curr_ip)
|
2022-02-04 16:10:35 +00:00
|
|
|
curr_ssid = get_wifi_name()
|
2022-02-05 07:26:49 +00:00
|
|
|
# print("ssid:", curr_ssid)
|
2022-02-02 07:01:23 +00:00
|
|
|
if curr_ip != device_ip:
|
2022-02-04 15:34:21 +00:00
|
|
|
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.")
|
2022-02-02 07:01:23 +00:00
|
|
|
break
|
2022-02-04 15:34:21 +00:00
|
|
|
device_ip = curr_ip
|
|
|
|
print("=========================================")
|
2022-02-05 07:26:49 +00:00
|
|
|
print(f"ws-scrcpy IP changed to {device_ip}:8000 on {wifi_ssid}")
|
2022-02-04 15:34:21 +00:00
|
|
|
print("=========================================")
|
2022-02-04 16:10:35 +00:00
|
|
|
show_toast(f"ws-scrcpy: {device_ip}:8000 on {wifi_ssid}")
|
2022-02-05 07:54:29 +00:00
|
|
|
show_notification(f"ws-scrcpy: {device_ip}:8000",
|
|
|
|
f"Wifi: {wifi_ssid}, access {device_ip}:8000 in browser to control the device.",
|
|
|
|
[2000, 1000, 500], True,
|
|
|
|
buttons = [
|
|
|
|
{
|
|
|
|
"label": "Open in browser",
|
|
|
|
"action": f"termux-open-url http://{device_ip}:8000"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"label": "Stop",
|
|
|
|
"action": f"kill -15 {ws_scrcpy.pid}"
|
|
|
|
}
|
|
|
|
])
|
2022-02-05 07:26:49 +00:00
|
|
|
if curr_ssid != wifi_ssid and curr_ssid not in "<unknown ssid>": # Do not set current ssid to unknown ssid.
|
2022-02-04 16:10:35 +00:00
|
|
|
print("Wifi ssid changed, changing ssid in notification...")
|
|
|
|
wifi_ssid = curr_ssid
|
|
|
|
print("=========================================")
|
2022-02-05 07:26:49 +00:00
|
|
|
print(f"ws-scrcpy IP changed to {device_ip}:8000 on {wifi_ssid}")
|
2022-02-04 16:10:35 +00:00
|
|
|
print("=========================================")
|
|
|
|
show_toast(f"ws-scrcpy: {device_ip}:8000 on {wifi_ssid}")
|
2022-02-05 07:54:29 +00:00
|
|
|
show_notification(f"ws-scrcpy: {device_ip}:8000",
|
|
|
|
f"Wifi: {wifi_ssid}, access {device_ip}:8000 in browser to control the device.",
|
|
|
|
buttons = [
|
|
|
|
{
|
|
|
|
"label": "Open in browser",
|
|
|
|
"action": f"termux-open-url http://{device_ip}:8000"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"label": "Stop",
|
|
|
|
"action": f"kill -15 {ws_scrcpy.pid}"
|
|
|
|
}
|
|
|
|
])
|
2022-02-04 15:34:21 +00:00
|
|
|
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()
|
2022-02-05 07:28:31 +00:00
|
|
|
|
2022-02-04 15:34:21 +00:00
|
|
|
print("ws-scrcpy has been stopped.")
|
2022-02-05 07:54:29 +00:00
|
|
|
show_notification("ws-scrcpy has been stopped.", "You may start again by clicking the restart button.", buttons=[
|
|
|
|
{
|
|
|
|
"label": "Restart",
|
|
|
|
"action": f"{sys.executable} {__file__}",
|
|
|
|
}
|
|
|
|
], ongoing=False)
|
2022-02-02 07:01:23 +00:00
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|