@plasosdk/plaso-electron-sdk 1.3.6 → 1.3.8-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +311 -34
- package/index.html +58 -8
- package/js/macro.js +9 -4
- package/js/render.js +20 -7
- package/package.json +4 -1
- package/scripts/downloadPrebuild.js +2 -2
package/README.md
CHANGED
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
# Plaso SDK for Electron
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
-
|
|
7
|
-
- [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
## 目录
|
|
4
|
+
|
|
5
|
+
<!-- toc -->
|
|
6
|
+
- [环境支持](#环境支持)
|
|
7
|
+
- [安装](#安装)
|
|
8
|
+
- [electron-builder打包特别说明](#electron-builder打包特别说明)
|
|
9
|
+
- [使用](#使用)
|
|
10
|
+
- [在主进程中使用](#在主进程中使用)
|
|
11
|
+
- [在渲染进程中使用](#在渲染进程中使用)
|
|
12
|
+
- [打开实时课堂](#打开实时课堂)
|
|
13
|
+
- [打开备课课堂](#打开备课课堂)
|
|
14
|
+
- [打开实时课堂/备课课堂通用参数说明](#打开实时课堂备课课堂通用参数说明)
|
|
15
|
+
- [API参考](#api参考)
|
|
16
|
+
- [资料中心](#资料中心)
|
|
17
|
+
- [播放历史课堂](#播放历史课堂)
|
|
18
|
+
- [注意点](#注意点)
|
|
19
|
+
- [Q&A](#qa)
|
|
20
|
+
<!-- tocstop -->
|
|
21
|
+
|
|
22
|
+
## 环境支持
|
|
23
|
+
|
|
24
|
+
- MacOS:x86-64、arm64
|
|
25
|
+
- Windows:ia32、x64
|
|
26
|
+
- Electron:14.0.0~22.3.27
|
|
12
27
|
|
|
13
28
|
## 安装
|
|
14
29
|
|
|
@@ -27,33 +42,63 @@ npm install @plasosdk/plaso-electron-sdk --global-style
|
|
|
27
42
|
**<font color=red>注意: 不同平台需要单独安装,尤其是MacOS,请分别在Intel芯片和Apple芯片的电脑上安装</font>**
|
|
28
43
|
|
|
29
44
|
## electron-builder打包特别说明
|
|
30
|
-
|
|
45
|
+
|
|
46
|
+
### extraResources配置
|
|
47
|
+
|
|
48
|
+
> ⚠️ 注意:该配置在 1.3.7 版本有所调整。如果你正在从 < 1.3.7 升级到 >= 1.3.7,请参照下列说明进行调整。
|
|
49
|
+
|
|
50
|
+
agora-electron-sdk-v4包是在安装plaso-electron-sdk时通过脚本动态安装的,不在dependencies依赖树中,electron-builder打包时不会包含它。为了保留它,需要配置[extraResources](https://www.electron.build/configuration#extraresources)来单独拷贝一份,把它包含进来。
|
|
51
|
+
|
|
31
52
|
```json
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
53
|
+
{
|
|
54
|
+
"extraResources": [
|
|
55
|
+
{
|
|
56
|
+
"from": "node_modules/@plasosdk/plaso-electron-sdk/node_modules/agora-electron-sdk-v4",
|
|
57
|
+
"to": "app/node_modules/@plasosdk/plaso-electron-sdk/node_modules/agora-electron-sdk-v4",
|
|
58
|
+
"filter": [
|
|
59
|
+
"**/*"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"from": "node_modules/@plasosdk/plaso-electron-sdk/node_modules/agora-electron-sdk-v4/node_modules",
|
|
64
|
+
"to": "app/node_modules/@plasosdk/plaso-electron-sdk/node_modules/agora-electron-sdk-v4/node_modules",
|
|
65
|
+
"filter": [
|
|
66
|
+
"**/*"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
44
71
|
```
|
|
72
|
+
|
|
45
73
|
同时,由于这个包是通过extraResources进行拷贝的,需要在签名脚本中额外配置这个包的路径。
|
|
46
74
|
|
|
47
|
-
|
|
75
|
+
### 关闭asar
|
|
76
|
+
|
|
77
|
+
需要关闭asar,否则SDK会无法加载到node_modules。关闭方式详见electron-buider[官方文档](https://www.electron.build/configuration#asar)。
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"asar": false, // 关闭asar
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### MacOS赋予文件执行权限
|
|
86
|
+
|
|
87
|
+
打MacOS的包时有几个需要额外进行授权的可执行文件。建议通过额外的脚本来执行。
|
|
88
|
+
|
|
89
|
+
下面的可执行文件是在安装@plasosdk/plaso-electron-sdk时动态下载的,因此授权的脚本需要保证是在文件下载之后。
|
|
90
|
+
|
|
48
91
|
```shell
|
|
49
92
|
// 截图会用到
|
|
50
|
-
chmod +x node_modules/@plasosdk/plaso-electron-sdk/lib/flameshot
|
|
93
|
+
chmod +x node_modules/@plasosdk/plaso-electron-sdk/lib/flameshot.app
|
|
51
94
|
|
|
52
95
|
// 桌面共享会用到
|
|
53
96
|
chmod +x node_modules/@plasosdk/plaso-electron-sdk/lib/PlasoALD/PlasoALD.scpt
|
|
54
97
|
```
|
|
55
98
|
|
|
56
|
-
|
|
99
|
+
### 验证
|
|
100
|
+
|
|
101
|
+
MacOS打包完毕后,建议安装打好的包,验证上述操作是否成功。核对清单:
|
|
57
102
|
- 检查包内容中node_modules里面的agora-electron-sdk-v4包中是否包含node_modules,包含则说明打包正确。
|
|
58
103
|
- 上课测试截图功能是否正常,截图正常说明授权正确。
|
|
59
104
|
- 上课测试桌面共享功能,使用屏幕共享,并在浏览器里面播放一段视频,结束课堂后检查回放中能否听到桌面共享那一段播放的视频的声音,能听到说明授权正确。
|
|
@@ -98,7 +143,8 @@ interface CreateClassWindowPamras {
|
|
|
98
143
|
callback: (result: boolean) => void,
|
|
99
144
|
) => void;
|
|
100
145
|
onOpenResourceCenterFn?: () => void;
|
|
101
|
-
onGetExtFileNameFn?: (...args: any[]) => Promise<string>;
|
|
146
|
+
onGetExtFileNameFn?: (info: any[], ...args: any[]) => Promise<string>;
|
|
147
|
+
onGetPreParseFileNameFn?: (info: any[], option: { suffix: string }) => Promise<string>;
|
|
102
148
|
}
|
|
103
149
|
```
|
|
104
150
|
|
|
@@ -205,6 +251,7 @@ interface IQueryParams {
|
|
|
205
251
|
isNewMT?: number;
|
|
206
252
|
enableNewClassExam?: number;
|
|
207
253
|
d_enableReRecording?: number;
|
|
254
|
+
d_restrictAssistantPerm?: number;
|
|
208
255
|
}
|
|
209
256
|
```
|
|
210
257
|
|
|
@@ -234,6 +281,7 @@ interface IQueryParams {
|
|
|
234
281
|
| recordScreen | 否 | string | 传入`screen`表示录制屏幕(当前仅支持录制老师屏幕)|
|
|
235
282
|
| enableNewClassExam | 否 | number | 是否启用新版随堂测,取值:<ul><li>0: 不启用</li><li>1: 启用选择题</li><li>2: 启用填空题</li><li>3: 启用选择题+填空题</li></ul>默认不启用,建议传3 |
|
|
236
283
|
| d_enableReRecording | 否 | number | 是否允许老师/助教重新录制,取值:<ul><li>0: 不允许老师和助教重新录制</li><li>1: 仅允许老师重新录制</li><li>2: 仅允许助教重新录制</li><li>3: 允许老师和助教重新录制</li></ul>默认值为3 |
|
|
284
|
+
| d_restrictAssistantPerm | 否 | number | 是否启用限制助教权限,取值:<ul><li>0: 不启用</li><li>1: 启用</li></ul>默认不启用 |
|
|
237
285
|
|
|
238
286
|
**根据 queryParams 对象生成签名字符串**
|
|
239
287
|
> 注意:为了安全和各端签名统一,建议将签名的计算放在服务端,前端通过接口获取带签名的query
|
|
@@ -390,31 +438,208 @@ type onOpenResourceCenterFn = () => void;
|
|
|
390
438
|
// 通过 insertObject 插入的文件传入 参数 info 时,怎么从info中获取文件的可访问地址的逻辑在用户那,所以需要函数从外部用户获取外部用户传入的文件地址
|
|
391
439
|
// 其中info会作为args中的第一个参数回传
|
|
392
440
|
|
|
393
|
-
type onGetExtFileNameFn = (...args: any[]) => Promise<string>;
|
|
441
|
+
type onGetExtFileNameFn = (info: any, ...args: any[]) => Promise<string>;
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
##### onGetPreParseFileNameFn
|
|
445
|
+
```ts
|
|
446
|
+
// 通过 insertObject 插入的文件传入 参数 info 时,如果该资源是预解析资源,SDK需要访问解析后的文件
|
|
447
|
+
// 其中info会作为args中的第一个参数回传
|
|
448
|
+
// 预解析结果是一个文件夹,要访问文件夹里的哪个文件,会通过suffix指出,如/index.html、/pre1.jpg等
|
|
449
|
+
|
|
450
|
+
type onGetPreParseFileNameFn = (info: any, option: { suffix: string }) => Promise<string>;
|
|
394
451
|
```
|
|
395
452
|
|
|
396
453
|
#### API参考
|
|
397
454
|
|
|
398
455
|
##### PlasoElectronSdk.initLogConfig
|
|
399
456
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
**日志默认位置**:
|
|
457
|
+
该方法用于设置SDK日志位置,SDK崩溃时会在同级目录下生成`reports`文件夹存储 dump,在调用`createLiveClassWindow`前设置,不调用SDK会在默认位置写入日志,默认位置如下:
|
|
403
458
|
|
|
404
459
|
```js
|
|
460
|
+
// 获取日志路径的代码如下
|
|
405
461
|
require('path').join(require('electron').app.getPath('userData'), 'P403FileTemp');
|
|
406
462
|
|
|
407
463
|
// Windows:C:\Users\${userName}\AppData\Roaming\${appName}\P403FileTemp
|
|
408
464
|
// Mac:/Users/${userName}/Application\ Support/${appName}/P403FileTemp
|
|
409
465
|
```
|
|
410
466
|
|
|
411
|
-
|
|
467
|
+
参考示例:
|
|
412
468
|
|
|
413
469
|
```ts
|
|
414
470
|
// 代码示例
|
|
415
471
|
const PlasoElectronSdk = window.require('@plasosdk/plaso-electron-sdk');
|
|
416
|
-
const
|
|
417
|
-
PlasoElectronSdk.initLogConfig(
|
|
472
|
+
const logDir = 'C:/Users/userName/Desktop/electronDemo/electron12.0.18_x32/resources/app';
|
|
473
|
+
PlasoElectronSdk.initLogConfig(logDir);
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
> 重要:用户需要对日志目录做定期清理和及时上传日志到自己的服务器或OSS。推荐在退出SDK后做一次清理和上传,建议清理创建时间超过7天的日志即可。
|
|
477
|
+
相关代码示例如下:
|
|
478
|
+
```ts
|
|
479
|
+
import fs from 'fs';
|
|
480
|
+
import path from 'path';
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* 清理创建时间超过7天的日志
|
|
484
|
+
* @param logDir 日志目录
|
|
485
|
+
*/
|
|
486
|
+
function clearExpireLogs(logDir: string) {
|
|
487
|
+
try {
|
|
488
|
+
const logFile = fs.readdirSync(logDir).filter((fileName) => /\.(log|dmp|ips|diag)/.test(path.extname(fileName)));
|
|
489
|
+
logFile.forEach((fileName) => {
|
|
490
|
+
const fileInfo = fs.statSync(path.join(logDir, fileName));
|
|
491
|
+
const fileDate = new Date(fileInfo.birthtime).setHours(0, 0, 0, 0).valueOf();
|
|
492
|
+
const currentDate = new Date().setHours(0, 0, 0, 0);
|
|
493
|
+
if (currentDate - fileDate > 7 * 24 * 60 * 60 * 1000) {
|
|
494
|
+
fs.unlinkSync(path.join(logDir, fileName));
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
} catch (e) {
|
|
498
|
+
console.error('clear expire log error', e);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* 收集崩溃日志
|
|
504
|
+
* @param logDir 日志目录
|
|
505
|
+
* @param pendingUploadDir 待上传目录
|
|
506
|
+
*/
|
|
507
|
+
function collectCrashLogs(logDir: string, pendingUploadDir: string) {
|
|
508
|
+
const platform = require('os').platform();
|
|
509
|
+
switch (platform) {
|
|
510
|
+
case 'darwin':
|
|
511
|
+
const newDumpFolder = path.join(logDir, '/new/');
|
|
512
|
+
const pendingDumpFolder = path.join(logDir, '/pending/');
|
|
513
|
+
const completedDumpFolder = path.join(logDir, '/completed/');
|
|
514
|
+
const diagnosticReports = path.normalize('/Library/Logs/DiagnosticReports');
|
|
515
|
+
collectDumpFiles(newDumpFolder);
|
|
516
|
+
collectDumpFiles(pendingDumpFolder);
|
|
517
|
+
collectDumpFiles(completedDumpFolder);
|
|
518
|
+
collectSystemLogs(diagnosticReports);
|
|
519
|
+
break;
|
|
520
|
+
case 'win32':
|
|
521
|
+
const reportsDumpFolder = path.join(logDir, '/reports/');
|
|
522
|
+
collectDumpFiles(reportsDumpFolder);
|
|
523
|
+
break;
|
|
524
|
+
default:
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function collectSystemLogs(folder: string) {
|
|
529
|
+
try {
|
|
530
|
+
const files = fs.readdirSync(folder);
|
|
531
|
+
const todayDate = new Date();
|
|
532
|
+
const formatDate = `${todayDate.getFullYear()}-${(todayDate.getMonth() + 1).toString().padStart(2, '0')}-${todayDate
|
|
533
|
+
.getDate()
|
|
534
|
+
.toString()
|
|
535
|
+
.padStart(2, '0')}`;
|
|
536
|
+
files.forEach((name) => {
|
|
537
|
+
const reg = new RegExp(`Electron Helper.*${formatDate}`);
|
|
538
|
+
if (name && name.match(reg)) {
|
|
539
|
+
fs.copyFileSync(path.join(folder, name), path.join(pendingUploadDir, name));
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
} catch (e) {
|
|
543
|
+
console.error(e);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function collectDumpFiles(folder: string) {
|
|
548
|
+
try {
|
|
549
|
+
const files = fs.readdirSync(folder);
|
|
550
|
+
files.forEach((name) => {
|
|
551
|
+
if (name) {
|
|
552
|
+
fs.copyFileSync(path.join(folder, name), path.join(pendingUploadDir, name));
|
|
553
|
+
fs.unlinkSync(path.join(folder, name));
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
} catch (e) {
|
|
557
|
+
console.error(e);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* 收集业务日志
|
|
564
|
+
* @param logDir 日志目录
|
|
565
|
+
* @param pendingUploadDir 待上传目录
|
|
566
|
+
*/
|
|
567
|
+
function collectBussinessLogs(logDir: string, pendingUploadDir: string) {
|
|
568
|
+
const logFiles = fs.readdirSync(logDir).filter((fileName) => /\.(log|dmp|ips|diag)/.test(path.extname(fileName)));
|
|
569
|
+
logFiles.forEach((fileName) => {
|
|
570
|
+
fs.copyFileSync(path.join(logDir, fileName), path.join(pendingUploadDir, fileName));
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* 收集日志
|
|
576
|
+
* @param logDir 日志目录
|
|
577
|
+
* @param pendingUploadDir 待上传目录
|
|
578
|
+
*/
|
|
579
|
+
function collectLogs(logDir: string, pendingUploadDir: string) {
|
|
580
|
+
collectCrashLogs(logDir, pendingUploadDir);
|
|
581
|
+
collectBussinessLogs(logDir, pendingUploadDir);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* 压缩日志
|
|
586
|
+
* @param pendingUploadDir 待上传目录
|
|
587
|
+
*/
|
|
588
|
+
function zipLogs(pendingUploadDir: string): Promise<string> {
|
|
589
|
+
return new Promise((resolve) => {
|
|
590
|
+
const zipLogPath = path.join(pendingUploadDir, 'xxx.zip'); // 这边的xxx.zip取名时建议将用户名以及上传时间拼接上去,方便查询
|
|
591
|
+
// TODO: 这边做压缩,压缩完毕后将zip文件路径返回
|
|
592
|
+
resolve(zipLogPath);
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* 上传日志到服务器
|
|
598
|
+
* @param filePath zip文件路径
|
|
599
|
+
*/
|
|
600
|
+
function uploadToServer(filePath: string): Promise<boolean> {
|
|
601
|
+
return new Promise((resolve) => {
|
|
602
|
+
// TODO: 这边做上传
|
|
603
|
+
resolve(true);
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* 上传日志
|
|
609
|
+
* @param logDir 日志目录
|
|
610
|
+
*/
|
|
611
|
+
export function uploadLogs(logDir: string): Promise<boolean> {
|
|
612
|
+
return new Promise(async (resolve) => {
|
|
613
|
+
try {
|
|
614
|
+
// 上传前先清理过期的日志,避免垃圾日志太多
|
|
615
|
+
clearExpireLogs(logDir);
|
|
616
|
+
|
|
617
|
+
// 创建待上传目录
|
|
618
|
+
const pendingUploadDir = path.join(logDir, 'pendingUpload');
|
|
619
|
+
if (fs.existsSync(pendingUploadDir)) {
|
|
620
|
+
fs.rmdirSync(pendingUploadDir, { recursive: true });
|
|
621
|
+
}
|
|
622
|
+
fs.mkdirSync(pendingUploadDir);
|
|
623
|
+
|
|
624
|
+
// 将日志复制到待上传目录
|
|
625
|
+
collectLogs(logDir, pendingUploadDir);
|
|
626
|
+
|
|
627
|
+
// 压缩待上传目录
|
|
628
|
+
const zipName = await zipLogs(pendingUploadDir);
|
|
629
|
+
|
|
630
|
+
// 将压缩后的日志上传到服务器
|
|
631
|
+
await uploadToServer(zipName);
|
|
632
|
+
|
|
633
|
+
// 上传完成后删除临时文件
|
|
634
|
+
fs.unlinkSync(zipName);
|
|
635
|
+
fs.rmdirSync(pendingUploadDir, { recursive: true });
|
|
636
|
+
|
|
637
|
+
resolve(true);
|
|
638
|
+
} catch (error) {
|
|
639
|
+
resolve(false);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
}
|
|
418
643
|
```
|
|
419
644
|
|
|
420
645
|
##### PlasoElectronSdk.getVersion
|
|
@@ -465,6 +690,8 @@ const enum FILE_TYPE {
|
|
|
465
690
|
DOC,
|
|
466
691
|
/** 这种模式传入真实EXCEL地址 */
|
|
467
692
|
XLS,
|
|
693
|
+
/** 预解析PPT */
|
|
694
|
+
ISPRINGPPT,
|
|
468
695
|
/** 备课文件 */
|
|
469
696
|
PREPARE_LESSONS,
|
|
470
697
|
};
|
|
@@ -483,6 +710,9 @@ interface IFileData {
|
|
|
483
710
|
/** 访问权限为公开的全地址,建议使用https协议的全地址,传入`info`时会忽略该属性 */
|
|
484
711
|
url?: string;
|
|
485
712
|
|
|
713
|
+
/** 预解析结果的页数(插入预解析PDF/DOC/XLS时要传入这个属性) */
|
|
714
|
+
totalPages?: number;
|
|
715
|
+
|
|
486
716
|
/**
|
|
487
717
|
* 当用户插入的文件有签名时效时需要以info属性插入文件,这种方式需要配合onGetExtFileNameFn回调使用。
|
|
488
718
|
* `info`的值由SDK用户自行决定,SDK内部会把`info`作为onGetExtFileNameFn回调函数的参数传入,由SDK用户
|
|
@@ -553,6 +783,18 @@ const fileDataWithUrl = {
|
|
|
553
783
|
};
|
|
554
784
|
```
|
|
555
785
|
|
|
786
|
+
###### 插入预解析PPT
|
|
787
|
+
|
|
788
|
+
```ts
|
|
789
|
+
// 预解析PPT仅支持info插入
|
|
790
|
+
|
|
791
|
+
const fileDataWithInfo = {
|
|
792
|
+
type: PlasoElectronSdk.FILE_TYPE.ISPRINGPPT,
|
|
793
|
+
title: 'xxx',
|
|
794
|
+
info: ['xxx', ...],
|
|
795
|
+
};
|
|
796
|
+
```
|
|
797
|
+
|
|
556
798
|
###### 插入音视频
|
|
557
799
|
|
|
558
800
|
```ts
|
|
@@ -589,6 +831,17 @@ const fileDataWithInfo = {
|
|
|
589
831
|
}
|
|
590
832
|
```
|
|
591
833
|
|
|
834
|
+
###### 插入预解析PDF
|
|
835
|
+
```ts
|
|
836
|
+
// 预解析PDF仅支持info接入,且需要传入totalPages总页数
|
|
837
|
+
|
|
838
|
+
const fileDataWithInfo = {
|
|
839
|
+
type: PlasoElectronSdk.FILE_TYPE.PDF,
|
|
840
|
+
title: 'xxx',
|
|
841
|
+
info: ['xxx', ...],
|
|
842
|
+
totalPages: 12,
|
|
843
|
+
}
|
|
844
|
+
```
|
|
592
845
|
|
|
593
846
|
###### 插入WORD/EXCEL/DOC/XLS
|
|
594
847
|
|
|
@@ -602,6 +855,19 @@ const fileDataWithUrl = {
|
|
|
602
855
|
}
|
|
603
856
|
```
|
|
604
857
|
|
|
858
|
+
###### 插入预解析DOC/XLS
|
|
859
|
+
|
|
860
|
+
```ts
|
|
861
|
+
// 预解析DOC/XLS仅支持info插入,且需要传入totalPages总页数
|
|
862
|
+
|
|
863
|
+
const fileDataWithInfo = {
|
|
864
|
+
type: PlasoElectronSdk.FILE_TYPE/.DOC/.XLS,
|
|
865
|
+
title: 'xxx',
|
|
866
|
+
info: ['xxx', ...],
|
|
867
|
+
totalPages: 12,
|
|
868
|
+
}
|
|
869
|
+
```
|
|
870
|
+
|
|
605
871
|
## 资料中心
|
|
606
872
|
|
|
607
873
|
step1、进课堂时,对象 **classOptions.supportShowResourceCenter** 需要是 true
|
|
@@ -610,7 +876,9 @@ step2、在课堂内点击资料中心后,会触发进课堂时传入的回调
|
|
|
610
876
|
|
|
611
877
|
step3、选择文件后,通过 **PlasoElectronSdk.insertObject** 方法插入文件,方法入参参考 [insertObject](#insert-object) 说明
|
|
612
878
|
|
|
613
|
-
step4、insertObject 方法 入参 有 info 时,此时 会触发进课堂时传入的回调函数 **onGetExtFileNameFn** 来获取文件的全地址
|
|
879
|
+
step4.1、insertObject 方法 入参 有 info 时,此时 会触发进课堂时传入的回调函数 **onGetExtFileNameFn** 来获取文件的全地址
|
|
880
|
+
|
|
881
|
+
step4.2、insertObject 方法插入的如果是预解析资源,那么就会触发回调 **onGetPreParseFileNameFn** 来获取解析后资源的全地址
|
|
614
882
|
|
|
615
883
|
## 播放历史课堂
|
|
616
884
|
|
|
@@ -618,6 +886,8 @@ step4、insertObject 方法 入参 有 info 时,此时 会触发进课堂时
|
|
|
618
886
|
|
|
619
887
|
2、当课堂中insertObject使用了`info`属性时,SDK内部需要外部传入`getExtFileName`,因此历史课堂需要使用jssdk的接入方式, 详见:**[Web播放器-jssdk接入](https://open.plaso.cn/doc-6285192#jssdk%E6%8E%A5%E5%85%A5)**
|
|
620
888
|
|
|
889
|
+
3、当课堂中insertObject插入了预解析资源时,SDK内部需要外部传入`getPreParseFileName`,因此历史课堂需要使用jssdk的接入方式,同上。
|
|
890
|
+
|
|
621
891
|
## 注意点
|
|
622
892
|
|
|
623
893
|
(1)用户的课堂外主窗口销毁时需要销毁课堂窗口
|
|
@@ -625,3 +895,10 @@ step4、insertObject 方法 入参 有 info 时,此时 会触发进课堂时
|
|
|
625
895
|
(2)**基于 此包封装新包时**:注意 @electron/remote 这个包的位置需要 和新包处于同级目录,需要把 和该包同级的@electron/remote 移到新包的同级目录处
|
|
626
896
|
|
|
627
897
|
(3)确保 仅最后的 node_moudles 的顶层有 @electron/remote
|
|
898
|
+
|
|
899
|
+
## Q&A
|
|
900
|
+
|
|
901
|
+
### 进课堂遇到闪退/崩溃怎么办?
|
|
902
|
+
- 检查应用程序路径中是否存在中文,路径中不应出现中文</br>
|
|
903
|
+
可通过在主进程中,用`console.log(require.resolve('@plasosdk/plaso-electron-sdk'))`来检查路径
|
|
904
|
+
- 如果是Mac,进课堂会需要麦克风权限。打开系统设置->隐私与安全性->麦克风,查看列表里你的应用程序是否被授权。
|
package/index.html
CHANGED
|
@@ -137,14 +137,63 @@
|
|
|
137
137
|
document.head.appendChild(ele);
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
let hasInit = false;
|
|
141
|
+
function init(appinfo) {
|
|
142
|
+
if (hasInit) {
|
|
143
|
+
return;
|
|
144
|
+
} else {
|
|
145
|
+
hasInit = true;
|
|
146
|
+
}
|
|
140
147
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (
|
|
144
|
-
|
|
148
|
+
// from url
|
|
149
|
+
var query;
|
|
150
|
+
if (window.location.search) {
|
|
151
|
+
query = window.location.search.substring(1).split("&");
|
|
152
|
+
for (var i = 0, l = query.length; i < l; i++) {
|
|
153
|
+
var temp = query[i].split('=');
|
|
154
|
+
if (temp.length == 2) {
|
|
155
|
+
globalAppInfo[temp[0]] = decodeURIComponent(temp[1]);
|
|
156
|
+
} else if (
|
|
157
|
+
typeof globalAppInfo[temp[0]] === 'string' &&
|
|
158
|
+
globalAppInfo[temp[0]].length > 0 &&
|
|
159
|
+
!isNaN(Number(globalAppInfo[temp[0]])) &&
|
|
160
|
+
Number(globalAppInfo[temp[0]]) !== undefined &&
|
|
161
|
+
!globalAppInfo[temp[0]]?.includes?.('.')
|
|
162
|
+
) {
|
|
163
|
+
globalAppInfo[temp[0]] = Number(globalAppInfo[temp[0]]);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// from ipc
|
|
169
|
+
if (appinfo) {
|
|
170
|
+
globalAppInfo = Object.assign({}, globalAppInfo, appinfo)
|
|
145
171
|
}
|
|
146
|
-
globalAppInfo.needFullScreen = false;
|
|
147
172
|
|
|
173
|
+
// properties init
|
|
174
|
+
if (globalAppInfo.rhost == undefined && location.protocol.indexOf('http') == 0) {
|
|
175
|
+
globalAppInfo.rhost = './';
|
|
176
|
+
if (!globalAppInfo.dhost) {
|
|
177
|
+
if (globalAppInfo.env) {
|
|
178
|
+
if (globalAppInfo.env === 'local') {
|
|
179
|
+
globalAppInfo.dhost = location.origin;
|
|
180
|
+
} else {
|
|
181
|
+
globalAppInfo.dhost = `${location.protocol}//${globalAppInfo.env}.plaso.cn/`;
|
|
182
|
+
}
|
|
183
|
+
} else if (location.origin.indexOf('plaso.cn') > -1) {
|
|
184
|
+
globalAppInfo.dhost = location.origin.replace('wwwr', 'www');
|
|
185
|
+
globalAppInfo.env = location.host.split('.')[0];
|
|
186
|
+
} else if (location.origin.indexOf('127.0.0.1') > -1 || location.origin.indexOf('192.168') > -1) {
|
|
187
|
+
globalAppInfo.env = 'dev';
|
|
188
|
+
globalAppInfo.dhost = `${location.protocol}//${globalAppInfo.env}.plaso.cn/`;
|
|
189
|
+
} else {
|
|
190
|
+
globalAppInfo.dhost = `${location.protocol}//www.plaso.cn/`;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (!globalAppInfo.env) globalAppInfo.env = 'www';
|
|
195
|
+
globalAppInfo.markString = globalAppInfo.markString ?? '';
|
|
196
|
+
globalAppInfo.needFullScreen = false;
|
|
148
197
|
if (globalAppInfo.rhost && globalAppInfo.rhost.substring(globalAppInfo.rhost.length - 1) != '/') {
|
|
149
198
|
globalAppInfo.rhost += "/";
|
|
150
199
|
}
|
|
@@ -152,6 +201,7 @@
|
|
|
152
201
|
globalAppInfo.dhost += "/";
|
|
153
202
|
}
|
|
154
203
|
|
|
204
|
+
// load all.js
|
|
155
205
|
path = globalAppInfo.initjs || "js/all.js";
|
|
156
206
|
include(path);
|
|
157
207
|
}
|
|
@@ -166,14 +216,14 @@
|
|
|
166
216
|
if (key === 'openerId') openerId = Number(value);
|
|
167
217
|
});
|
|
168
218
|
|
|
169
|
-
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.
|
|
170
|
-
if (
|
|
219
|
+
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.INIT_GLOBAL_APPINFO, (_, appinfo) => {
|
|
220
|
+
if (appinfo) init(appinfo);
|
|
171
221
|
});
|
|
172
222
|
|
|
173
223
|
if(openerId){
|
|
174
224
|
const remote = getElectronRemote();
|
|
175
225
|
const webContents = remote.getCurrentWebContents();
|
|
176
|
-
ipcRenderer.sendTo(openerId, CLASS_WINDOW_MESG_TYPE.
|
|
226
|
+
ipcRenderer.sendTo(openerId, CLASS_WINDOW_MESG_TYPE.HTML_READY, {webContentId: webContents.id});
|
|
177
227
|
}
|
|
178
228
|
|
|
179
229
|
}
|
package/js/macro.js
CHANGED
|
@@ -24,10 +24,10 @@ const CLASS_WINDOW_MESG_TYPE = {
|
|
|
24
24
|
LIVE_WINDOW_READY: 'liveWindowReady',
|
|
25
25
|
/** 课堂结束 */
|
|
26
26
|
ON_CLASS_FINISHED: 'onClassFinished',
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
|
|
27
|
+
/** 初始化 globalAppInfo */
|
|
28
|
+
INIT_GLOBAL_APPINFO: 'init_global_appinfo',
|
|
29
|
+
/** html ready */
|
|
30
|
+
HTML_READY: 'html_ready',
|
|
31
31
|
/** 保存白板文件 */
|
|
32
32
|
SAVE_BOARD: 'save_board',
|
|
33
33
|
/** 响应保存白板文件 */
|
|
@@ -42,6 +42,10 @@ const CLASS_WINDOW_MESG_TYPE = {
|
|
|
42
42
|
GET_EXT_FILE_NAME: 'getExtFileName',
|
|
43
43
|
/** 响应getExtFileName */
|
|
44
44
|
RESP_GET_EXT_FILE_NAME: 'respGetExtFileName',
|
|
45
|
+
/** 通知外部调用getPreParseFileName */
|
|
46
|
+
GET_PRE_PARSE_FILE_NAME: 'getPreParseFileName',
|
|
47
|
+
/** 响应getPreParseFileName */
|
|
48
|
+
RESP_GET_PRE_PARSE_FILE_NAME: 'respGetPreParseFileName',
|
|
45
49
|
};
|
|
46
50
|
|
|
47
51
|
const LEVEL = {
|
|
@@ -68,6 +72,7 @@ const FILE_TYPE = {
|
|
|
68
72
|
VIDEO: 7,
|
|
69
73
|
DOC: 14,
|
|
70
74
|
XLS: 15,
|
|
75
|
+
ISPRINGPPT: 21,
|
|
71
76
|
PREPARE_LESSONS: 22,
|
|
72
77
|
};
|
|
73
78
|
|
package/js/render.js
CHANGED
|
@@ -46,7 +46,8 @@ let currentWebContentsId = null;
|
|
|
46
46
|
* callback: (result: boolean) => void
|
|
47
47
|
* ) => void} [onSaveBoardFn] 保存板书,具体的保存逻辑由外部实现,取消保存板书时,callback传false, 不然传true
|
|
48
48
|
* @property {()=>void} [onOpenResourceCenterFn] 通知外部用户打开自己的资料中心,资料中心的具体ui和逻辑由外部用户自己实现
|
|
49
|
-
* @property {(
|
|
49
|
+
* @property {(info: any, option: any)=>Promise<string>} [onGetExtFileNameFn] 通过insertObject插入的文件传入 参数 info 时,怎么从info中获取文件的可访问地址的逻辑在用户那,所以需要函数从外部用户获取外部用户传入的文件地址
|
|
50
|
+
* @property {(info: any, option: { suffix: string })=>Promise<string>} [onGetPreParseFileNameFn] 类似onGetExtFileNameFn,只是获取的文件地址是预解析文件地址,通过suffix得到预解析文件路径
|
|
50
51
|
*
|
|
51
52
|
*/
|
|
52
53
|
|
|
@@ -64,10 +65,11 @@ function createClassWindow(classWindowProps) {
|
|
|
64
65
|
onSaveBoardFn,
|
|
65
66
|
onOpenResourceCenterFn,
|
|
66
67
|
onGetExtFileNameFn,
|
|
68
|
+
onGetPreParseFileNameFn,
|
|
67
69
|
} = classWindowProps;
|
|
68
70
|
|
|
69
71
|
logger.info(
|
|
70
|
-
`课堂窗口创建信息: classOptions is ${JSON.stringify(classOptions)},electronWinOptions is ${JSON.stringify(
|
|
72
|
+
`课堂窗口创建信息: version is ${getVersion()}, classOptions is ${JSON.stringify(classOptions)},electronWinOptions is ${JSON.stringify(
|
|
71
73
|
electronWinOptions,
|
|
72
74
|
)}`,
|
|
73
75
|
);
|
|
@@ -108,7 +110,7 @@ function createClassWindow(classWindowProps) {
|
|
|
108
110
|
rhost = `https://${env}.plaso.cn/static/yxtelectronsdk/`;
|
|
109
111
|
dhost = `https://${env}.plaso.cn/`;
|
|
110
112
|
} else {
|
|
111
|
-
rhost = `https://wwwr.plaso.cn/static/sdk/styleupime/${classOptions.version ?? '1.
|
|
113
|
+
rhost = `https://wwwr.plaso.cn/static/sdk/styleupime/${classOptions.version ?? '1.59.201'}/`;
|
|
112
114
|
dhost = 'https://www.plaso.cn/';
|
|
113
115
|
}
|
|
114
116
|
|
|
@@ -225,12 +227,12 @@ function createClassWindow(classWindowProps) {
|
|
|
225
227
|
logger.info(`课堂已结束,id:${currentWinId}, meetingId is ${meetingId}`);
|
|
226
228
|
if (onClassFinishedFn && currentWinId) onClassFinishedFn(meetingId);
|
|
227
229
|
};
|
|
228
|
-
const
|
|
230
|
+
const onHtmlReady = (event, value) => {
|
|
229
231
|
currentWebContentsId = value?.webContentId;
|
|
230
232
|
logger.info(`课堂窗口 html ready,web contents id:${currentWebContentsId}`);
|
|
231
233
|
|
|
232
234
|
if (currentWebContentsId) {
|
|
233
|
-
ipcRenderer.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.
|
|
235
|
+
ipcRenderer.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.INIT_GLOBAL_APPINFO, classOptionsObj);
|
|
234
236
|
}
|
|
235
237
|
};
|
|
236
238
|
const onSaveBoard = (event, value) => {
|
|
@@ -258,12 +260,22 @@ function createClassWindow(classWindowProps) {
|
|
|
258
260
|
}
|
|
259
261
|
};
|
|
260
262
|
|
|
263
|
+
const onGetPreParseFileName = async (event, requestId, ...args) => {
|
|
264
|
+
if (onGetPreParseFileNameFn && args.length > 0) {
|
|
265
|
+
const url = await onGetPreParseFileNameFn(...args);
|
|
266
|
+
if (url) {
|
|
267
|
+
ipcRenderer.sendTo(currentWebContentsId, CLASS_WINDOW_MESG_TYPE.RESP_GET_PRE_PARSE_FILE_NAME, requestId, url);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
261
272
|
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.LIVE_WINDOW_READY, onClassWindowReady);
|
|
262
273
|
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.ON_CLASS_FINISHED, onClassFinished);
|
|
263
|
-
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.
|
|
274
|
+
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.HTML_READY, onHtmlReady);
|
|
264
275
|
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.SAVE_BOARD, onSaveBoard);
|
|
265
276
|
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.OPEN_RESOURCE_CENTER, onOpenResourceCenter);
|
|
266
277
|
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.GET_EXT_FILE_NAME, onGetExtFileName);
|
|
278
|
+
ipcRenderer.on(CLASS_WINDOW_MESG_TYPE.GET_PRE_PARSE_FILE_NAME, onGetPreParseFileName);
|
|
267
279
|
|
|
268
280
|
const url = require('url');
|
|
269
281
|
const htmlPath =
|
|
@@ -293,10 +305,11 @@ function createClassWindow(classWindowProps) {
|
|
|
293
305
|
currentWebContentsId = null;
|
|
294
306
|
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.LIVE_WINDOW_READY, onClassWindowReady);
|
|
295
307
|
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.ON_CLASS_FINISHED, onClassFinished);
|
|
296
|
-
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.
|
|
308
|
+
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.HTML_READY, onHtmlReady);
|
|
297
309
|
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.SAVE_BOARD, onSaveBoard);
|
|
298
310
|
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.OPEN_RESOURCE_CENTER, onOpenResourceCenter);
|
|
299
311
|
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.GET_EXT_FILE_NAME, onGetExtFileName);
|
|
312
|
+
ipcRenderer.removeListener(CLASS_WINDOW_MESG_TYPE.GET_PRE_PARSE_FILE_NAME, onGetPreParseFileName);
|
|
300
313
|
});
|
|
301
314
|
},
|
|
302
315
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plasosdk/plaso-electron-sdk",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.8-beta.1",
|
|
4
4
|
"description": "伯索课堂Electron SDK",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
"electron",
|
|
15
15
|
"classroom"
|
|
16
16
|
],
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@electron/remote": "^2.1.2"
|
|
19
|
+
},
|
|
17
20
|
"dependencies": {
|
|
18
21
|
"@plasosdk/screenshot": "^1.3.4",
|
|
19
22
|
"@plasosdk/winproxy": "^1.1.5",
|
|
@@ -94,10 +94,10 @@ const npmInstall = () => {
|
|
|
94
94
|
installCb(agora_electron_sdk_v4, installAgora, error, stdout, stderr);
|
|
95
95
|
if (!error?.message) {
|
|
96
96
|
const oldAgoraV4Path = path.join(__dirname, '../node_modules', 'agora-electron-sdk');
|
|
97
|
-
const newAgoraV4Path = path.join(__dirname, '
|
|
97
|
+
const newAgoraV4Path = path.join(__dirname, '../node_modules', 'agora-electron-sdk-v4');
|
|
98
98
|
fs.move(oldAgoraV4Path, newAgoraV4Path, { overwrite: true }, (err) => {
|
|
99
99
|
if (!err) {
|
|
100
|
-
logger.info('
|
|
100
|
+
logger.info('命名agora_electron_sdk_v4成功');
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
103
|
}
|