Supra_App/src/lib/core/adb/adb.ts

185 lines
4.4 KiB
TypeScript
Raw Normal View History

2026-02-17 14:30:02 +07:00
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<ArrayBufferLike>[] = [];
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<MaybeConsumable<Uint8Array>> = 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
}