import { Adb, AdbDaemonTransport, encodeUtf8 } from '@yume-chan/adb'; import AdbWebCredentialStore from '@yume-chan/adb-credential-web'; import { AdbDaemonWebUsbDeviceManager, AdbDaemonWebUsbDeviceObserver, type AdbDaemonWebUsbDevice } from '@yume-chan/adb-daemon-webusb'; import { AdbInstance } from '../../../routes/state.svelte'; import { deviceCredentialManager } from './deviceCredManager'; import { Consumable, MaybeConsumable, ReadableStream } from '@yume-chan/stream-extra'; import { AdbScrcpyClient } from '@yume-chan/adb-scrcpy'; export async function connnectViaWebUSB() { const device = await AdbDaemonWebUsbDeviceManager.BROWSER?.requestDevice(); console.log('usb ok', globalThis.navigator.usb); if (device) { console.log('connect ', device.name); try { const credentialStore = new AdbWebCredentialStore(); const connection = await device.connect(); const transport = await AdbDaemonTransport.authenticate({ connection: connection, serial: device.serial, credentialStore: credentialStore }); const adb = new Adb(transport); saveAdbInstance(adb); // save device info await deviceCredentialManager.saveDeviceInfo(device); } catch (e: any) { console.error('error on connect', e); throw new Error(e.toString()); } } } export async function connectDeviceByCred( device: AdbDaemonWebUsbDevice, credStore: AdbWebCredentialStore ) { try { const connection = await device.connect(); const transport = await AdbDaemonTransport.authenticate({ connection: connection, serial: device.serial, credentialStore: credStore }); const adb = new Adb(transport); saveAdbInstance(adb); return true; } catch (error) { throw error; } } export function saveAdbInstance(adb: Adb | undefined) { AdbInstance.instance = adb; } export function getAdbInstance() { return AdbInstance.instance; } export async function executeCmd(command: string) { let instance = getAdbInstance(); if (!instance) { console.error('instance not found'); return {}; } try { if (instance?.subprocess.shellProtocol?.isSupported) { const result = await instance.subprocess.shellProtocol.spawnWaitText(command); return { output: result.stdout, error: result.stderr, exitCode: result.exitCode }; } else { const process = await instance.subprocess.noneProtocol.spawn(command); const reader = process.output.getReader(); const chunks = []; const decoder = new TextDecoder(); while (true) { const { done, value } = await reader.read(); if (done) break; chunks.push(decoder.decode(value, { stream: true })); } return { output: chunks.join('') }; } } catch (e: any) { console.log(e.message); //ExactReadable ended if (e.message.includes('ExactReadable ended')) { console.error('connection cut off'); return { error: 'ExactReadableEndedError' }; } console.error('error while execute command', e); return {}; } } export async function disconnect() { let instance = getAdbInstance(); if (instance) { await instance.close(); console.log('close instance'); saveAdbInstance(undefined); } } export async function pull(filename: string) { let instance = getAdbInstance(); if (instance) { let chunkList: Uint8Array[] = []; let sync = await instance.sync(); const content = sync.read(filename); let result = content.values(); let res; let result_string = ''; while ((res = await result.next()) != null) { // console.log(res.value); if (res.value != undefined) { result_string += new TextDecoder().decode(res.value); } if (res.done) { break; } } return result_string; } } export async function push(path: string, obj: string) { let instance = getAdbInstance(); if (instance) { let sync = await instance.sync(); const encoder = new TextEncoder(); const file: ReadableStream> = new ReadableStream({ start(controller) { controller.enqueue(new Uint8Array(encoder.encode(obj))); controller.close(); } }); try { console.log('support push v2', sync.supportsSendReceiveV2); await sync.write({ filename: path, file }); } catch (error) { console.log('error while trying to write to machine', error); } finally { await sync.dispose(); } } } // logcat stream // TODO: screen mirror export function getScrcpyBinaryFromSource() { //https://github.com/Genymobile/scrcpy/releases }