Commit 83ceae1a authored by ali's avatar ali

feat: 照片数字人直播切换到sdk 接口方案

parent d32e1cdc
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"axios": "^1.6.2", "axios": "^1.6.2",
"crypto-js": "^4.1.1",
"electron-store": "^8.1.0", "electron-store": "^8.1.0",
"EventEmitter": "^1.0.0", "EventEmitter": "^1.0.0",
"flv.js": "^1.6.2", "flv.js": "^1.6.2",
...@@ -3911,6 +3912,11 @@ ...@@ -3911,6 +3912,11 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
},
"node_modules/css": { "node_modules/css": {
"version": "2.2.4", "version": "2.2.4",
"resolved": "https://registry.npmmirror.com/css/-/css-2.2.4.tgz", "resolved": "https://registry.npmmirror.com/css/-/css-2.2.4.tgz",
......
...@@ -41,7 +41,8 @@ ...@@ -41,7 +41,8 @@
"vue": "^3.3.7", "vue": "^3.3.7",
"vue-i18n": "^9.6.2", "vue-i18n": "^9.6.2",
"vue-router": "^4.2.5", "vue-router": "^4.2.5",
"vuetify": "^3.3.23" "vuetify": "^3.3.23",
"crypto-js": "^4.1.1"
}, },
"devDependencies": { "devDependencies": {
"@mdi/font": "^7.3.67", "@mdi/font": "^7.3.67",
...@@ -75,4 +76,4 @@ ...@@ -75,4 +76,4 @@
"vue-tsc": "^1.8.22", "vue-tsc": "^1.8.22",
"xvfb-maybe": "^0.2.1" "xvfb-maybe": "^0.2.1"
} }
} }
\ No newline at end of file
...@@ -64,10 +64,11 @@ const asrItems = ref([ ...@@ -64,10 +64,11 @@ const asrItems = ref([
// 'Whisper Api' // 'Whisper Api'
]) ])
const liveHosts = ref([ const liveHosts = ref([
'http://111.229.216.162:9000', 'laihua',
'http://124.221.182.173:9000', // 'http://111.229.216.162:9000',
'http://110.42.214.59:9000', // 'http://124.221.182.173:9000',
'http://122.51.32.12:9000' // 'http://110.42.214.59:9000',
// 'http://122.51.32.12:9000'
]) ])
const asrSelect = ref(setting.asr) const asrSelect = ref(setting.asr)
......
/* eslint-disable camelcase */ /* eslint-disable camelcase */
import http from '@/renderer/utils/http' import http, { axiosInstance } from '@/renderer/utils/http'
import { JWT } from '@/renderer/utils/jwt';
import { HwWebRTC } from './HwWebRTC' import { HwWebRTC } from './HwWebRTC'
import { guid } from '@/renderer/utils/index' import { guid } from '@/renderer/utils/index'
import EventEmitter from 'EventEmitter' import EventEmitter from 'EventEmitter'
...@@ -11,14 +12,20 @@ type LiveOptions = { ...@@ -11,14 +12,20 @@ type LiveOptions = {
audioUrl?: string | null audioUrl?: string | null
taskId: string taskId: string
imgUrl: string imgUrl: string
pushUrl: string pushUrl?: string
pullUrl: string pullUrl?: string
speed?: number speed?: number
bitrate: number bitrate: number
superres: boolean superres: boolean
isLast: boolean isLast: boolean
} }
type SdkConfigDataParams = {
baseUrl: string;
baseSocketUrl: string;
};
export class PhotoAnswer { export class PhotoAnswer {
question = '' question = ''
answer = '' answer = ''
...@@ -65,7 +72,7 @@ export type PhotoEventTypeFn<T extends PhotoEventType> = { ...@@ -65,7 +72,7 @@ export type PhotoEventTypeFn<T extends PhotoEventType> = {
}[T] }[T]
export class PhotoRole extends EventEmitter { export class PhotoRole extends EventEmitter {
readonly host: string private host: string
readonly view: HTMLCanvasElement readonly view: HTMLCanvasElement
readonly ctx: CanvasRenderingContext2D readonly ctx: CanvasRenderingContext2D
private _webRTCContainer: HTMLDivElement = document.createElement('div') private _webRTCContainer: HTMLDivElement = document.createElement('div')
...@@ -109,10 +116,10 @@ export class PhotoRole extends EventEmitter { ...@@ -109,10 +116,10 @@ export class PhotoRole extends EventEmitter {
this._liveTaskQueue.push({ this._liveTaskQueue.push({
imgUrl: this.url, imgUrl: this.url,
taskId, taskId,
pushUrl: `rtmp://push.laihua.com/web/${taskId}`, // pushUrl: `rtmp://push.laihua.com/web/${taskId}`,
pullUrl: `webrtc://pull.laihua.com/web/${taskId}`, // pullUrl: `webrtc://pull.laihua.com/web/${taskId}`,
speaker: 'speaker', speaker: '101015',
languageCode: 'zh-CN', languageCode: 'zh_cn',
audioUrl, audioUrl,
speed: 10, speed: 10,
bitrate: 2000, bitrate: 2000,
...@@ -224,7 +231,7 @@ export class PhotoRole extends EventEmitter { ...@@ -224,7 +231,7 @@ export class PhotoRole extends EventEmitter {
const resp = (await http({ const resp = (await http({
method: 'POST', method: 'POST',
url: `${this.host}/create`, url: `${this.host}/live`,
data: { ...options } // ip: 'http://116.63.168.14:9000' data: { ...options } // ip: 'http://116.63.168.14:9000'
})) as { })) as {
code: number code: number
...@@ -254,7 +261,7 @@ export class PhotoRole extends EventEmitter { ...@@ -254,7 +261,7 @@ export class PhotoRole extends EventEmitter {
private async _appendLive(options: LiveOptions) { private async _appendLive(options: LiveOptions) {
const resp = (await http({ const resp = (await http({
method: 'POST', method: 'POST',
url: `${this.host}/append`, url: `${this.host}/live/append`,
data: { ...options } // ip: 'http://116.63.168.14:9000' data: { ...options } // ip: 'http://116.63.168.14:9000'
})) as { })) as {
code: number code: number
...@@ -284,7 +291,7 @@ export class PhotoRole extends EventEmitter { ...@@ -284,7 +291,7 @@ export class PhotoRole extends EventEmitter {
private async _pushLive(taskId: string) { private async _pushLive(taskId: string) {
const resp = (await http({ const resp = (await http({
method: 'POST', method: 'POST',
url: `${this.host}/push`, url: `${this.host}/live/push`,
data: { taskId } data: { taskId }
})) as { })) as {
code: number code: number
...@@ -302,7 +309,7 @@ export class PhotoRole extends EventEmitter { ...@@ -302,7 +309,7 @@ export class PhotoRole extends EventEmitter {
private async _getLiveStatus(taskId: string) { private async _getLiveStatus(taskId: string) {
const resp = (await http({ const resp = (await http({
method: 'GET', method: 'GET',
url: `${this.host}/status`, url: `${this.host}/live/validateStatus`,
params: { taskId } params: { taskId }
})) as { })) as {
code: number code: number
...@@ -355,20 +362,20 @@ export class PhotoRole extends EventEmitter { ...@@ -355,20 +362,20 @@ export class PhotoRole extends EventEmitter {
async initLive() { async initLive() {
console.time('init') console.time('init')
console.time('init-_createLive') console.time('_createLive')
const { pullUrl, taskId, imgInfo } = await this._createLive({ const { pullUrl, taskId, imgInfo } = await this._createLive({
imgUrl: this.url, imgUrl: this.url,
pushUrl: `rtmp://push.laihua.com/web/${this.sessionId}`, // pushUrl: `rtmp://push.laihua.com/web/${this.sessionId}`,
pullUrl: `webrtc://pull.laihua.com/web/${this.sessionId}`, // pullUrl: `webrtc://pull.laihua.com/web/${this.sessionId}`,
taskId: this.sessionId, taskId: this.sessionId,
speaker: 'speaker', speaker: '101015',
languageCode: 'zh-CN', languageCode: 'zh_cn',
audioUrl: '', audioUrl: '',
bitrate: 2000, bitrate: 2000,
superres: false, superres: false,
isLast: true isLast: true
}) })
console.timeEnd('init-_createLive') console.timeEnd('_createLive')
this._rtcVideoInfo = imgInfo this._rtcVideoInfo = imgInfo
console.time('play') console.time('play')
...@@ -454,10 +461,33 @@ export class PhotoRole extends EventEmitter { ...@@ -454,10 +461,33 @@ export class PhotoRole extends EventEmitter {
this._rtc = new HwWebRTC(this._webRTCContainer.id) this._rtc = new HwWebRTC(this._webRTCContainer.id)
this._bindEvents() this._bindEvents()
if (this.host === 'laihua') {
this.host = await this.auth();
}
await this.initLive() await this.initLive()
// this._pollStatus(this.sessionId); // this._pollStatus(this.sessionId);
} }
async auth() {
const token = JWT.encode(
JSON.stringify({ appId: '88ca56ac88257f14', iat: new Date().getTime() + 1000 * 30 }),
'92b39dd13b952bcd4ce1995473cc3302'
);
axiosInstance.defaults.headers.common.Authorization = `Bearer ${token}`;
const res = await http({ url: 'https://openapi.laihua.com/sdk/getConfig' });
const { code, data } = res as {
code: number | string;
data: SdkConfigDataParams | string;
};
if (code !== 200) {
throw new Error(`${code}: ${ JSON.stringify(res) }`);
}
return (data as SdkConfigDataParams).baseUrl;
}
destroy() { destroy() {
this._webRTCContainer && document.body.removeChild(this._webRTCContainer) this._webRTCContainer && document.body.removeChild(this._webRTCContainer)
this._rtc?.stopPlay() this._rtc?.stopPlay()
......
...@@ -66,7 +66,7 @@ const useSettingsStore = defineStore('settings', { ...@@ -66,7 +66,7 @@ const useSettingsStore = defineStore('settings', {
llmUrl: 'ws://127.0.0.1:9899/api/v1/stream', llmUrl: 'ws://127.0.0.1:9899/api/v1/stream',
llmToTTSSliceLength: 20, llmToTTSSliceLength: 20,
voskWsLUrl: 'ws://127.0.0.1:2700', voskWsLUrl: 'ws://127.0.0.1:2700',
liveHost: 'http://122.51.32.12:9000', liveHost: 'laihua',
vConsole: true vConsole: true
}) as ISettings, }) as ISettings,
getters: {}, getters: {},
......
import CryptoJS from 'crypto-js';
export class JWT {
static sign(content: string, secret: string) {
const r = CryptoJS.HmacSHA256(content, secret);
const b = CryptoJS.enc.Base64.stringify(r);
return this.base64urlEscape(b);
}
static base64urlEscape(str: string) {
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
static toBase64(content: string) {
return btoa(content);
}
static encode(username: string, secret: string) {
const header = this.toBase64(JSON.stringify({ typ: 'JWT', alg: 'HS256' }));
const content = this.toBase64(username);
const sign = this.sign([header, content].join('.'), secret);
return [header, content, sign].join('.');
}
static decode(token: string, secret: string) {
const [header, content, sign] = token.split('.');
const newSign = this.sign([header, content].join('.'), secret);
if (sign === newSign) {
return atob(content);
} else {
throw new Error('被篡改');
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment