├── .gitignore
├── ExtensionDemo
├── .gitignore
├── README.md
├── babel.config.json
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── audio-extension.ts
│ ├── index.ts
│ └── video-extension.ts
├── tsconfig.json
└── webpack.config.js
├── README.cn.md
├── README.md
├── package.json
├── scripts
├── pure.js
└── server.js
├── src
├── CHANGELOG.md
├── assets
│ ├── agora-logo-en.png
│ ├── agora-logo-zh.png
│ ├── bootstrap.bundle.min.js
│ ├── bootstrap.min.css
│ ├── favicon.ico
│ ├── github.png
│ └── jquery-3.4.1.min.js
├── common
│ ├── common.css
│ ├── constant.js
│ ├── left-menu.js
│ └── utils.js
├── example
│ ├── advanced
│ │ ├── adjustVideoProfile
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── audioEffects
│ │ │ ├── HeroicAdventure.mp3
│ │ │ ├── audio.mp3
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── customVideoSource
│ │ │ ├── assets
│ │ │ │ └── sample.mp4
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── displayCallStats
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── geoFencing
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── joinMultipleChannel
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── screenshot
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── selfCapturing
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── selfRendering
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── shareTheScreen
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── testMediaDevices
│ │ │ ├── index.html
│ │ │ ├── index.js
│ │ │ ├── jquery.jsonview.min.css
│ │ │ └── jquery.jsonview.min.js
│ │ └── vadExtention
│ │ │ ├── agora-extension-vad
│ │ │ ├── README.md
│ │ │ ├── index.d.ts
│ │ │ ├── index.esm.js
│ │ │ ├── index.js
│ │ │ ├── package.json
│ │ │ └── wasm
│ │ │ │ ├── web-vad.wasm
│ │ │ │ └── web-vad_simd.wasm
│ │ │ ├── chart.js
│ │ │ ├── index.css
│ │ │ ├── index.html
│ │ │ └── index.js
│ ├── basic
│ │ ├── basicLive
│ │ │ ├── assets
│ │ │ │ └── doubt.png
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── basicVideoCall
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ └── basicVoiceCall
│ │ │ ├── index.html
│ │ │ └── index.js
│ ├── extension
│ │ ├── aiDenoiser
│ │ │ ├── agora-extension-ai-denoiser
│ │ │ │ ├── README.md
│ │ │ │ ├── external
│ │ │ │ │ ├── denoiser-wasm-simd.wasm
│ │ │ │ │ └── denoiser-wasm.wasm
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.esm.js
│ │ │ │ ├── index.js
│ │ │ │ └── package.json
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── beauty
│ │ │ ├── agora-extension-beauty.js
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── spatialAudio
│ │ │ ├── audio_spatializer.wasm
│ │ │ ├── index.esm.js
│ │ │ ├── index.html
│ │ │ ├── index.js
│ │ │ ├── resources
│ │ │ │ ├── 1.mp3
│ │ │ │ ├── 2.mp3
│ │ │ │ └── 3.mp3
│ │ │ └── spatial-audio-worker.js
│ │ ├── superClarity
│ │ │ ├── agora-extension-pvc.js
│ │ │ ├── agora-extension-super-clarity.js
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── vad
│ │ │ ├── agora-extension-vad
│ │ │ │ ├── README.md
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index.esm.js
│ │ │ │ ├── index.js
│ │ │ │ ├── package.json
│ │ │ │ └── wasm
│ │ │ │ │ ├── web-vad.wasm
│ │ │ │ │ └── web-vad_simd.wasm
│ │ │ ├── assets
│ │ │ │ └── chart.js
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ ├── videoCompositor
│ │ │ ├── agora-extension-video-compositor
│ │ │ │ ├── LICENSE.txt
│ │ │ │ ├── agora-extension-video-compositor.js
│ │ │ │ ├── index.d.ts
│ │ │ │ ├── index_cn.d.ts
│ │ │ │ ├── index_en.d.ts
│ │ │ │ └── package.json
│ │ │ ├── assets
│ │ │ │ ├── city.jpg
│ │ │ │ └── space.jpg
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ └── virtualBackground
│ │ │ ├── agora-extension-virtual-background
│ │ │ ├── agora-extension-virtual-background.js
│ │ │ ├── index.d.ts
│ │ │ ├── index_cn.d.ts
│ │ │ ├── index_en.d.ts
│ │ │ ├── package.json
│ │ │ └── wasms
│ │ │ │ └── agora-wasm.wasm
│ │ │ ├── index.html
│ │ │ └── index.js
│ ├── framework
│ │ ├── react
│ │ │ ├── assets
│ │ │ │ ├── babel.min.js
│ │ │ │ ├── react-dom.production.min.js
│ │ │ │ └── react.production.min.js
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ └── vue
│ │ │ ├── assets
│ │ │ └── vue.min.js
│ │ │ ├── index.html
│ │ │ └── index.js
│ ├── others
│ │ ├── dualStream
│ │ │ ├── index.html
│ │ │ └── index.js
│ │ └── pushStreamToCDN
│ │ │ ├── index.html
│ │ │ └── index.js
│ └── quickStart
│ │ └── videoAndVoiceCalling
│ │ ├── index.html
│ │ └── index.js
├── i18n
│ ├── en
│ │ └── index.json
│ ├── language.js
│ └── zh-CN
│ │ └── index.json
├── index.html
└── index.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | dist
4 | dist-ssr
5 | *.local
6 | .vercel
7 |
--------------------------------------------------------------------------------
/ExtensionDemo/.gitignore:
--------------------------------------------------------------------------------
1 | dist/
2 | node_modules/
3 | .code/
--------------------------------------------------------------------------------
/ExtensionDemo/README.md:
--------------------------------------------------------------------------------
1 | # Agora-RTE-Extension Demo
2 |
3 | A simple demo for how to create video and audio extension for Agora Web SDK NG.
4 |
5 | Detailed documentation [here](https://docs.agora.io/en/extension_vendor/plugin_web_ng?platform=Web).
6 |
7 | ## Install Dependencies
8 |
9 | ```shell
10 | npm install
11 | ```
12 |
13 | ## Build
14 |
15 | ```shell
16 | npm run build
17 | ```
18 |
19 | ## Run the demo
20 |
21 | ```shell
22 | npm run demo
23 | ```
24 |
--------------------------------------------------------------------------------
/ExtensionDemo/babel.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["@babel/preset-typescript", "@babel/preset-env"]
3 | }
4 |
--------------------------------------------------------------------------------
/ExtensionDemo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 | Document
11 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/ExtensionDemo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "",
3 | "main": "dist/index.js",
4 | "scripts": {
5 | "test": "echo \"Error: no test specified\" && exit 1",
6 | "build": "webpack",
7 | "demo": "live-server ."
8 | },
9 | "author": "",
10 | "license": "ISC",
11 | "dependencies": {
12 | "agora-rte-extension": "^1.0.23"
13 | },
14 | "devDependencies": {
15 | "@babel/core": "^7.16.7",
16 | "@babel/preset-env": "^7.16.11",
17 | "@babel/preset-typescript": "^7.16.7",
18 | "babel-loader": "^8.2.3",
19 | "typescript": "^4.6.3",
20 | "webpack": "^5.71.0",
21 | "webpack-cli": "^4.9.2",
22 | "live-server": "^1.2.1"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/ExtensionDemo/src/audio-extension.ts:
--------------------------------------------------------------------------------
1 | import { AudioExtension, AudioProcessor, IAudioProcessorContext } from "agora-rte-extension";
2 |
3 | class SimpleAudioExtension extends AudioExtension {
4 | protected _createProcessor(): SimpleAudioProcessor {
5 | return new SimpleAudioProcessor();
6 | }
7 | }
8 |
9 | class SimpleAudioProcessor extends AudioProcessor {
10 | public name: string = "SimpleAudioProcessor";
11 | private gainNode?: GainNode;
12 | private oscillatorNode?: OscillatorNode;
13 |
14 | protected onPiped(context: IAudioProcessorContext): void {
15 | const audioContext = context.getAudioContext();
16 | const gainNode = audioContext.createGain();
17 | const oscillatorNode = audioContext.createOscillator();
18 | oscillatorNode.frequency.setValueAtTime(880, audioContext.currentTime);
19 | oscillatorNode.start();
20 | oscillatorNode.connect(gainNode);
21 |
22 | this.gainNode = gainNode;
23 | this.oscillatorNode = oscillatorNode;
24 | }
25 |
26 | protected onUnpiped() {
27 | this.gainNode = undefined;
28 | this.oscillatorNode?.stop();
29 | this.oscillatorNode = undefined;
30 | }
31 |
32 | protected onEnableChange(enabled: boolean): void | Promise {
33 | if (this.context && this.oscillatorNode && this.gainNode) {
34 | if (enabled) {
35 | this.oscillatorNode.connect(this.gainNode);
36 | } else {
37 | this.oscillatorNode.disconnect();
38 | }
39 | }
40 | }
41 |
42 | protected onNode(audioNode: AudioNode, context: IAudioProcessorContext): void | Promise {
43 | if (this.gainNode && this.oscillatorNode) {
44 | audioNode.connect(this.gainNode);
45 |
46 | this.output(this.gainNode, context);
47 | }
48 | }
49 | }
50 |
51 | export { SimpleAudioExtension };
52 |
--------------------------------------------------------------------------------
/ExtensionDemo/src/index.ts:
--------------------------------------------------------------------------------
1 | import { SimpleAudioExtension } from "./audio-extension";
2 | import { SimpleVideoExtension } from "./video-extension";
3 |
4 | export { SimpleAudioExtension, SimpleVideoExtension };
5 |
--------------------------------------------------------------------------------
/ExtensionDemo/src/video-extension.ts:
--------------------------------------------------------------------------------
1 | import { Extension, VideoProcessor, Ticker, IProcessorContext } from "agora-rte-extension";
2 |
3 | class SimpleVideoExtension extends Extension {
4 | protected _createProcessor(): SimpleVideoProcessor {
5 | return new SimpleVideoProcessor();
6 | }
7 | }
8 |
9 | class SimpleVideoProcessor extends VideoProcessor {
10 | public name: string = "SimpleVideoProcessor";
11 | private canvas: HTMLCanvasElement;
12 | private ctx: CanvasRenderingContext2D;
13 | private ticker: Ticker;
14 | private videoElement: HTMLVideoElement;
15 | private canvasTrack: MediaStreamTrack;
16 |
17 | public constructor() {
18 | super();
19 |
20 | this.canvas = document.createElement("canvas");
21 | this.canvas.width = 640;
22 | this.canvas.height = 480;
23 | this.ctx = this.canvas.getContext("2d")!;
24 | this.videoElement = document.createElement("video");
25 | this.videoElement.muted = true;
26 | const outputStream = this.canvas.captureStream(30);
27 | this.canvasTrack = outputStream.getVideoTracks()[0];
28 |
29 | this.ticker = new Ticker("RAF", 1000 / 30);
30 | this.ticker.add(this.process);
31 | }
32 |
33 | private process = () => {
34 | if (this.inputTrack) {
35 | this.ctx.drawImage(this.videoElement, 0, 0);
36 | this.ctx.fillStyle = "red";
37 | this.ctx.fillRect(0, 0, 100, 100);
38 | }
39 | };
40 |
41 | protected onEnableChange(enabled: boolean): void | Promise {
42 | if (!this.context) {
43 | return;
44 | }
45 |
46 | if (enabled) {
47 | this.ticker.start();
48 | this.output(this.canvasTrack, this.context);
49 | } else {
50 | this.ticker.stop();
51 | if (this.inputTrack) {
52 | this.output(this.inputTrack, this.context);
53 | }
54 | }
55 | }
56 |
57 | protected onTrack(track: MediaStreamTrack, ctx: IProcessorContext): void | Promise {
58 | this.videoElement.srcObject = new MediaStream([track]);
59 | this.videoElement.play();
60 | this.videoElement.onplaying = () => {
61 | this.canvas.width = this.videoElement.videoWidth;
62 | this.canvas.height = this.videoElement.videoHeight;
63 | };
64 |
65 | if (this.enabled) {
66 | this.ticker.start();
67 | this.output(this.canvasTrack, ctx);
68 | } else {
69 | this.ticker.stop();
70 | this.output(track, ctx);
71 | }
72 | }
73 |
74 | protected onUnpiped(): void {
75 | this.ticker.stop();
76 | }
77 | }
78 |
79 | export { SimpleVideoExtension };
80 |
--------------------------------------------------------------------------------
/ExtensionDemo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json.schemastore.org/tsconfig",
3 | "compilerOptions": {
4 | "moduleResolution": "node",
5 | "target": "es5",
6 | "module": "umd",
7 | "lib": ["es2015", "es2016", "es2017", "dom"],
8 | "strict": true,
9 | "strictNullChecks": true,
10 | "strictPropertyInitialization": true,
11 | "sourceMap": false,
12 | "declaration": true,
13 | "declarationMap": true,
14 | "allowJs": true,
15 | "emitDeclarationOnly": true,
16 | "allowSyntheticDefaultImports": true,
17 | "experimentalDecorators": true,
18 | "emitDecoratorMetadata": true,
19 | "esModuleInterop": true,
20 | "stripInternal": true,
21 | "isolatedModules": true,
22 | "outDir": "./dist",
23 | "typeRoots": ["node_modules/@types"]
24 | },
25 | "include": ["src/**/*"]
26 | }
27 |
--------------------------------------------------------------------------------
/ExtensionDemo/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const ROOT = __dirname;
3 |
4 | module.exports = {
5 | mode: "production",
6 | entry: path.resolve(ROOT, "src", "index.ts"),
7 | output: {
8 | path: path.resolve(ROOT, "dist"),
9 | filename: "index.js",
10 | library: {
11 | type: "umd",
12 | name: "SimpleExtension",
13 | },
14 | },
15 | module: {
16 | rules: [
17 | {
18 | test: /.ts$/,
19 | include: path.resolve(ROOT, "src"),
20 | exclude: /node_modules/,
21 | loader: "babel-loader",
22 | },
23 | ],
24 | },
25 | resolve: {
26 | extensions: [".ts", ".js"],
27 | },
28 | };
29 |
--------------------------------------------------------------------------------
/README.cn.md:
--------------------------------------------------------------------------------
1 | # Agora RTC Web SDK 4.x 示例项目
2 |
3 | _[English](README.md) | 简体中文_
4 |
5 | ## 简介
6 |
7 | 此仓库包含基于 Agora RTC Web SDK 4.x 的示例项目。
8 |
9 |
10 |
11 | Web SDK 4.x 是基于 Web SDK 3.x 开发的全量重构版本,在继承了 Web SDK 3.x 功能的基础上,优化了 SDK 的内部架构,提高了 API 的易用性。
12 |
13 | Web SDK 4.x 具有以下优势:
14 |
15 | - 面向开发者提供更先进的 API 架构和范式。
16 | - 所有异步场景的 API 使用 Promise 替代 Callback,提升集成代码的质量和健壮性。
17 | - 优化频道事件通知机制,统一频道内事件的命名和回调参数的格式,降低断线重连的处理难度。
18 | - 提供清晰和完善的错误码,方便错误排查。
19 | - 支持 TypeScript。
20 |
21 | ## 示例项目(使用 jQuery 和 Bootstrap)
22 |
23 | | 功能 | 示例项目位置 |
24 | | -------------- | ------------------------------------------------------------ |
25 | | 基础示例 | [/src/example/basic](/src/example/basic) |
26 | | 进阶示例 | [/src/example/advanced](/src/example/advanced) |
27 | | 插件示例 | [/src/example/plugin](/src/example/plugin) |
28 | | 其他示例 | [/src/example/others](/src/example/others) |
29 | | vue 框架示例 | [/src/example/framework/vue](/src/example/framework/vue) |
30 | | react 框架示例 | [/src/example/framework/react](/src/example/framework/react) |
31 |
32 | ### 如何运行示例项目
33 |
34 | #### 前提条件
35 |
36 | - 你必须使用 SDK 支持的浏览器运行示例项目。 关于支持的浏览器列表参考 [浏览器兼容性和已知问题](https://doc.shengwang.cn/doc/rtc/javascript/overview/browser-compatibility)。
37 |
38 | #### 运行步骤
39 |
40 | 1. 在项目根路径运行下面的命令安装依赖项。
41 |
42 | ```shell
43 | npm install
44 | ```
45 |
46 | 2. 运行下面的命令启动示例项目。
47 |
48 | ```shell
49 | npm run dev
50 | ```
51 |
52 | 3. 在浏览器打开 [http://localhost:3001/index.html](http://localhost:3001/index.html)
53 |
54 | 4. 在示例项目设置页面上,输入 App ID 和 App Certificate,然后点击设置按钮。
55 | - 关于 App ID 和 App Certificate 的获取方法参考 [开始使用 Agora 平台](https://docs.agora.io/cn/Agora%20Platform/get_appid_token)。
56 |
57 | ## 参考
58 |
59 | - [Web SDK 4.x 产品概述](https://doc.shengwang.cn/doc/rtc/javascript/overview/product-overview)
60 | - [Web SDK 4.x API 参考](https://doc.shengwang.cn/api-ref/rtc/javascript/overview)
61 | - [基于本仓库部署的在线 demo](https://webdemo.agora.io/)
62 |
63 | ## 反馈
64 |
65 | 如果你有任何问题或建议,可以通过 issue 的形式反馈。
66 |
67 | ## 相关资源
68 |
69 | - 你可以先参阅 [常见问题](https://doc.shengwang.cn/faq/list?category=integration-issues&platform=javascript&product=rtc)
70 | - 如果你想了解更多官方示例,可以参考 [官方 SDK 示例](https://github.com/AgoraIO)
71 | - 如果你想了解声网 SDK 在复杂场景下的应用,可以参考 [官方场景案例](https://github.com/AgoraIO-usecase)
72 | - 如果你想了解声网的一些社区开发者维护的项目,可以查看 [社区](https://github.com/AgoraIO-Community)
73 | - 若遇到问题需要开发者帮助,你可以到 [开发者社区](https://rtcdeveloper.com/) 提问
74 | - 如果需要售后技术支持, 你可以在 [Agora Dashboard](https://console.shengwang.cn/overview) 提交工单
75 |
76 | ## 许可证
77 |
78 | 示例项目遵守 MIT 许可证。
79 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sample projects for Agora RTC Web SDK 4.x
2 |
3 | _English | [简体中文](README.cn.md)_
4 |
5 | ## Overview
6 |
7 | This repository contains sample projects using the Agora RTC Web SDK 4.x.
8 |
9 |
10 |
11 | The Web SDK 4.x refactors the Web SDK 3.x. Based on the features of 3.x, 4.x fully optimizes the internal architecture of the SDK and provides more flexible and easy-to-use APIs.
12 |
13 | Compared to the Web SDK 3.x, the Web SDK 4.x has the following advantages:
14 |
15 | - Uses promises for asynchronous operations, which improves the robustness and readability of your code.
16 | - Supports TypeScript.
17 | - Replaces the Stream object with Track objects for separate and flexible control over audio and video.
18 | - Improves the channel event notification mechanism, making it easier for you to deal with reconnection.
19 | - Provides more accurate and comprehensive error codes for troubleshooting.
20 |
21 | ## Projects using jQuery and Bootstrap
22 |
23 | | Feature | Sample project location |
24 | | ----------------------- | ------------------------------------------------------------ |
25 | | Basic Examples | [/src/example/basic](/src/example/basic) |
26 | | Advanced Examples | [/src/example/advanced](/src/example/advanced) |
27 | | Plugin Examples | [/src/example/plugin](/src/example/plugin) |
28 | | Other Examples | [/src/example/others](/src/example/others) |
29 | | Vue Framework Example | [/src/example/framework/vue](/src/example/framework/vue) |
30 | | React Framework Example | [/src/example/framework/react](/src/example/framework/react) |
31 |
32 | ### How to run the sample projects
33 |
34 | #### Prerequisites
35 |
36 | You need a supported browser to run the sample projects. See [Product Overview](https://docs.agora.io/en/Interactive%20Broadcast/product_live?platform=Web#compatibility) for a list of supported browsers.
37 |
38 | #### Steps to run
39 |
40 | 1. In the project root path run the following command to install dependencies.
41 |
42 | ```shell
43 | npm install
44 | ```
45 |
46 | 2. Use the following command to run the sample project.
47 |
48 | ```shell
49 | npm run dev
50 | ```
51 |
52 | 3. Open link [http://localhost:3001/index.html](http://localhost:3001/index.html) in browser.
53 |
54 | 4. In the demo setting page, enter your App ID and App Certificate, then click SetUp button.
55 | - See [Get Started with Agora](https://docs.agora.io/en/Agora%20Platform/get_appid_token) to learn how to get an App ID and App Certificate.
56 |
57 | ## Reference
58 |
59 | - [Web SDK 4.x Product Overview](https://docs.agora.io/en/Interactive%20Broadcast/product_live?platform=Web)
60 | - [Web SDK 4.x API Reference](https://docs.agora.io/en/Interactive%20Broadcast/API%20Reference/web_ng/index.html)
61 | - [Online demo deployed from this repo](https://webdemo.agora.io/)
62 |
63 | ## Feedback
64 |
65 | If you have any problems or suggestions regarding the sample projects, feel free to file an issue.
66 |
67 | ## Related resources
68 |
69 | - Check our [FAQ](https://docs.agora.io/en/faq) to see if your issue has been recorded.
70 | - Dive into [Agora SDK Samples](https://github.com/AgoraIO) to see more tutorials
71 | - Take a look at [Agora Use Case](https://github.com/AgoraIO-usecase) for more complicated real use case
72 | - Repositories managed by developer communities can be found at [Agora Community](https://github.com/AgoraIO-Community)
73 | - If you encounter problems during integration, feel free to ask questions in [Stack Overflow](https://stackoverflow.com/questions/tagged/agora.io)
74 |
75 | ## License
76 |
77 | The sample projects are under the MIT license.
78 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api-examples-web",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "node ./scripts/server.js",
8 | "pure": "node ./scripts/pure.js",
9 | "lint": "prettier . --write --ignore-unknown"
10 | },
11 | "devDependencies": {
12 | "express": "^4.18.2",
13 | "prettier": "^3.3.3"
14 | },
15 | "author": "qz",
16 | "license": "ISC",
17 | "packageManager": "yarn@1.22.22"
18 | }
19 |
--------------------------------------------------------------------------------
/scripts/pure.js:
--------------------------------------------------------------------------------
1 | // TIP: This script is used to purge all SSO-related code from the project.
2 | // This file does not require attention.
3 |
4 | const { readdirSync, existsSync, readFileSync, writeFileSync, statSync } = require("fs");
5 | const path = require("path");
6 | const { exec } = require("child_process");
7 |
8 | const TARGET_DIR = path.resolve(__dirname, "../src");
9 | const REG_SSO_SCRIPT = /^\s+\ from html file
81 | const removeSSOScriptTag = (htmlPath) => {
82 | console.log(htmlPath);
83 | let htmlContent = readFileSync(htmlPath, "utf-8");
84 | htmlContent = htmlContent.replace(REG_SSO_SCRIPT, "");
85 | writeFileSync(htmlPath, htmlContent);
86 | };
87 |
88 | const removeSSOCode = (jsPath) => {
89 | console.log(jsPath);
90 | let jsContent = readFileSync(jsPath, "utf-8");
91 | jsContent = jsContent.replace(REG_SSO_CODE, "");
92 | writeFileSync(jsPath, jsContent);
93 | };
94 |
95 | function run() {
96 | console.log("----- pure run start -----");
97 | delSSORelatedFile();
98 | delSSORelatedHTMLCode();
99 | delSSORelatedJSCode();
100 | console.log("----- pure run end -----");
101 | }
102 |
103 | run();
104 |
--------------------------------------------------------------------------------
/scripts/server.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const path = require("path");
3 |
4 | // change the port if necessary
5 | const PORT = 3001;
6 | const URL = `http://localhost:${PORT}/index.html`;
7 |
8 | const dir = path.join(__dirname, "../src");
9 | const app = express();
10 | app.use(express.static(dir));
11 |
12 | app.listen(PORT, () => {
13 | console.info(`\n---------------------------------------\n`);
14 | console.info(`please visit: ${URL}`);
15 | console.info(`\n---------------------------------------\n`);
16 | });
17 |
--------------------------------------------------------------------------------
/src/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [2.1.0] - 2024-11-28
4 |
5 | 1. Address the issue of screen sharing being unavailable in Safari.
6 | 2. Disable the autoplay of custom videos in the mobile version of Safari.
7 | 3. Enable the selection of MP3 files on iOS devices for audio effect testing.
8 | 4. Rectify the problem of the device still capturing from the camera and microphone when not in use.
9 | 5. Resolve the inability to adjust audio sound effects on mobile devices.
10 | 6. Replace VAD with Voice Activity Detection.
11 | 7. Introduce a dropdown menu for selecting to subscribe to remote users.
12 | 8. Add a direct link to GitHub for convenient access.
13 | 9. Support the addition of dynamic tokens for channel entry.
14 |
15 | ## [2.1.0] - 2024-12-30
16 |
17 | 1. Add translate
18 | 2. Add rule verification for appid
19 | 3. Enter appid to save automatically
20 | 4. Corrected some boot links
21 |
--------------------------------------------------------------------------------
/src/assets/agora-logo-en.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/92692ec78a3b53c2398b6e6508e4bf86b9d4ca54/src/assets/agora-logo-en.png
--------------------------------------------------------------------------------
/src/assets/agora-logo-zh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/92692ec78a3b53c2398b6e6508e4bf86b9d4ca54/src/assets/agora-logo-zh.png
--------------------------------------------------------------------------------
/src/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/92692ec78a3b53c2398b6e6508e4bf86b9d4ca54/src/assets/favicon.ico
--------------------------------------------------------------------------------
/src/assets/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AgoraIO/API-Examples-Web/92692ec78a3b53c2398b6e6508e4bf86b9d4ca54/src/assets/github.png
--------------------------------------------------------------------------------
/src/common/left-menu.js:
--------------------------------------------------------------------------------
1 | const DEFAULT_ZH_DOC_URL = "https://doc.shengwang.cn/doc/rtc/javascript/get-started/enable-service";
2 | const DEFAULT_EN_DOC_URL =
3 | "https://docs.agora.io/en/video-calling/get-started/authentication-workflow?platform=web";
4 |
5 | function __isSmallScreen() {
6 | const SM_WIDTH = 576; // small screen width (phone)
7 | const windowWidth = $(window).width();
8 | return windowWidth <= SM_WIDTH;
9 | }
10 |
11 | function __transListToMenuDom(list) {
12 | return list
13 | .map((item) => {
14 | return ``;
31 | })
32 | .join("");
33 | }
34 |
35 | function __initMenu() {
36 | let language = getLanguage();
37 | let href = "";
38 | let logoImgSrc = "";
39 | if (language == "en") {
40 | href = DEFAULT_EN_DOC_URL;
41 | logoImgSrc = `${ORIGIN_URL}/assets/agora-logo-en.png`;
42 | } else {
43 | href = DEFAULT_ZH_DOC_URL;
44 | logoImgSrc = `${ORIGIN_URL}/assets/agora-logo-zh.png`;
45 | }
46 | let logoHtml = ` `;
47 | let menuHtml = `
48 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
--------------------------------------------------------------------------------
/src/example/framework/react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Use React -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/example/others/dualStream/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Dual Stream -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/src/example/others/dualStream/index.js:
--------------------------------------------------------------------------------
1 | // create Agora client
2 | var client = AgoraRTC.createClient({
3 | mode: "rtc",
4 | codec: "vp8",
5 | });
6 | AgoraRTC.enableLogUpload();
7 | var localTracks = {
8 | videoTrack: null,
9 | audioTrack: null,
10 | };
11 | var remoteUsers = {};
12 | // Agora client options
13 | var options = getOptionsFromLocal();
14 |
15 | $("body").on("click", ".player", async function (e) {
16 | e.preventDefault();
17 | const player = e.target.parentElement.parentElement;
18 | const uid = Number(player.dataset.uid);
19 |
20 | try {
21 | // Set user clicked to HQ/LQ Stream
22 | await changeSomeUserStream(uid);
23 | } catch (error) {
24 | console.error(error);
25 | message.error(error.message);
26 | }
27 | });
28 |
29 | $("#join-form").submit(async function (e) {
30 | e.preventDefault();
31 | $("#join").attr("disabled", true);
32 | try {
33 | options.channel = $("#channel").val();
34 | options.uid = Number($("#uid").val());
35 | options.token = await agoraGetAppData(options);
36 | await join();
37 | setOptionsToLocal(options);
38 | message.success("join channel success!");
39 | } catch (error) {
40 | console.error(error);
41 | message.error(error.message);
42 | } finally {
43 | $("#leave").attr("disabled", false);
44 | }
45 | });
46 |
47 | $("#leave").click(function (e) {
48 | leave();
49 | });
50 |
51 | async function join() {
52 | // add event listener to play remote tracks when remote user publishs.
53 | client.on("user-published", handleUserPublished);
54 | client.on("user-unpublished", handleUserUnpublished);
55 |
56 | // start Proxy if needed
57 | const mode = Number(options.proxyMode);
58 | if (mode != 0 && !isNaN(mode)) {
59 | client.startProxyServer(mode);
60 | }
61 |
62 | // Customize the video profile of the low-quality stream: 160 × 120, 15 fps, 120 Kbps.
63 | client.setLowStreamParameter({
64 | width: 160,
65 | height: 120,
66 | framerate: 15,
67 | bitrate: 120,
68 | });
69 |
70 | // Enable dual-stream mode.
71 | await client.enableDualStream();
72 |
73 | // join a channel and create local tracks, we can use Promise.all to run them concurrently
74 | [options.uid, localTracks.audioTrack, localTracks.videoTrack] = await Promise.all([
75 | // join the channel
76 | client.join(options.appid, options.channel, options.token || null, options.uid || null),
77 | // create local tracks, using microphone and camera
78 | AgoraRTC.createMicrophoneAudioTrack({
79 | encoderConfig: "music_standard",
80 | }),
81 | AgoraRTC.createCameraVideoTrack(),
82 | ]);
83 |
84 | // play local video track
85 | localTracks.videoTrack.play("local-player");
86 | $("#local-player-name").text(`uid: ${options.uid}`);
87 |
88 | // publish local tracks to channel
89 | await client.publish(Object.values(localTracks));
90 | console.log("publish success");
91 | }
92 |
93 | async function changeSomeUserStream(uid) {
94 | const streamType = $(`#stream-type-${uid}`).text();
95 | if (streamType == "HQ Stream") {
96 | // Set the remote user to the low-quality video stream.
97 | await client.setRemoteVideoStreamType(uid, 1);
98 | $(`#stream-type-${uid}`).text("LQ Stream");
99 | } else if (streamType == "LQ Stream") {
100 | // Set the remote user to the high-quality video stream.
101 | await client.setRemoteVideoStreamType(uid, 0);
102 | $(`#stream-type-${uid}`).text("HQ Stream");
103 | }
104 | }
105 |
106 | async function leave() {
107 | for (trackName in localTracks) {
108 | var track = localTracks[trackName];
109 | if (track) {
110 | track.stop();
111 | track.close();
112 | localTracks[trackName] = undefined;
113 | }
114 | }
115 |
116 | // remove remote users and player views
117 | remoteUsers = {};
118 | $("#remote-playerlist").html("");
119 |
120 | // disable dual-stream mode
121 | await client.disableDualStream();
122 |
123 | // leave the channel
124 | await client.leave();
125 | $("#local-player-name").text("");
126 | $("#join").attr("disabled", false);
127 | $("#leave").attr("disabled", true);
128 | console.log("client leaves channel success");
129 | }
130 |
131 | async function subscribe(user, mediaType) {
132 | const uid = user.uid;
133 | // subscribe to a remote user
134 | await client.subscribe(user, mediaType);
135 | console.log("subscribe success");
136 | if (mediaType === "video") {
137 | const player = $(`
138 |
139 |
140 |
uid: ${uid}
141 | HQ Stream
142 |
143 |
144 |
145 | `);
146 | $("#remote-playerlist").append(player);
147 | user.videoTrack.play(`player-${uid}`);
148 | }
149 | if (mediaType === "audio") {
150 | user.audioTrack.play();
151 | }
152 | }
153 |
154 | function handleUserPublished(user, mediaType) {
155 | const id = user.uid;
156 | remoteUsers[id] = user;
157 | subscribe(user, mediaType);
158 | }
159 |
160 | function handleUserUnpublished(user, mediaType) {
161 | if (mediaType === "video") {
162 | const id = user.uid;
163 | delete remoteUsers[id];
164 | $(`#player-wrapper-${id}`).remove();
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/example/others/pushStreamToCDN/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Push Stream To CDN -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/src/example/quickStart/videoAndVoiceCalling/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Basic Live -- Agora
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
38 |
39 |
40 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/src/i18n/en/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "settingMenu": "Setting",
3 | "quickMenu": "Quick Start",
4 | "basicMenu": "Basic Examples",
5 | "videoAndVoiceCalling": "Video and Voice Calling",
6 | "interactiveLiveStreamingStandard":"Interactive Live Streaming Standard",
7 | "interactiveLiveStreamingPremium":"Interactive Live Streaming Premium",
8 | "advancedMenu": "Advanced Examples",
9 | "othersMenu": "Other Examples",
10 | "extensionMenu": "Plugin Examples",
11 | "frameworkMenu": "Framework Examples",
12 | "initializeSettings": "Initialize Settings",
13 | "basicVoiceCall": "Basic Voice Calling",
14 | "basicVideoCall": "Basic Video Calling",
15 | "basicLive": "Basic Live Streaming",
16 | "shareTheScreen": "Screen Share",
17 | "testMediaDevices": "Test/Switch Media Devices",
18 | "adjustVideoProfile": "Adjust Video Profile",
19 | "displayCallStats": "Display In-call Statistics",
20 | "audioEffects": "Audio Effects",
21 | "joinMultipleChannel": "Join Multiple Channels",
22 | "customVideoSource": "Custom Video Source",
23 | "selfRendering": "Video Self-Rendering",
24 | "selfCapturing": "Video Self-Capturing",
25 | "screenshot": "Video Screenshot",
26 | "geoFencing": "Network Geofencing",
27 | "pushStreamToCDN": "Push Streams to CDN",
28 | "dualStream": "Enable Dual-stream Mode",
29 | "virtualBackground": "Virtual Background",
30 | "beauty": "Beauty",
31 | "aiDenoiser": "AI Denoiser",
32 | "superClarity": "Super Clarity",
33 | "spatialAudio": "Spatial Audio",
34 | "vad": "Voice Activity Detection",
35 | "videoCompositor": "Local Video Compositing",
36 | "vue": "Vue",
37 | "react": "React",
38 | "setting": "SetUp",
39 | "cloudProxy": "Cloud Proxy",
40 | "certificate": "APP Certificate(optional)",
41 | "language": "Language",
42 | "agora": "Agora",
43 | "clickChange": "Click to change",
44 | "needHelp": "If you need help, please visit the official website",
45 | "agoraDocs": "Agora Docs",
46 | "sampleCode": "Sample Code",
47 | "stt": "Speech To Text",
48 | "autoPlayFailed": " The autoplay has failed; please click to initiate playback.",
49 | "appIdTips": "If you don`t know what is your appid, checkout this ",
50 | "appCertificateTips": " If you don`t know what is your app certificate, checkout this ",
51 | "cloudProxyTips": "If you don`t know what is Cloud Proxy, checkout this ",
52 | "chooseCustom": "Choose Custom Option to manually enter the project's App ID and App Certificate (optional).",
53 | "and": "and"
54 | }
--------------------------------------------------------------------------------
/src/i18n/language.js:
--------------------------------------------------------------------------------
1 | let LANGUAGE_CACHE_KEY = "__language_data__";
2 | let LANGUAGE_CACHE_DATA = JSON.parse(sessionStorage.getItem(LANGUAGE_CACHE_KEY)) || {};
3 |
4 | function __insertIe8nText(data) {
5 | var insertEle = $(".i18n");
6 | insertEle.each(function () {
7 | let dataName = $(this).attr("name");
8 | let i18nTxt = data[dataName];
9 | $(this).html(i18nTxt);
10 | });
11 | }
12 |
13 | function __getLanguageData(language, success, fail) {
14 | let url = "";
15 | if (LANGUAGE_CACHE_DATA[language] && LANGUAGE_CACHE_DATA[language].sampleCode) {
16 | success(LANGUAGE_CACHE_DATA[language]);
17 | return;
18 | }
19 |
20 | switch (language) {
21 | case "zh":
22 | url = `${ORIGIN_URL}/i18n/zh-CN/index.json`;
23 | break;
24 | case "zh-CN":
25 | url = `${ORIGIN_URL}/i18n/zh-CN/index.json`;
26 | break;
27 | case "en":
28 | url = `${ORIGIN_URL}/i18n/en/index.json`;
29 | break;
30 | default:
31 | url = `${ORIGIN_URL}/i18n/en/index.json`;
32 | }
33 |
34 | $.ajax({
35 | url: url,
36 | async: true,
37 | cache: false,
38 | dataType: "json",
39 | success: success,
40 | error: fail,
41 | });
42 | }
43 |
44 | function __execI18n() {
45 | const language = getLanguage();
46 |
47 | const success = function (data, status) {
48 | LANGUAGE_CACHE_DATA[language] = data;
49 | sessionStorage.setItem(LANGUAGE_CACHE_KEY, JSON.stringify(LANGUAGE_CACHE_DATA));
50 | __insertIe8nText(data);
51 | };
52 |
53 | const fail = function (jqXHR, textStatus, errorThrown) {
54 | let msg = "get language data failed!";
55 | let href = window.location.href;
56 | if (/^file/.test(href)) {
57 | msg += "can not get language data from local file, please use http server!";
58 | }
59 | console.error(msg, jqXHR, textStatus);
60 | };
61 |
62 | __getLanguageData(language, success, fail);
63 | }
64 |
65 | function __insertLanguageSwitch() {
66 | const languageSwitchHtml = `
67 |
68 |
69 |
70 |
71 | English
72 | 简体中文
73 |
74 |
`;
75 |
76 | $("#language-switch-wrapper").html(languageSwitchHtml);
77 |
78 | const language = getLanguage();
79 |
80 | if (language == "en") {
81 | $("#language-english").addClass("active");
82 | $("#language-text").text("English");
83 | } else {
84 | $("#language-zh").addClass("active");
85 | $("#language-text").text("简体中文");
86 | }
87 |
88 | function switchLanguage(v) {
89 | if (language == v) {
90 | return;
91 | }
92 | let options = getOptionsFromLocal();
93 | if (v == "zh-CN" || v == "zh") {
94 | $("#language-zh").addClass("active");
95 | $("#language-english").removeClass("active");
96 | $("#language-text").text("简体中文");
97 | options.language = "zh-CN";
98 | setOptionsToLocal(options);
99 | } else {
100 | $("#language-english").addClass("active");
101 | $("#language-zh").removeClass("active");
102 | $("#language-text").text("English");
103 | options.language = "en";
104 | setOptionsToLocal(options);
105 | }
106 | window.location.reload();
107 | }
108 |
109 | $("#language-english").click(function (e) {
110 | switchLanguage("en");
111 | });
112 |
113 | $("#language-zh").click(function (e) {
114 | switchLanguage("zh-CN");
115 | });
116 | }
117 |
118 | window.addEventListener("pageshow", function (e) {
119 | __insertLanguageSwitch();
120 | __execI18n();
121 | });
122 |
--------------------------------------------------------------------------------
/src/i18n/zh-CN/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "settingMenu": "设置",
3 | "quickMenu": "快速开始",
4 | "basicMenu": "基础示例",
5 | "videoAndVoiceCalling": "音视频通话",
6 | "interactiveLiveStreamingStandard":"极速直播",
7 | "interactiveLiveStreamingPremium":"互动直播",
8 | "advancedMenu": "进阶示例",
9 | "othersMenu": "其他示例",
10 | "extensionMenu": "插件示例",
11 | "frameworkMenu": "框架示例",
12 | "initializeSettings": "初始化设置",
13 | "basicVoiceCall": "基础语音通话",
14 | "basicVideoCall": "基础视频通话",
15 | "basicLive": "基础视频直播",
16 | "shareTheScreen": "屏幕共享",
17 | "testMediaDevices": "音视频设备检测",
18 | "adjustVideoProfile": "视频编码属性",
19 | "displayCallStats": "通话中质量检测",
20 | "audioEffects": "音频音效",
21 | "joinMultipleChannel": "加入多频道",
22 | "customVideoSource": "自定义视频源",
23 | "selfRendering": "视频自渲染",
24 | "selfCapturing": "视频自采集",
25 | "screenshot": "视频截图",
26 | "geoFencing": "区域访问限制",
27 | "pushStreamToCDN": "推流到CDN",
28 | "dualStream": "大小流",
29 | "virtualBackground": "虚拟背景",
30 | "beauty": "美颜",
31 | "aiDenoiser": "AI降噪",
32 | "superClarity": "AI画质",
33 | "spatialAudio": "空间音频",
34 | "vad": "语音活动检测",
35 | "videoCompositor": "本地合图",
36 | "vue": "使用Vue",
37 | "react": "使用React",
38 | "setting": "设置",
39 | "cloudProxy": "云代理",
40 | "certificate": "App 证书 (可选)",
41 | "language": "语言",
42 | "agora": "声网",
43 | "clickChange": "点击修改",
44 | "needHelp": "需要帮助请跳转至",
45 | "agoraDocs": "声网文档",
46 | "stt": "语音转文字",
47 | "autoPlayFailed": "自动播放失败,点击即可播放。",
48 | "appIdTips": "点击这里 了解App ID",
49 | "appCertificateTips": "点击这里 了解App 证书",
50 | "cloudProxyTips": "点击这里 了解云代理",
51 | "chooseCustom": "选择自定义,可手动填写项目的 App ID 和 App 证书(如有)。",
52 | "sampleCode": "示例代码",
53 | "and": "和"
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Agora WebSDK Demos
10 |
11 |
12 |
13 |
14 |
15 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | let options = getOptionsFromLocal();
2 | let modeList = [
3 | {
4 | label: "Off",
5 | detail: "Disable Cloud Proxy",
6 | value: "0",
7 | },
8 | {
9 | label: "UDP Mode",
10 | detail: "Enable Cloud Proxy via UDP protocol",
11 | value: "3",
12 | },
13 | {
14 | label: "TCP Mode",
15 | detail: "Enable Cloud Proxy via TCP/TLS port 443",
16 | value: "5",
17 | },
18 | ];
19 | let proxyModeItem;
20 | let projectList = [];
21 |
22 | $(() => {
23 | initVersion();
24 | initModes();
25 | initDocUrl();
26 | });
27 |
28 |
29 |
30 | const saveConfig = () => {
31 | options.appid = escapeHTML($("#appid").val().trim());
32 | options.certificate = escapeHTML($("#certificate").val().trim());
33 | options.proxyMode = proxyModeItem.value;
34 | setOptionsToLocal(options);
35 | }
36 |
37 |
38 |
39 | const checkAppId = () => {
40 | const projectAppIdType = $("#project-id-select").val();
41 | if(projectAppIdType !== 'custom_settings'){
42 | return true
43 | }
44 | // check appid
45 | const appId = $("#appid").val().trim();
46 | if (!appId) {
47 | alert("Need to set up appID and appCertificate!");
48 | return;
49 | }
50 | const isValidate = /^[A-Za-z0-9]{32}$/.test(appId);
51 | if (!isValidate) {
52 | alert("AppID verification failed, please enter correct information and try again!");
53 | }
54 | return isValidate;
55 |
56 | }
57 | $(document).ready(function () {
58 | $(document).off('click', '.sidebar-bottom').on('click', '.sidebar-bottom', function (e) {
59 | if ($(e.target).closest('.sidebar-bottom').length) {
60 | saveConfig();
61 | }
62 |
63 | const target = $(e.target).attr('data-href');
64 | if (!target) {
65 | return;
66 | }
67 | const isValidate = checkAppId();
68 | if (!isValidate) {
69 | return;
70 | }
71 | window.location.href = $(e.target).attr('data-href');
72 | });
73 | });
74 |
75 |
76 | $(".proxy-list").change(function (e) {
77 | changeModes(this.value);
78 | saveConfig();
79 | });
80 |
81 | $("#setup-btn").click(function (e) {
82 | saveConfig();
83 | const isValidate = checkAppId();
84 | if (!isValidate) {
85 | return;
86 | }
87 | message.success("Set successfully! Link to function page!");
88 | const href = getJumpBackUrl();
89 | window.location.href = href;
90 | });
91 |
92 | $("#appid").change(function (e) {
93 | saveConfig();
94 | })
95 |
96 | $("#certificate").change(function (e) {
97 | saveConfig();
98 | });
99 |
100 | $("#project-id-select").change(function (e) {
101 | const appId = this.value;
102 | if (appId === "custom_settings") {
103 | $("#appid").val("");
104 | $("#certificate").val("");
105 | $("#appid").prop("disabled", false);
106 | $("#certificate").prop("disabled", false);
107 | return;
108 | } else {
109 | $("#appid").prop("disabled", true);
110 | $("#certificate").prop("disabled", true);
111 | }
112 | const project = projectList.find((project) => project.appId === appId);
113 | $("#appid").val(project.appId);
114 | $("#certificate").val(project.appSecret);
115 |
116 | });
117 |
118 | async function changeModes(label) {
119 | proxyModeItem = modeList.find((profile) => profile.label === label);
120 | }
121 |
122 | function initModes() {
123 | modeList.forEach((profile) => {
124 | $(".proxy-list").append(
125 | `${profile.label}: ${profile.detail} `,
126 | );
127 | });
128 | proxyModeItem = modeList.find((profile) => profile.value === options.proxyMode) || modeList[0];
129 | $(".proxy-list").val(proxyModeItem.label);
130 | }
131 |
132 | function initVersion() {
133 | const version = AgoraRTC.VERSION;
134 | $("#version-text").text(`v${version}`);
135 | }
136 |
137 | function appendCustomOption() {
138 | $("#project-id-select").append(
139 | `Custom `,
140 | )
141 | }
142 | function initDocUrl() {
143 | $("#certificate-link").attr("href", appCertificateLink);
144 | $("#appid-link").attr("href", appIdLink);
145 | $("#proxy-link").attr("href", proxyLink);
146 |
147 | $('.sidebar-bottom a').each(function () {
148 | const href = $(this).attr('href');
149 | $(this).attr('data-href', href);
150 | $(this).removeAttr('href');
151 | });
152 | const observer = new MutationObserver((mutations) => {
153 | mutations.forEach((mutation) => {
154 | $('.sidebar-bottom a').each(function () {
155 | const href = $(this).attr('href');
156 | $(this).attr('data-href', href);
157 | $(this).removeAttr('href');
158 | });
159 | if (mutation.type === 'childList') {
160 | const certificateLinkNode = document.getElementById('certificate-link');
161 | const appidLinkNode = document.getElementById('appid-link');
162 | const proxyLinkNode = document.getElementById('proxy-link');
163 | if (certificateLinkNode) {
164 | certificateLinkNode.setAttribute('href', appCertificateLink);
165 | }
166 | if (appidLinkNode) {
167 | appidLinkNode.setAttribute('href', appIdLink);
168 | }
169 | if (proxyLinkNode) {
170 | proxyLinkNode.setAttribute('href', proxyLink);
171 | }
172 | if (certificateLinkNode && appidLinkNode && proxyLinkNode) {
173 | observer.disconnect();
174 | }
175 | }
176 | });
177 | });
178 | observer.observe(document.body, { childList: true, subtree: true });
179 | }
180 |
181 | let reGetProjects = false;
182 |
--------------------------------------------------------------------------------