ytptube/app/local.py
2026-06-14 18:35:03 +03:00

152 lines
4.1 KiB
Python

#!/usr/bin/env python3
import argparse
import http.client
import os
import pathlib
import socket
import sys
import time
import webbrowser
from urllib.parse import urlsplit
if __name__ == "__main__":
from multiprocessing import freeze_support
freeze_support()
import dotenv
os.environ["PYTHONUTF8"] = "1"
sys.path.insert(0, os.path.join(getattr(sys, "_MEIPASS", os.path.abspath(os.path.dirname(__file__))), "app"))
APP_NAME = "YTPTube"
APP_ROOT = str((pathlib.Path(__file__).parent / "..").resolve())
if APP_ROOT not in sys.path:
sys.path.insert(0, APP_ROOT)
import platformdirs
def set_env():
dct = {}
if not os.getenv("YTP_CONFIG_PATH"):
dct["YTP_CONFIG_PATH"] = platformdirs.user_config_dir(APP_NAME.lower(), "arabcoders", ensure_exists=True)
if not os.getenv("YTP_TEMP_PATH"):
dct["YTP_TEMP_PATH"] = platformdirs.user_cache_dir(APP_NAME.lower(), "arabcoders", ensure_exists=True)
if not os.getenv("YTP_DOWNLOAD_PATH"):
dct["YTP_DOWNLOAD_PATH"] = platformdirs.user_downloads_dir()
if os.getenv("YTP_ACCESS_LOG", None) is None:
dct["YTP_ACCESS_LOG"] = "false"
if not os.getenv("YTP_HOST"):
dct["YTP_HOST"] = "127.0.0.1"
if dct:
os.environ.update(dct)
def open_browser_when_ready(url: str, timeout: float = 5.0) -> None:
import threading
def wait_then_open():
parsed = urlsplit(url)
path = parsed.path or "/"
deadline = time.time() + timeout
while time.time() < deadline:
try:
conn = http.client.HTTPConnection(parsed.hostname or "127.0.0.1", parsed.port or 80, timeout=1)
try:
conn.request("GET", path)
response = conn.getresponse()
if response.status == 200:
os.environ.pop("LD_LIBRARY_PATH", None)
webbrowser.open_new(url)
return
finally:
conn.close()
except Exception:
time.sleep(0.5)
sys.stderr.write(
f"Failed to open browser automatically within {timeout} seconds. Please open {url} manually.\n"
)
threading.Thread(target=wait_then_open, daemon=True).start()
def app_start(host: str, port: int) -> None:
import asyncio
from aiohttp.web_runner import GracefulExit
from app.main import Main
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
Main(is_native=True).start(host, port)
except GracefulExit:
sys.exit(0)
def update_env_file(env_file: pathlib.Path, port: int) -> None:
lines = []
if env_file.exists():
with env_file.open("r", encoding="utf-8") as f:
lines = f.readlines()
port_line = f"YTP_PORT={port}\n"
for i, line in enumerate(lines):
if line.startswith("YTP_PORT="):
lines[i] = port_line
break
else:
lines.append(port_line)
with env_file.open("w", encoding="utf-8") as f:
f.writelines(lines)
def main():
parser = argparse.ArgumentParser(description="Start YTPTube server", allow_abbrev=False)
parser.add_argument("--no-browser", action="store_true", help="Do not open browser on start")
args, _ = parser.parse_known_args()
set_env()
env_file: pathlib.Path = pathlib.Path(os.getenv("YTP_CONFIG_PATH", "")) / ".env"
port = None
if env_file.exists():
dotenv.load_dotenv(env_file)
port = os.getenv("YTP_PORT")
host = os.getenv("YTP_HOST", "127.0.0.1")
if not port or 8081 == int(port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((host, 0))
port = s.getsockname()[1]
update_env_file(env_file, port)
url = f"http://{host}:{port}"
env_no_browser: bool = os.getenv("YTP_NO_BROWSER", "false").lower() in ("1", "true", "yes")
if not args.no_browser and not env_no_browser:
open_browser_when_ready(url)
app_start(host, int(port))
if __name__ == "__main__":
main()