Загрузка данных


# -*- coding: utf-8 -*-
import sys
import time
import socket
import threading
import io
import struct

# Automatic check and installation of missing libraries
try:
    import mss
    from PIL import Image
    import pyautogui
except ImportError:
    print("[Demirast] Установка недостающих библиотек (mss, pillow, pyautogui)...")
    import subprocess
    subprocess.check_call([sys.executable, "-m", "pip", "install", "mss", "pillow", "pyautogui"])
    import mss
    from PIL import Image
    import pyautogui

pyautogui.FAILSAFE = False
pyautogui.PAUSE = 0.0  # Removing PyAutoGUI delay for fast streaming and inputs!

def main():
    print("=====================================================")
    print("       DEMIRAST MULTI-CONNECTION PC CLIENT v2.1      ")
    print("=====================================================")
    print("Выберите способ подключения:")
    print("1. Wi-Fi - Телефон как СЕРВЕР (Рекомендуется)")
    print("2. Wi-Fi - ПК как СЕРВЕР")
    print("3. USB Кабель (через ADB порт-форвардинг)")
    print("4. Bluetooth - Телефон как СЕРВЕР")
    print("5. Bluetooth - ПК как СЕРВЕР")
    print("=====================================================")
    
    choice = input("Введите номер (1-5): ").strip()
    if choice not in ["1", "2", "3", "4", "5"]:
        print("[Demirast] Неверный выбор.")
        input("\nНажмите Enter для выхода...")
        return

    state = {
        "delay": 1.0 / 60,
        "monitor_index": 0,
        "current_monitor": None
    }
    
    client_socket = None
    server_socket = None
    
    if choice == "1":
        ip = input("Введите IP-адрес телефона (отображается в приложении): ").strip()
        port_str = input("Введите порт (по умолчанию 12345): ").strip()
        port = int(port_str) if port_str else 12345
        print(f"\n[Demirast] Ожидание запуска Wi-Fi сервера на телефоне ({ip}:{port})...")
        print("Пожалуйста, запустите сервер на Android в приложении.")
        print("Для выхода нажмите Ctrl+C.")
        while True:
            try:
                client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                client_socket.settimeout(2.0)
                client_socket.connect((ip, port))
                client_socket.settimeout(None)
                print("[Demirast] Подключение к Wi-Fi серверу установлено!")
                break
            except (socket.timeout, ConnectionRefusedError, OSError):
                time.sleep(1.0)
            except KeyboardInterrupt:
                print("\n[Demirast] Подключение отменено пользователем.")
                return

    elif choice == "2":
        port_str = input("Введите порт для сервера на ПК (по умолчанию 12345): ").strip()
        port = int(port_str) if port_str else 12345
        try:
            print(f"[Demirast] Запуск TCP сервера на ПК на порту {port}...")
            server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            server_socket.bind(("0.0.0.0", port))
            server_socket.listen(1)
            try:
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.connect(("8.8.8.8", 80))
                pc_ip = s.getsockname()[0]
                s.close()
            except:
                pc_ip = "127.0.0.1"
            print(f"[Demirast] Сервер запущен. На телефоне введите IP: {pc_ip} и нажмите 'Подключиться'")
            client_socket, addr = server_socket.accept()
            print(f"[Demirast] Подключено мобильное устройство: {addr}")
        except Exception as e:
            print(f"[Demirast] Ошибка TCP сервера: {e}")
            input("\nНажмите Enter для выхода...")
            return

    elif choice == "3":
        port_str = input("Введите порт (по умолчанию 12345): ").strip()
        port = int(port_str) if port_str else 12345
        print("\n=== НАСТРОЙКА USB ПОДКЛЮЧЕНИЯ ===")
        print("1. Подключите телефон к ПК по USB кабелю.")
        print("2. Включите 'Отладка по USB' в настройках телефона.")
        print("3. Выполните на ПК в командной строке (CMD / PowerShell):")
        print(f"   adb forward tcp:{port} tcp:{port}")
        print("4. Нажмите 'Запустить Сервер на Android' в приложении телефона.")
        input("\nКогда выполните шаги, нажмите ENTER для начала ожидания подключения...")
        print(f"\n[Demirast] Ожидание запуска сервера на телефоне через порт USB (127.0.0.1:{port})...")
        print("Для выхода нажмите Ctrl+C.")
        while True:
            try:
                client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                client_socket.settimeout(2.0)
                client_socket.connect(("127.0.0.1", port))
                client_socket.settimeout(None)
                print("[Demirast] Подключение по USB кабелю установлено успешно!")
                break
            except (socket.timeout, ConnectionRefusedError, OSError):
                time.sleep(1.0)
            except KeyboardInterrupt:
                print("\n[Demirast] Подключение отменено пользователем.")
                return

    elif choice == "4":
        if not hasattr(socket, "AF_BLUETOOTH"):
            print("[Demirast] Ошибка: Ваша версия Python или ОС не поддерживает AF_BLUETOOTH напрямую.")
            print("Рекомендуется использовать Wi-Fi или USB-Кабель подключение (варианты 1, 2, 3)!")
            input("\nНажмите Enter для выхода...")
            return
        addr = input("Введите MAC-адрес телефона (например, AA:BB:CC:DD:EE:FF): ").strip()
        print(f"\n[Demirast] Ожидание запуска Bluetooth сервера на телефоне ({addr})...")
        print("Для выхода нажмите Ctrl+C.")
        while True:
            try:
                client_socket = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
                client_socket.connect((addr, 1))
                print("[Demirast] Успешно подключено по Bluetooth!")
                break
            except Exception:
                time.sleep(1.5)
            except KeyboardInterrupt:
                print("\n[Demirast] Подключение отменено пользователем.")
                return

    elif choice == "5":
        if not hasattr(socket, "AF_BLUETOOTH"):
            print("[Demirast] Ошибка: Ваша версия Python или ОС не поддерживает AF_BLUETOOTH напрямую.")
            print("Рекомендуется использовать Wi-Fi или USB-Кабель подключение (варианты 1, 2, 3)!")
            input("\nНажмите Enter для выхода...")
            return
        try:
            print("[Demirast] Запуск Bluetooth RFCOMM сервера на ПК...")
            server_socket = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
            server_socket.bind(("", 1))
            server_socket.listen(1)
            print("[Demirast] Сервер запущен. Нажмите в приложении 'Подключиться к ПК' (Клиент) через Bluetooth.")
            client_socket, client_info = server_socket.accept()
            print(f"[Demirast] Мобильный клиент подключился: {client_info}")
        except Exception as e:
            print(f"[Demirast] Ошибка Bluetooth сервера: {e}")
            input("\nНажмите Enter для выхода...")
            return

    # Background thread to receive touch input, mouse clicks, and configuration from Android
    def receive_inputs():
        try:
            while True:
                cmd = client_socket.recv(1)
                if not cmd:
                    break
                if cmd == b'M': # Move
                    coords = client_socket.recv(8)
                    if len(coords) == 8:
                        x_ratio, y_ratio = struct.unpack('!ff', coords)
                        mon = state["current_monitor"]
                        if mon:
                            monitor_left = mon.get("left", 0)
                            monitor_top = mon.get("top", 0)
                            monitor_width = mon.get("width", 1920)
                            monitor_height = mon.get("height", 1080)
                            target_x = monitor_left + int(x_ratio * monitor_width)
                            target_y = monitor_top + int(y_ratio * monitor_height)
                        else:
                            screen_w, screen_h = pyautogui.size()
                            target_x = int(x_ratio * screen_w)
                            target_y = int(y_ratio * screen_h)
                        pyautogui.moveTo(target_x, target_y)
                elif cmd == b'C': # Click
                    pyautogui.click()
                elif cmd == b'F': # Frame rate configuration
                    fps_data = client_socket.recv(4)
                    if len(fps_data) == 4:
                        fps = struct.unpack('!i', fps_data)[0]
                        if 1 <= fps <= 144:
                            state["delay"] = 1.0 / fps
                            print(f"[Demirast] Частота трансляции изменена на {fps} Гц (интервал: {state['delay']:.4f} с)")
                elif cmd == b'D': # Monitor Index configuration
                    mon_index_bytes = client_socket.recv(1)
                    if len(mon_index_bytes) == 1:
                        state["monitor_index"] = int(mon_index_bytes[0])
                        print(f"[Demirast] Выбранный экран изменен на: Экран {state['monitor_index'] + 1}")
        except Exception as e:
            print(f"[Demirast] Поток ввода остановлен: {e}")

    threading.Thread(target=receive_inputs, daemon=True).start()

    # Screen frames streaming loop
    print("[Demirast] Запуск трансляции экрана...")
    try:
        with mss.MSS() as sct:
            while True:
                # Dynamic monitor selection
                monitors = sct.monitors
                idx = state["monitor_index"] + 1 # individual monitors start at 1
                if idx < len(monitors):
                    monitor = monitors[idx]
                else:
                    monitor = monitors[-1]
                
                state["current_monitor"] = monitor
                
                # Capture frame
                sct_img = sct.grab(monitor)
                img = Image.frombytes("RGB", sct_img.size, sct_img.bgra, "raw", "BGRX")
                
                # Compress screen size if it's too large
                if img.width > 1280:
                    img = img.resize((1280, int(1280 * img.height / img.width)), Image.Resampling.BILINEAR)
                
                # Compress image into highly optimized JPEG bytes
                out_bytes = io.BytesIO()
                img.save(out_bytes, format="JPEG", quality=55)
                jpeg_data = out_bytes.getvalue()
                
                # Send length header (4 bytes) + image bytes
                length_prefix = len(jpeg_data).to_bytes(4, byteorder='big')
                client_socket.sendall(length_prefix + jpeg_data)
                
                # Sleep to match frame rate
                time.sleep(state["delay"])
    except Exception as e:
        print(f"[Demirast] Трансляция завершена: {e}")
    finally:
        try:
            client_socket.close()
        except:
            pass
        if server_socket:
            try:
                server_socket.close()
            except:
                pass
        print("[Demirast] Сессия завершена. Спасибо за использование!")
        input("\nНажмите Enter для выхода...")

if __name__ == "__main__":
    main()