mirror of
https://github.com/tl-open-source/tl-rtc-file.git
synced 2025-09-26 19:41:16 +08:00
feat: add document
feat: show websocket state feat: transfer file code perf feat: video controls feat: ice state change alter feat: remove p2p check feat: website page update feat: update doc feat: update shell feat: update index page fix: change nickname notify fix: change nickname content filter fix: live share url role error
This commit is contained in:
213
README.md
213
README.md
@@ -1,4 +1,4 @@
|
||||
# tl-rtc-file-tool (tl webrtc file tools)
|
||||
# tl-rtc-file-tool (tl webrtc file(media) tools)
|
||||
|
||||
[](https://webrtc.org.cn/)
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
@@ -7,252 +7,241 @@
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://im.iamtsm.cn/file" target="_blank">体验地址</a> |
|
||||
<a href="https://im.iamtsm.cn/file" target="_blank">Demo</a> |
|
||||
<a href="https://im.iamtsm.cn/document" target="_blank">Document</a> |
|
||||
<a href="https://hub.docker.com/u/iamtsm" target="_blank">DockerHub</a> |
|
||||
<a href="https://github.com/tl-open-source/tl-rtc-file/blob/master/doc/README_EN.md" target="_blank"> EN-DOC </a> |QQ群:
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=TKCwMBjN" target="_blank">624214498 </a>
|
||||
<a href="https://github.com/tl-open-source/tl-rtc-file/blob/master/doc/README_ZN.md" target="_blank">中文文档</a> | QQ Group:
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=TKCwMBjN" target="_blank">624214498</a>
|
||||
</p>
|
||||
|
||||
## 目录
|
||||
## Table of Contents
|
||||
|
||||
- [背景](#背景)
|
||||
- [优点](#优点)
|
||||
- [部署前必看](#部署前必看)
|
||||
- [自行部署](#自行部署)
|
||||
- [安装环境](#安装环境)
|
||||
- [启动服务](#启动服务)
|
||||
- [docker部署](#docker部署)
|
||||
- [docker一键脚本启动](#docker一键脚本启动)
|
||||
- [docker-compose命令启动](#docker-compose启动)
|
||||
- [自行打包镜像启动](#自行打包启动镜像)
|
||||
- [其他形式部署](#其他形式部署)
|
||||
- [配置数据库 (非必须步骤)](#配置数据库-非必须步骤)
|
||||
- [管理后台 (非必须步骤)](#管理后台-非必须步骤)
|
||||
- [企微通知 (非必须步骤)](#企微通知-非必须步骤)
|
||||
- [OSS云存储 (非必须步骤)](#oss云存储-非必须步骤)
|
||||
- [Chat-GPT (非必须步骤)](#chat-gpt-非必须步骤)
|
||||
- [配置turnserver (局域网非必须步骤,公网必须步骤)](#配置turnserver-局域网非必须步骤公网必须步骤)
|
||||
- [概述图](#概述图)
|
||||
- [Background](#background)
|
||||
- [Advantages](#advantages)
|
||||
- [Pre-deployment Considerations](#pre-deployment-considerations)
|
||||
- [Self-Deployment](#self-deployment)
|
||||
- [Installing Dependencies](#installing-dependencies)
|
||||
- [Starting the Service](#starting-the-service)
|
||||
- [Docker Deployment](#docker-deployment)
|
||||
- [One-Click Docker Script](#one-click-docker-script)
|
||||
- [Using docker-compose](#using-docker-compose)
|
||||
- [Self-Building and Starting the Image](#self-building-and-starting-the-image)
|
||||
- [Other Deployment Methods](#other-deployment-methods)
|
||||
- [Configuring the Database (Optional)](#configuring-the-database-optional)
|
||||
- [Admin Panel (Optional)](#admin-panel-optional)
|
||||
- [WeChat Notifications (Optional)](#wechat-notifications-optional)
|
||||
- [OSS Cloud Storage (Optional)](#oss-cloud-storage-optional)
|
||||
- [Chat-GPT (Optional)](#chat-gpt-optional)
|
||||
- [Configuring turnserver (Optional for LAN, Required for WAN)](#configuring-turnserver-optional-for-lan-required-for-wan)
|
||||
- [Overview Diagram](#overview-diagram)
|
||||
- [License](#license)
|
||||
- [免责声明](#免责声明)
|
||||
- [Disclaimer](#disclaimer)
|
||||
|
||||
## 背景
|
||||
## Background
|
||||
|
||||
20年毕设的题目相关整理出来的,用webrt在web端传输文件,支持传输超大文件。
|
||||
This project was developed based on the topic of the graduation project in 2020. It allows file transfer using WebRTC in web applications and supports transferring large files.
|
||||
|
||||
## 优点
|
||||
## Advantages
|
||||
|
||||
分片传输,跨终端,不限平台,方便使用,内网不限速(局域网最高到过70多M/s),支持私有部署,支持多文件拖拽发送,网页文件预览。 扩展了许多丰富的小功能,如本地屏幕录制,远程屏幕共享(无延迟),远程音视频通话(无延迟),直播(无延迟),密码房间,oss云存储,中继服务设置,webrtc检测,webrtc统计,文字传输(群聊,私聊),公共聊天,远程画板,AI聊天框,丰富的后台管理,实时执行日志展示,机器人告警通知等功能... 等等
|
||||
Fragmented transmission, cross-platform, platform-independent, easy to use, no speed limit in the local network (up to over 70 MB/s in the LAN), supports private deployment, supports drag-and-drop sending of multiple files, web file preview. Many additional features have been added, such as local screen recording, remote screen sharing (zero-latency), remote audio and video calls (zero-latency), live streaming (zero-latency), password-protected rooms, OSS cloud storage, relay service settings, WebRTC detection, WebRTC statistics, text transmission (group chat, private chat), public chat, remote whiteboard, AI chatbox, feature-rich admin panel, real-time execution log display, robot alert notifications, and more.
|
||||
|
||||
## Pre-deployment Considerations
|
||||
|
||||
## 部署前必看
|
||||
Whether it's self-deployment, Docker deployment, or other script deployments, you need to modify the corresponding configurations in `tlrtcfile.env` before performing the following operations. Further configuration modifications and service restarts are required.
|
||||
|
||||
无论是自行部署,还是docker部署,还是其他脚本部署,都需要先行修改 `tlrtcfile.env` 中相应配置,再执行下面操作,且后续还需修改配置,需要重启服务
|
||||
Of course, you can also use the default configurations without modifications, but the default configurations are only suitable for testing on localhost. They won't be accessible to others, making it impossible for others to use. Therefore, if you intend to deploy on a server for local network or public network users, you must configure `tlrtcfile.env` accordingly.
|
||||
|
||||
当然,你也可以不修改配置,使用默认的配置,但是默认的配置仅限于可以在localhost测试使用,其他人访问不到也使用不了。所以如果是需要部署到服务器上给局域网或者公网其他用户使用,就必须按需设置好 `tlrtcfile.env`
|
||||
## Self-Deployment
|
||||
#### Installing Dependencies
|
||||
|
||||
|
||||
## 自行部署
|
||||
#### 安装环境
|
||||
|
||||
安装node-14.21.x或14.21.x以上,npm后,进入项目目录运行下面命令
|
||||
Install Node.js 14.21.x or above, and npm. Then, navigate to the project directory and run the following command:
|
||||
```
|
||||
cd svr/
|
||||
|
||||
npm install
|
||||
```
|
||||
首次运行执行一次下面的命令
|
||||
|
||||
For the first run, execute the following command:
|
||||
```
|
||||
npm run build:pro
|
||||
```
|
||||
|
||||
如果你需要自己开发/修改前端页面,用这个命令,不需要开发页面就跳过这一个
|
||||
|
||||
If you need to develop or modify the frontend pages, use this command. If not, you can skip this step:
|
||||
```
|
||||
npm run build:dev
|
||||
```
|
||||
|
||||
#### 启动服务
|
||||
#### Starting the Service
|
||||
|
||||
启动以下两个服务, 选一种模式启动即可,两者的区别就是,https环境启动才可以使用音视频,直播,屏幕共享功能,其他功能不影响
|
||||
Start the following two services. Choose one mode to start. The only difference between them is that the HTTPS mode is required to use features like audio/video streaming, live streaming, and screen sharing. Other features are not affected.
|
||||
|
||||
http模式启动后,访问 `http://你的机器ip:9092`
|
||||
|
||||
- 启动api服务 和 socket服务
|
||||
After starting in HTTP mode, access the service at `http://your_machine_ip:9092`.
|
||||
|
||||
- Start the API and socket services:
|
||||
```
|
||||
npm run http-api
|
||||
|
||||
npm run http-socket
|
||||
```
|
||||
|
||||
Or, start in HTTPS mode and access the service at `https://your_machine_ip:9092`.
|
||||
|
||||
或者使用https模式启动,访问 `https://你的机器ip:9092`
|
||||
|
||||
- 启动api服务 和 socket服务
|
||||
|
||||
- Start the API and socket services:
|
||||
```
|
||||
npm run https-api
|
||||
|
||||
npm run https-socket
|
||||
```
|
||||
|
||||
## docker部署
|
||||
## Docker Deployment
|
||||
|
||||
目前支持 `官方镜像` 和 `自行打包镜像`,使用官方镜像目前支持两种操作方式 `docker脚本启动`,`docker-compose启动`
|
||||
#### docker一键脚本启动
|
||||
Currently, both `official images` and `self-built images` are supported. For official images, there are two ways to operate: `docker script startup` and `docker-compose startup`.
|
||||
|
||||
进入 `bin/` 目录执行脚本 `auto-pull-and-start-docker.sh`
|
||||
#### One-Click Docker Script
|
||||
|
||||
Navigate to the `bin/` directory and execute the `auto-pull-and-start-docker.sh` script:
|
||||
```
|
||||
chmod +x ./auto-pull-and-start-docker.sh
|
||||
|
||||
./auto-pull-and-start-docker.sh
|
||||
```
|
||||
#### docker-compose启动
|
||||
|
||||
根据你的 `Docker Compose` 版本在 `主目录` 执行如下对应的命令
|
||||
#### Using docker-compose
|
||||
|
||||
- 对于 `Docker Compose V1`
|
||||
In the main directory, execute the corresponding command based on your Docker Compose version:
|
||||
|
||||
- For Docker Compose V1:
|
||||
```
|
||||
docker-compose --profile=http up -d
|
||||
```
|
||||
|
||||
- 对于 `Docker Compose V2`
|
||||
|
||||
- For Docker Compose V2:
|
||||
```
|
||||
docker compose --profile=http up -d
|
||||
```
|
||||
|
||||
#### 自行打包启动镜像
|
||||
#### Self-Building and Starting the Image
|
||||
|
||||
进入 `docker/` 目录后根据你的 `Docker Compose` 版本在主目录执行如下对应的命令
|
||||
Navigate to the `docker/` directory and execute the corresponding command based on your Docker Compose version:
|
||||
|
||||
- 对于 `Docker Compose V1`
|
||||
- For Docker Compose V1:
|
||||
```
|
||||
docker-compose -f docker-compose-build-code.yml up -d
|
||||
```
|
||||
|
||||
- 对于 `Docker Compose V2`
|
||||
|
||||
- For Docker Compose V2:
|
||||
```
|
||||
docker compose -f docker-compose-build-code.yml up -d
|
||||
```
|
||||
|
||||
## 其他形式部署
|
||||
## Other Deployment Methods
|
||||
|
||||
除了上面的手动安装,docker官方镜像,docker自己打包镜像之外,还支持自动脚本,托管平台一键部署等
|
||||
In addition to the manual installation, Docker official images, and self-built Docker images, there are other methods such as automatic scripts and one-click deployments on hosting platforms.
|
||||
|
||||
下载项目后,可以进入 `bin/` 目录,选择对应的系统脚本,直接执行即可,会自动检测安装环境,自动安装依赖,自动启动服务
|
||||
After downloading the project, navigate to the `bin/` directory and choose the appropriate system script to execute. It will automatically detect the environment, install dependencies, and start the service.
|
||||
|
||||
#### ubuntu自动脚本 (比如ubuntu16)
|
||||
#### Automatic script for Ubuntu (e.g., Ubuntu 16)
|
||||
|
||||
- 如果脚本没有执行权限,执行一下下面的命令
|
||||
- If the script doesn't have execution permission, run the following command:
|
||||
```
|
||||
chmod +x ./ubuntu16/*.sh
|
||||
```
|
||||
|
||||
- 使用 `http` 方式则是执行这个脚本
|
||||
- If using HTTP, execute this script:
|
||||
```
|
||||
./auto-check-install-http.sh
|
||||
```
|
||||
|
||||
- 或者使用 `https` 方式则是执行这个脚本
|
||||
- If using HTTPS, execute this script:
|
||||
```
|
||||
./auto-check-install-https.sh
|
||||
```
|
||||
|
||||
- 停止服务脚本 :
|
||||
- To stop the service:
|
||||
```
|
||||
./auto-stop.sh
|
||||
```
|
||||
|
||||
#### windows自动脚本
|
||||
#### Automatic script for Windows
|
||||
|
||||
- 使用 `http` 方式则是执行这个脚本
|
||||
- If using HTTP, execute this script:
|
||||
```
|
||||
windows/auto-check-install-http.bat
|
||||
```
|
||||
|
||||
- 或者使用https方式则是执行这个脚本
|
||||
- If using HTTPS, execute this script:
|
||||
```
|
||||
windows/auto-check-install-https.bat
|
||||
```
|
||||
|
||||
#### zeabur平台一键部署
|
||||
#### One-Click Deployment on Zeabur Platform
|
||||
|
||||
[](https://zeabur.com/templates/898TLE?referralCode=iamtsm)
|
||||
|
||||
## Other Configuration Options
|
||||
|
||||
## 其他配置项
|
||||
#### 配置数据库 (非必须步骤)
|
||||
#### Configuring the Database (Optional)
|
||||
|
||||
需要自行安装mysql数据库,新建一个数据库名称为 `webchat`,然后修改 `tlrtcfile.env` 中的数据库相关配置即可
|
||||
You need to install MySQL database manually, create a database named `webchat`, and then modify the database-related configurations in `tlrtcfile.env`.
|
||||
|
||||
#### 企微通知 (非必须步骤)
|
||||
#### Admin Panel (Optional)
|
||||
|
||||
如果需要设置一些访问通知,错误告警通知,可以在企业微信建立机器人后,每一个机器人会有一个key,修改 `tlrtcfile.env` 中的企业微信通知相关配置即可
|
||||
Prerequisite: Database configuration must be enabled.
|
||||
|
||||
#### OSS云存储 (非必须步骤)
|
||||
Modify the admin panel-related configurations in `tlrtcfile.env`. After starting, enter the configured room number and password to access the admin panel.
|
||||
|
||||
目前支持对接了seafile存储,后续会逐步支持阿里云,腾讯云,七牛云,自己的服务器等存储方式。 修改 `tlrtcfile.env` 中的OSS存储相关配置即可
|
||||
#### WeChat Notifications (Optional)
|
||||
|
||||
#### Chat-GPT (非必须步骤)
|
||||
If you need to set up notification for access and error alerts, you can create a WeChat Work robot and get an API key. Modify the WeChat notification configurations in `tlrtcfile.env`.
|
||||
|
||||
对接了openai的接口,内置了一个聊天对话框, 修改 `tlrtcfile.env` 中的openai相关配置即可
|
||||
#### OSS Cloud Storage (Optional)
|
||||
|
||||
#### 管理后台 (非必须步骤)
|
||||
The project currently supports Seafile storage integration, and future updates will include support for Alibaba Cloud, Tencent Cloud, Qiniu Cloud, and self-hosted server storage methods. Modify the OSS storage configurations in `tlrtcfile.env`.
|
||||
|
||||
前提 : 需要开启数据库配置
|
||||
#### Chat-GPT (Optional)
|
||||
|
||||
修改 `tlrtcfile.env` 中的管理后台相关配置即可, 启动后,输入配置的房间号,输入密码,即可进入管理后台
|
||||
Integrated with the OpenAI API, this project includes a chat dialog. Modify the OpenAI configurations in `tlrtcfile.env`.
|
||||
|
||||
#### 配置turnserver (局域网非必须步骤,公网必须步骤)
|
||||
#### Configuring turnserver (Optional for LAN, Required for WAN)
|
||||
|
||||
目前有两种形式去生成使用turn服务的帐号密码,一种是固定帐号密码 (优先推荐),一种是有效期帐号密码。**选一种方式即可** ,以下以ubuntu示例
|
||||
|
||||
安装coturn
|
||||
There are two ways to generate TURN server credentials: fixed credentials (recommended) and time-limited credentials. Choose one method. The following example uses Ubuntu.
|
||||
|
||||
Install coturn:
|
||||
```
|
||||
sudo apt-get install coturn
|
||||
```
|
||||
|
||||
有效帐号密码模式配置文件 : `docker/coturn/turnserver-with-secret-user.conf`
|
||||
For time-limited credentials, modify the configuration file `docker/coturn/turnserver-with-secret-user.conf`.
|
||||
|
||||
- 修改配置文件字段
|
||||
- Modify the fields in the configuration file:
|
||||
```
|
||||
`listening-device`, `listening-ip`, `external-ip`, `static-auth-secret`, `realm`
|
||||
```
|
||||
- 启动turnserver
|
||||
|
||||
- Start the turnserver:
|
||||
```
|
||||
turnserver -c /这个地方路径填完整/conf/turn/turnserver-with-secret-user.conf
|
||||
turnserver -c /path/to/conf/turn/turnserver-with-secret-user.conf
|
||||
```
|
||||
|
||||
固定帐号密码模式配置文件 : `docker/coturn/turnserver-with-fixed-user.conf`
|
||||
For fixed credentials, modify the configuration file `docker/coturn/turnserver-with-fixed-user.conf`.
|
||||
|
||||
- 修改配置文件字段
|
||||
- Modify the fields in the configuration file:
|
||||
```
|
||||
`listening-device`, `listening-ip`, `external-ip`, `user`, `realm`
|
||||
```
|
||||
- 生成用户
|
||||
- Generate a user:
|
||||
```
|
||||
turnadmin -a -u 帐号 -p 密码 -r 这个地方填配置文件中的relam
|
||||
turnadmin -a -u username -p password -r realm_in_config_file
|
||||
```
|
||||
- 启动turnserver
|
||||
- Start the turnserver:
|
||||
```
|
||||
turnserver -c /这个地方路径填完整/docker/coturn/turnserver-with-secret-user.conf
|
||||
turnserver -c /path/to/docker/coturn/turnserver-with-secret-user.conf
|
||||
```
|
||||
|
||||
部署好coturn后,在对应的 `tlrtcfile.env` 配置中设置好webrtc相关信息即可
|
||||
After setting up coturn, configure the WebRTC-related information in the corresponding `tlrtcfile.env` configuration.
|
||||
|
||||
## 概述图
|
||||
## Overview Diagram
|
||||
|
||||

|
||||

|
||||
|
||||
## License
|
||||
|
||||
### MIT License Copyright (c) 2022 iamtsm
|
||||
### MIT License Copyright (c) 2020 ~ 2023 iamtsm
|
||||
|
||||
## 免责声明
|
||||
## Disclaimer
|
||||
|
||||
[免责声明](DISCLAIMER.md)
|
||||
[Disclaimer](DISCLAIMER.md)
|
@@ -78,7 +78,6 @@ docker run \
|
||||
-e MYSQL_DATABASE=webchat \
|
||||
-e MYSQL_USER=tlrtcfile \
|
||||
-e MYSQL_PASSWORD=tlrtcfile \
|
||||
-v $(dirname "$PWD")/docker/mysql/data/mysql.env:/tlrtcfile/docker/mysql/mysql.env \
|
||||
-v $(dirname "$PWD")/docker/mysql/data/db:/var/lib/mysql \
|
||||
-v $(dirname "$PWD")/docker/mysql/data/my.cnf:/etc/mysql/conf.d/my.cnf \
|
||||
-v $(dirname "$PWD")/docker/mysql/data/log:/var/log/mysql \
|
||||
@@ -132,7 +131,6 @@ docker run \
|
||||
-e tl_rtc_file_notify_open \
|
||||
-e tl_rtc_file_notify_qiwei_normal \
|
||||
-e tl_rtc_file_notify_qiwei_error \
|
||||
-v $(dirname "$PWD")/tlrtcfile.env:/tlrtcfile/tlrtcfile.env \
|
||||
--link mysql \
|
||||
-d iamtsm/tl-rtc-file-api tlapi
|
||||
|
||||
@@ -174,6 +172,5 @@ docker run \
|
||||
-e tl_rtc_file_notify_open \
|
||||
-e tl_rtc_file_notify_qiwei_normal \
|
||||
-e tl_rtc_file_notify_qiwei_error \
|
||||
-v $(dirname "$PWD")/tlrtcfile.env:/tlrtcfile/tlrtcfile.env \
|
||||
--link mysql \
|
||||
-d iamtsm/tl-rtc-file-socket tlsocket
|
246
doc/README_EN.md
246
doc/README_EN.md
@@ -1,246 +0,0 @@
|
||||
# tl-rtc-file-tool (tl webrtc file tools)
|
||||
|
||||
[](https://webrtc.org.cn/)
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://im.iamtsm.cn/file" target="_blank">Demo</a> |
|
||||
<a href="https://hub.docker.com/u/iamtsm" target="_blank">DockerHub</a> |
|
||||
<a href="https://github.com/tl-open-source/tl-rtc-file/blob/master/doc/README_EN.md" target="_blank">EN-DOC</a> | QQ Group:
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=TKCwMBjN" target="_blank">624214498</a>
|
||||
</p>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Background](#background)
|
||||
- [Advantages](#advantages)
|
||||
- [Pre-deployment Considerations](#pre-deployment-considerations)
|
||||
- [Self-Deployment](#self-deployment)
|
||||
- [Installing Dependencies](#installing-dependencies)
|
||||
- [Starting the Service](#starting-the-service)
|
||||
- [Docker Deployment](#docker-deployment)
|
||||
- [One-Click Docker Script](#one-click-docker-script)
|
||||
- [Using docker-compose](#using-docker-compose)
|
||||
- [Self-Building and Starting the Image](#self-building-and-starting-the-image)
|
||||
- [Other Deployment Methods](#other-deployment-methods)
|
||||
- [Configuring the Database (Optional)](#configuring-the-database-optional)
|
||||
- [Admin Panel (Optional)](#admin-panel-optional)
|
||||
- [WeChat Notifications (Optional)](#wechat-notifications-optional)
|
||||
- [OSS Cloud Storage (Optional)](#oss-cloud-storage-optional)
|
||||
- [Chat-GPT (Optional)](#chat-gpt-optional)
|
||||
- [Configuring turnserver (Optional for LAN, Required for WAN)](#configuring-turnserver-optional-for-lan-required-for-wan)
|
||||
- [Overview Diagram](#overview-diagram)
|
||||
- [License](#license)
|
||||
- [Disclaimer](#disclaimer)
|
||||
|
||||
## Background
|
||||
|
||||
This project was developed based on the topic of the graduation project in 2020. It allows file transfer using WebRTC in web applications and supports transferring large files.
|
||||
|
||||
## Advantages
|
||||
|
||||
Fragmented transmission, cross-platform, platform-independent, easy to use, no speed limit in the local network (up to over 70 MB/s in the LAN), supports private deployment, supports drag-and-drop sending of multiple files, web file preview. Many additional features have been added, such as local screen recording, remote screen sharing (zero-latency), remote audio and video calls (zero-latency), live streaming (zero-latency), password-protected rooms, OSS cloud storage, relay service settings, WebRTC detection, WebRTC statistics, text transmission (group chat, private chat), public chat, remote whiteboard, AI chatbox, feature-rich admin panel, real-time execution log display, robot alert notifications, and more.
|
||||
|
||||
## Pre-deployment Considerations
|
||||
|
||||
Whether it's self-deployment, Docker deployment, or other script deployments, you need to modify the corresponding configurations in `tlrtcfile.env` before performing the following operations. Further configuration modifications and service restarts are required.
|
||||
|
||||
Of course, you can also use the default configurations without modifications, but the default configurations are only suitable for testing on localhost. They won't be accessible to others, making it impossible for others to use. Therefore, if you intend to deploy on a server for local network or public network users, you must configure `tlrtcfile.env` accordingly.
|
||||
|
||||
## Self-Deployment
|
||||
#### Installing Dependencies
|
||||
|
||||
Install Node.js 14.21.x or above, and npm. Then, navigate to the project directory and run the following command:
|
||||
```
|
||||
cd svr/
|
||||
|
||||
npm install
|
||||
```
|
||||
For the first run, execute the following command:
|
||||
```
|
||||
npm run build:pro
|
||||
```
|
||||
If you need to develop or modify the frontend pages, use this command. If not, you can skip this step:
|
||||
```
|
||||
npm run build:dev
|
||||
```
|
||||
|
||||
#### Starting the Service
|
||||
|
||||
Start the following two services. Choose one mode to start. The only difference between them is that the HTTPS mode is required to use features like audio/video streaming, live streaming, and screen sharing. Other features are not affected.
|
||||
|
||||
After starting in HTTP mode, access the service at `http://your_machine_ip:9092`.
|
||||
|
||||
- Start the API and socket services:
|
||||
```
|
||||
npm run http-api
|
||||
npm run http-socket
|
||||
```
|
||||
|
||||
Or, start in HTTPS mode and access the service at `https://your_machine_ip:9092`.
|
||||
|
||||
- Start the API and socket services:
|
||||
```
|
||||
npm run https-api
|
||||
npm run https-socket
|
||||
```
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
Currently, both `official images` and `self-built images` are supported. For official images, there are two ways to operate: `docker script startup` and `docker-compose startup`.
|
||||
|
||||
#### One-Click Docker Script
|
||||
|
||||
Navigate to the `bin/` directory and execute the `auto-pull-and-start-docker.sh` script:
|
||||
```
|
||||
chmod +x ./auto-pull-and-start-docker.sh
|
||||
./auto-pull-and-start-docker.sh
|
||||
```
|
||||
|
||||
#### Using docker-compose
|
||||
|
||||
In the main directory, execute the corresponding command based on your Docker Compose version:
|
||||
|
||||
- For Docker Compose V1:
|
||||
```
|
||||
docker-compose --profile=http up -d
|
||||
```
|
||||
|
||||
- For Docker Compose V2:
|
||||
```
|
||||
docker compose --profile=http up -d
|
||||
```
|
||||
|
||||
#### Self-Building and Starting the Image
|
||||
|
||||
Navigate to the `docker/` directory and execute the corresponding command based on your Docker Compose version:
|
||||
|
||||
- For Docker Compose V1:
|
||||
```
|
||||
docker-compose -f docker-compose-build-code.yml up -d
|
||||
```
|
||||
|
||||
- For Docker Compose V2:
|
||||
```
|
||||
docker compose -f docker-compose-build-code.yml up -d
|
||||
```
|
||||
|
||||
## Other Deployment Methods
|
||||
|
||||
In addition to the manual installation, Docker official images, and self-built Docker images, there are other methods such as automatic scripts and one-click deployments on hosting platforms.
|
||||
|
||||
After downloading the project, navigate to the `bin/` directory and choose the appropriate system script to execute. It will automatically detect the environment, install dependencies, and start the service.
|
||||
|
||||
#### Automatic script for Ubuntu (e.g., Ubuntu 16)
|
||||
|
||||
- If the script doesn't have execution permission, run the following command:
|
||||
```
|
||||
chmod +x ./ubuntu16/*.sh
|
||||
```
|
||||
|
||||
- If using HTTP, execute this script:
|
||||
```
|
||||
./auto-check-install-http.sh
|
||||
```
|
||||
|
||||
- If using HTTPS, execute this script:
|
||||
```
|
||||
./auto-check-install-https.sh
|
||||
```
|
||||
|
||||
- To stop the service:
|
||||
```
|
||||
./auto-stop.sh
|
||||
```
|
||||
|
||||
#### Automatic script for Windows
|
||||
|
||||
- If using HTTP, execute this script:
|
||||
```
|
||||
windows/auto-check-install-http.bat
|
||||
```
|
||||
|
||||
- If using HTTPS, execute this script:
|
||||
```
|
||||
windows/auto-check-install-https.bat
|
||||
```
|
||||
|
||||
#### One-Click Deployment on Zeabur Platform
|
||||
|
||||
[](https://zeabur.com/templates/898TLE?referralCode=iamtsm)
|
||||
|
||||
## Other Configuration Options
|
||||
|
||||
#### Configuring the Database (Optional)
|
||||
|
||||
You need to install MySQL database manually, create a database named `webchat`, and then modify the database-related configurations in `tlrtcfile.env`.
|
||||
|
||||
#### Admin Panel (Optional)
|
||||
|
||||
Prerequisite: Database configuration must be enabled.
|
||||
|
||||
Modify the admin panel-related configurations in `tlrtcfile.env`. After starting, enter the configured room number and password to access the admin panel.
|
||||
|
||||
#### WeChat Notifications (Optional)
|
||||
|
||||
If you need to set up notification for access and error alerts, you can create a WeChat Work robot and get an API key. Modify the WeChat notification configurations in `tlrtcfile.env`.
|
||||
|
||||
#### OSS Cloud Storage (Optional)
|
||||
|
||||
The project currently supports Seafile storage integration, and future updates will include support for Alibaba Cloud, Tencent Cloud, Qiniu Cloud, and self-hosted server storage methods. Modify the OSS storage configurations in `tlrtcfile.env`.
|
||||
|
||||
#### Chat-GPT (Optional)
|
||||
|
||||
Integrated with the OpenAI API, this project includes a chat dialog. Modify the OpenAI configurations in `tlrtcfile.env`.
|
||||
|
||||
#### Configuring turnserver (Optional for LAN, Required for WAN)
|
||||
|
||||
There are two ways to generate TURN server credentials: fixed credentials (recommended) and time-limited credentials. Choose one method. The following example uses Ubuntu.
|
||||
|
||||
Install coturn:
|
||||
```
|
||||
sudo apt-get install coturn
|
||||
```
|
||||
|
||||
For time-limited credentials, modify the configuration file `docker/coturn/turnserver-with-secret-user.conf`.
|
||||
|
||||
- Modify the fields in the configuration file:
|
||||
```
|
||||
`listening-device`, `listening-ip`, `external-ip`, `static-auth-secret`, `realm`
|
||||
```
|
||||
- Start the turnserver:
|
||||
```
|
||||
turnserver -c /path/to/conf/turn/turnserver-with-secret-user.conf
|
||||
```
|
||||
|
||||
For fixed credentials, modify the configuration file `docker/coturn/turnserver-with-fixed-user.conf`.
|
||||
|
||||
- Modify the fields in the configuration file:
|
||||
```
|
||||
`listening-device`, `listening-ip`, `external-ip`, `user`, `realm`
|
||||
```
|
||||
- Generate a user:
|
||||
```
|
||||
turnadmin -a -u username -p password -r realm_in_config_file
|
||||
```
|
||||
- Start the turnserver:
|
||||
```
|
||||
turnserver -c /path/to/docker/coturn/turnserver-with-secret-user.conf
|
||||
```
|
||||
|
||||
After setting up coturn, configure the WebRTC-related information in the corresponding `tlrtcfile.env` configuration.
|
||||
|
||||
## Overview Diagram
|
||||
|
||||

|
||||
|
||||
## License
|
||||
|
||||
### MIT License Copyright (c) 2022 iamtsm
|
||||
|
||||
## Disclaimer
|
||||
|
||||
[Disclaimer](DISCLAIMER.md)
|
259
doc/README_ZN.md
Normal file
259
doc/README_ZN.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# tl-rtc-file-tool (tl webrtc file(media) tools)
|
||||
|
||||
[](https://webrtc.org.cn/)
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
[](https://github.com/iamtsm/tl-rtc-file/)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://im.iamtsm.cn/file" target="_blank">体验地址</a> |
|
||||
<a href="https://im.iamtsm.cn/document" target="_blank">详细文档</a> |
|
||||
<a href="https://hub.docker.com/u/iamtsm" target="_blank">DockerHub</a> |
|
||||
<a href="https://github.com/tl-open-source/tl-rtc-file/blob/master/README.md" target="_blank"> EN-DOC </a> |QQ群:
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=TKCwMBjN" target="_blank">624214498 </a>
|
||||
</p>
|
||||
|
||||
## 目录
|
||||
|
||||
- [背景](#背景)
|
||||
- [优点](#优点)
|
||||
- [部署前必看](#部署前必看)
|
||||
- [自行部署](#自行部署)
|
||||
- [安装环境](#安装环境)
|
||||
- [启动服务](#启动服务)
|
||||
- [docker部署](#docker部署)
|
||||
- [docker一键脚本启动](#docker一键脚本启动)
|
||||
- [docker-compose命令启动](#docker-compose启动)
|
||||
- [自行打包镜像启动](#自行打包启动镜像)
|
||||
- [其他形式部署](#其他形式部署)
|
||||
- [配置数据库 (非必须步骤)](#配置数据库-非必须步骤)
|
||||
- [管理后台 (非必须步骤)](#管理后台-非必须步骤)
|
||||
- [企微通知 (非必须步骤)](#企微通知-非必须步骤)
|
||||
- [OSS云存储 (非必须步骤)](#oss云存储-非必须步骤)
|
||||
- [Chat-GPT (非必须步骤)](#chat-gpt-非必须步骤)
|
||||
- [配置turnserver (局域网非必须步骤,公网必须步骤)](#配置turnserver-局域网非必须步骤公网必须步骤)
|
||||
- [概述图](#概述图)
|
||||
- [License](#license)
|
||||
- [免责声明](#免责声明)
|
||||
|
||||
## 背景
|
||||
|
||||
20年毕设的题目相关整理出来的,用webrt在web端传输文件,支持传输超大文件。
|
||||
|
||||
## 优点
|
||||
|
||||
分片传输,跨终端,不限平台,方便使用,内网不限速(局域网最高到过70多M/s),支持私有部署,支持多文件拖拽发送,网页文件预览。 扩展了许多丰富的小功能,如本地屏幕录制,远程屏幕共享(无延迟),远程音视频通话(无延迟),直播(无延迟),密码房间,oss云存储,中继服务设置,webrtc检测,webrtc统计,文字传输(群聊,私聊),公共聊天,远程画板,AI聊天框,丰富的后台管理,实时执行日志展示,机器人告警通知等功能... 等等
|
||||
|
||||
|
||||
## 部署前必看
|
||||
|
||||
无论是自行部署,还是docker部署,还是其他脚本部署,都需要先行修改 `tlrtcfile.env` 中相应配置,再执行下面操作,且后续还需修改配置,需要重启服务
|
||||
|
||||
当然,你也可以不修改配置,使用默认的配置,但是默认的配置仅限于可以在localhost测试使用,其他人访问不到也使用不了。所以如果是需要部署到服务器上给局域网或者公网其他用户使用,就必须按需设置好 `tlrtcfile.env`
|
||||
|
||||
|
||||
## 自行部署
|
||||
#### 安装环境
|
||||
|
||||
安装node-14.21.x或14.21.x以上,npm后,进入项目目录运行下面命令
|
||||
```
|
||||
cd svr/
|
||||
|
||||
npm install
|
||||
```
|
||||
首次运行执行一次下面的命令
|
||||
|
||||
```
|
||||
npm run build:pro
|
||||
```
|
||||
|
||||
如果你需要自己开发/修改前端页面,用这个命令,不需要开发页面就跳过这一个
|
||||
|
||||
```
|
||||
npm run build:dev
|
||||
```
|
||||
|
||||
#### 启动服务
|
||||
|
||||
启动以下两个服务, 选一种模式启动即可,两者的区别就是,https环境启动才可以使用音视频,直播,屏幕共享功能,其他功能不影响
|
||||
|
||||
http模式启动后,访问 `http://你的机器ip:9092`
|
||||
|
||||
- 启动api服务 和 socket服务
|
||||
|
||||
```
|
||||
npm run http-api
|
||||
|
||||
npm run http-socket
|
||||
```
|
||||
|
||||
|
||||
或者使用https模式启动,访问 `https://你的机器ip:9092`
|
||||
|
||||
- 启动api服务 和 socket服务
|
||||
|
||||
```
|
||||
npm run https-api
|
||||
|
||||
npm run https-socket
|
||||
```
|
||||
|
||||
## docker部署
|
||||
|
||||
目前支持 `官方镜像` 和 `自行打包镜像`,使用官方镜像目前支持两种操作方式 `docker脚本启动`,`docker-compose启动`
|
||||
#### docker一键脚本启动
|
||||
|
||||
进入 `bin/` 目录执行脚本 `auto-pull-and-start-docker.sh`
|
||||
|
||||
```
|
||||
chmod +x ./auto-pull-and-start-docker.sh
|
||||
|
||||
./auto-pull-and-start-docker.sh
|
||||
```
|
||||
#### docker-compose启动
|
||||
|
||||
根据你的 `Docker Compose` 版本在 `主目录` 执行如下对应的命令
|
||||
|
||||
- 对于 `Docker Compose V1`
|
||||
```
|
||||
docker-compose --profile=http up -d
|
||||
```
|
||||
|
||||
- 对于 `Docker Compose V2`
|
||||
```
|
||||
docker compose --profile=http up -d
|
||||
```
|
||||
|
||||
#### 自行打包启动镜像
|
||||
|
||||
进入 `docker/` 目录后根据你的 `Docker Compose` 版本在主目录执行如下对应的命令
|
||||
|
||||
- 对于 `Docker Compose V1`
|
||||
```
|
||||
docker-compose -f docker-compose-build-code.yml up -d
|
||||
```
|
||||
|
||||
- 对于 `Docker Compose V2`
|
||||
```
|
||||
docker compose -f docker-compose-build-code.yml up -d
|
||||
```
|
||||
|
||||
## 其他形式部署
|
||||
|
||||
除了上面的手动安装,docker官方镜像,docker自己打包镜像之外,还支持自动脚本,托管平台一键部署等
|
||||
|
||||
下载项目后,可以进入 `bin/` 目录,选择对应的系统脚本,直接执行即可,会自动检测安装环境,自动安装依赖,自动启动服务
|
||||
|
||||
#### ubuntu自动脚本 (比如ubuntu16)
|
||||
|
||||
- 如果脚本没有执行权限,执行一下下面的命令
|
||||
```
|
||||
chmod +x ./ubuntu16/*.sh
|
||||
```
|
||||
|
||||
- 使用 `http` 方式则是执行这个脚本
|
||||
```
|
||||
./auto-check-install-http.sh
|
||||
```
|
||||
|
||||
- 或者使用 `https` 方式则是执行这个脚本
|
||||
```
|
||||
./auto-check-install-https.sh
|
||||
```
|
||||
|
||||
- 停止服务脚本 :
|
||||
```
|
||||
./auto-stop.sh
|
||||
```
|
||||
|
||||
#### windows自动脚本
|
||||
|
||||
- 使用 `http` 方式则是执行这个脚本
|
||||
```
|
||||
windows/auto-check-install-http.bat
|
||||
```
|
||||
|
||||
- 或者使用https方式则是执行这个脚本
|
||||
```
|
||||
windows/auto-check-install-https.bat
|
||||
```
|
||||
|
||||
#### zeabur平台一键部署
|
||||
|
||||
[](https://zeabur.com/templates/898TLE?referralCode=iamtsm)
|
||||
|
||||
|
||||
## 其他配置项
|
||||
#### 配置数据库 (非必须步骤)
|
||||
|
||||
需要自行安装mysql数据库,新建一个数据库名称为 `webchat`,然后修改 `tlrtcfile.env` 中的数据库相关配置即可
|
||||
|
||||
#### 企微通知 (非必须步骤)
|
||||
|
||||
如果需要设置一些访问通知,错误告警通知,可以在企业微信建立机器人后,每一个机器人会有一个key,修改 `tlrtcfile.env` 中的企业微信通知相关配置即可
|
||||
|
||||
#### OSS云存储 (非必须步骤)
|
||||
|
||||
目前支持对接了seafile存储,后续会逐步支持阿里云,腾讯云,七牛云,自己的服务器等存储方式。 修改 `tlrtcfile.env` 中的OSS存储相关配置即可
|
||||
|
||||
#### Chat-GPT (非必须步骤)
|
||||
|
||||
对接了openai的接口,内置了一个聊天对话框, 修改 `tlrtcfile.env` 中的openai相关配置即可
|
||||
|
||||
#### 管理后台 (非必须步骤)
|
||||
|
||||
前提 : 需要开启数据库配置
|
||||
|
||||
修改 `tlrtcfile.env` 中的管理后台相关配置即可, 启动后,输入配置的房间号,输入密码,即可进入管理后台
|
||||
|
||||
#### 配置turnserver (局域网非必须步骤,公网必须步骤)
|
||||
|
||||
目前有两种形式去生成使用turn服务的帐号密码,一种是固定帐号密码 (优先推荐),一种是有效期帐号密码。**选一种方式即可** ,以下以ubuntu示例
|
||||
|
||||
安装coturn
|
||||
|
||||
```
|
||||
sudo apt-get install coturn
|
||||
```
|
||||
|
||||
有效帐号密码模式配置文件 : `docker/coturn/turnserver-with-secret-user.conf`
|
||||
|
||||
- 修改配置文件字段
|
||||
```
|
||||
`listening-device`, `listening-ip`, `external-ip`, `static-auth-secret`, `realm`
|
||||
```
|
||||
- 启动turnserver
|
||||
|
||||
```
|
||||
turnserver -c /这个地方路径填完整/conf/turn/turnserver-with-secret-user.conf
|
||||
```
|
||||
|
||||
固定帐号密码模式配置文件 : `docker/coturn/turnserver-with-fixed-user.conf`
|
||||
|
||||
- 修改配置文件字段
|
||||
```
|
||||
`listening-device`, `listening-ip`, `external-ip`, `user`, `realm`
|
||||
```
|
||||
- 生成用户
|
||||
```
|
||||
turnadmin -a -u 帐号 -p 密码 -r 这个地方填配置文件中的relam
|
||||
```
|
||||
- 启动turnserver
|
||||
```
|
||||
turnserver -c /这个地方路径填完整/docker/coturn/turnserver-with-secret-user.conf
|
||||
```
|
||||
|
||||
部署好coturn后,在对应的 `tlrtcfile.env` 配置中设置好webrtc相关信息即可
|
||||
|
||||
## 概述图
|
||||
|
||||

|
||||
|
||||
## License
|
||||
|
||||
### MIT License Copyright (c) 2020 ~ 2023 iamtsm
|
||||
|
||||
## 免责声明
|
||||
|
||||
[免责声明](DISCLAIMER.md)
|
64
doc/gitbook/CONF_ALL.md
Normal file
64
doc/gitbook/CONF_ALL.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# 配置统计汇总
|
||||
|
||||
- `tlrtcfile.env` [ 主要的项目配置文件 ]
|
||||
- `docker-compose.yml` [ 使用官方镜像启动的docker-compose文件 ]
|
||||
- `docker-compose-with-all-env.yml` [ 使用官方镜像启动的,内置了所有tlrtcfile.env配置的 docker-compose文件 ]
|
||||
- `docker/docker-compose-build-code.yml` [ 可以自己编译打包镜像的docker-compose文件 ]
|
||||
- `docker/mysql/mysql.env` [ mysql的外部挂载配置文件 ]
|
||||
- `docker/coturn/coturn.env` [ coturn的外部挂载配置文件 ]
|
||||
- `docker/coturn/turnserver-with-fixed-user.conf` [ coturn的项目默认内置的固定帐号模式的配置文件 ]
|
||||
- `docker/coturn/turnserver-with-secret-user.conf` [ coturn的项目默认内置的有时效性帐号模式的配置文件 ]
|
||||
|
||||
配置不算多,以上是算上所有服务后 (`api服务`, `socket服务`, `mysql服务`, `coturn服务`)的配置汇总
|
||||
|
||||
进行分类到不同服务后,每个服务可能需要修改的配置划分如下
|
||||
|
||||
### 部署api服务相关配置
|
||||
|
||||
可能需要修改
|
||||
|
||||
- `tlrtcfile.env`
|
||||
- `docker-compose.yml`
|
||||
- `docker-compose-with-all-env.yml`
|
||||
- `docker/docker-compose-build-code.yml`
|
||||
- `docker/mysql/mysql.env`
|
||||
- `docker/coturn/coturn.env`
|
||||
- `docker/coturn/turnserver-with-fixed-user.conf`
|
||||
- `docker/coturn/turnserver-with-secret-user.conf`
|
||||
|
||||
### 部署socket服务相关配置
|
||||
|
||||
可能需要修改
|
||||
|
||||
- `tlrtcfile.env`
|
||||
- `docker-compose.yml`
|
||||
- `docker-compose-with-all-env.yml`
|
||||
- `docker/docker-compose-build-code.yml`
|
||||
- `docker/mysql/mysql.env`
|
||||
- `docker/coturn/coturn.env`
|
||||
- `docker/coturn/turnserver-with-fixed-user.conf`
|
||||
- `docker/coturn/turnserver-with-secret-user.conf`
|
||||
|
||||
### 部署mysql服务相关配置
|
||||
|
||||
可能需要修改
|
||||
|
||||
- `tlrtcfile.env`
|
||||
- `docker-compose.yml`
|
||||
- `docker-compose-with-all-env.yml`
|
||||
- `docker/docker-compose-build-code.yml`
|
||||
- `docker/mysql/mysql.env`
|
||||
|
||||
### 部署coturn服务相关配置
|
||||
|
||||
可能需要修改
|
||||
|
||||
- `tlrtcfile.env`
|
||||
- `docker-compose.yml`
|
||||
- `docker-compose-with-all-env.yml`
|
||||
- `docker/docker-compose-build-code.yml`
|
||||
- `docker/coturn/coturn.env`
|
||||
- `docker/coturn/turnserver-with-fixed-user.conf`
|
||||
- `docker/coturn/turnserver-with-secret-user.conf`
|
||||
|
||||
我知道,看到这么多配置,有些小伙伴已经开始急了,但是先别急,不是每一个都需要改的,而是选择不同的部署模式,部署不同的服务,只是改对应的配置就好。不是全部要改,后面的文档,我将对不同部署模式需要修改的配置进行说明。
|
135
doc/gitbook/ENV_SETTING.md
Normal file
135
doc/gitbook/ENV_SETTING.md
Normal file
@@ -0,0 +1,135 @@
|
||||
下面先对项目默认配置文件 `tlrtcfile.env` 进行简单说明
|
||||
|
||||
# tlrtcfile.env
|
||||
|
||||
这个配置文件主要分为几部分,每个配置部分相对独立,不会相互影响
|
||||
|
||||
### 基础配置
|
||||
|
||||
**这个地方全部设置好,服务就可以基本使用了**
|
||||
|
||||
```
|
||||
#-----------------以下为基础配置-----------------#
|
||||
## api服务端口
|
||||
tl_rtc_file_api_port=9092
|
||||
## websocket服务端口
|
||||
tl_rtc_file_socket_port=8444
|
||||
## websocket服务地址
|
||||
tl_rtc_file_socket_host=127.0.0.1:8444
|
||||
```
|
||||
|
||||
### webrtc配置
|
||||
|
||||
**主要是coturn中继服务器相关,不是非必需配置,主要用于处理公网环境的p2p打洞失败的数据中转用的服务器**
|
||||
|
||||
```
|
||||
#-----------------以下为webrtc相关配置-----------------#
|
||||
## webrtc-stun中继服务地址
|
||||
tl_rtc_file_webrtc_stun_host=stun:127.0.0.1:3478
|
||||
## webrtc-turn中继服务地址
|
||||
tl_rtc_file_webrtc_turn_host=turn:127.0.0.1:3478?transport=udp
|
||||
## webrtc中继服务用户名
|
||||
tl_rtc_file_webrtc_turn_username=tlrtcfile
|
||||
## webrtc中继服务密码
|
||||
tl_rtc_file_webrtc_turn_credential=tlrtcfile
|
||||
## webrtc中继服务Secret
|
||||
tl_rtc_file_webrtc_turn_secret=tlrtcfile
|
||||
## webrtc中继服务帐号过期时间 (毫秒)
|
||||
tl_rtc_file_webrtc_turn_expire=86400000
|
||||
```
|
||||
|
||||
### mysql配置
|
||||
|
||||
**主要是mysql数据库相关的配置,主要用于后台管理统计,功能控制,取件码等场景,不是非必需**
|
||||
|
||||
```
|
||||
#-----------------以下为mysql数据库相关配置-----------------#
|
||||
## 是否开启数据库
|
||||
tl_rtc_file_db_open=false
|
||||
## 数据库地址
|
||||
tl_rtc_file_db_mysql_host=mysql
|
||||
## 数据库端口
|
||||
tl_rtc_file_db_mysql_port=3306
|
||||
## 数据库名称
|
||||
tl_rtc_file_db_mysql_dbName=webchat
|
||||
## 数据库用户名
|
||||
tl_rtc_file_db_mysql_user=tlrtcfile
|
||||
## 数据库密码
|
||||
tl_rtc_file_db_mysql_password=tlrtcfile
|
||||
```
|
||||
|
||||
### 后台管理配置
|
||||
|
||||
**主要是进入后台管理的管理员房间帐号配置,前提是需要设置好并开启数据库相关配置**
|
||||
|
||||
```
|
||||
#-----------------以下为管理后台相关配置-----------------#
|
||||
## 管理后台房间号
|
||||
tl_rtc_file_manage_room=tlrtcfile
|
||||
## 管理后台密码
|
||||
tl_rtc_file_manage_password=tlrtcfile
|
||||
```
|
||||
|
||||
### 告警通知配置
|
||||
|
||||
**主要用于系统异常告警,日常操作通知,需要注册企业微信,并传创建好群聊机器人**
|
||||
|
||||
```
|
||||
#-----------------以下为企业微信通知相关配置-----------------#
|
||||
## 企业微信通知开关
|
||||
tl_rtc_file_notify_open=false
|
||||
## 企业微信通知机器人KEY,正常通知,如果有多个key,逗号分隔
|
||||
tl_rtc_file_notify_qiwei_normal=
|
||||
## 企业微信通知机器人KEY,错误通知,如果有多个key,逗号分隔
|
||||
tl_rtc_file_notify_qiwei_error=
|
||||
```
|
||||
|
||||
### OpenAI聊天配置
|
||||
|
||||
**项目内置了一个聊天对话框,对接了chatgpt的开放接口,只需要注册并设置好自己的帐号key即可**
|
||||
|
||||
```
|
||||
#-----------------以下为openai相关配置-----------------#
|
||||
## openai-key,如果有多个key,逗号分隔
|
||||
tl_rtc_file_openai_keys=
|
||||
```
|
||||
|
||||
|
||||
### Oss云存储配置
|
||||
|
||||
目前还不是特别完善,后续会逐步更新主流厂商,目前仅支持了seafile私有云盘
|
||||
|
||||
**主要是用于目前已有的取件码形式,和后续会支持的多厂商云存储,打造可以自己私有化部署的一套式文件传输,暂存文件等功能等在线应用**
|
||||
|
||||
```
|
||||
#-----------------以下为oss相关配置-----------------#
|
||||
## oss-seafile存储库ID
|
||||
tl_rtc_file_oss_seafile_repoid=
|
||||
## oss-seafile地址
|
||||
tl_rtc_file_oss_seafile_host=
|
||||
## oss-seafile用户名
|
||||
tl_rtc_file_oss_seafile_username=
|
||||
## oss-seafile密码
|
||||
tl_rtc_file_oss_seafile_password=
|
||||
|
||||
## oss-alyun存储accessKey
|
||||
tl_rtc_file_oss_alyun_AccessKey=
|
||||
## oss-aly存储SecretKey
|
||||
tl_rtc_file_oss_alyun_Secretkey=
|
||||
## oss-aly存储bucket
|
||||
tl_rtc_file_oss_alyun_bucket=
|
||||
|
||||
## oss-txyun存储accessKey
|
||||
tl_rtc_file_oss_txyun_AccessKey=
|
||||
## oss-txyunt存储SecretKey
|
||||
tl_rtc_file_oss_txyun_Secretkey=
|
||||
## oss-txyun存储bucket
|
||||
tl_rtc_file_oss_txyun_bucket=
|
||||
|
||||
## oss-qiniuyun存储accessKey
|
||||
tl_rtc_file_oss_qiniuyun_AccessKey=
|
||||
## oss-qiniuyunt存储SecretKey
|
||||
tl_rtc_file_oss_qiniuyun_Secretkey=
|
||||
## oss-qiniuyun存储bucket
|
||||
tl_rtc_file_oss_qiniuyun_bucket=
|
||||
```
|
25
doc/gitbook/FAQ.md
Normal file
25
doc/gitbook/FAQ.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# 常见问题
|
||||
|
||||
##
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
##
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
##
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
##
|
||||
|
||||
```
|
||||
|
||||
```
|
7
doc/gitbook/README.md
Normal file
7
doc/gitbook/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
## tl-rtc-file
|
||||
|
||||
欢迎使用,觉得不错的可以在github或gitee上面start/watch/fork 一键三连,感兴趣/有开发能力的可以提交你的代码共同维护
|
||||
|
||||
## 如果觉得项目不错,可以赞赏支持开发者进行后续的维护
|
||||
|
||||

|
1
doc/gitbook/README_DEV.md
Normal file
1
doc/gitbook/README_DEV.md
Normal file
@@ -0,0 +1 @@
|
||||
# tl-rtc-file二次开发手册
|
69
doc/gitbook/SUMMARY.md
Normal file
69
doc/gitbook/SUMMARY.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# Summary
|
||||
|
||||
* [tl-rtc-file-使用手册](README.md)
|
||||
|
||||
* [选择安装模式](install/INSTALL.md)
|
||||
|
||||
* [命令部署](install/INSTALL_BY_COMMAND.md)
|
||||
|
||||
* [服务器一键脚本部署](install/INSTALL_BY_COMMAND_SHELL.md)
|
||||
|
||||
* [官方docker-compose挂载配置部署](install/INSTALL_BY_DOCKER_COMPOSE.md)
|
||||
|
||||
* [官方docker-compose内置配置部署](install/INSTALL_BY_DOCKER_COMPOSE_WITH_ENV.md)
|
||||
|
||||
* [自行打包镜像-docker-compose部署](install/INSTALL_BY_DOCKER_COMPOSE_BUILD_CODE.md)
|
||||
|
||||
* [官方docker镜像一键脚本](install/INSTALL_BY_DOCKER_SHELL.md)
|
||||
|
||||
* [托管平台-zeabur部署](install/INSTALL_BY_ZEABUR.md)
|
||||
|
||||
* [项目配置统计](CONF_ALL.md)
|
||||
|
||||
* [配置简要说明](ENV_SETTING.md)
|
||||
|
||||
* [tl-rtc-file-开发手册](README_DEV.md)
|
||||
|
||||
* [设计简介](dev/INTRO.md)
|
||||
|
||||
* [客户端](dev/client/CLIENT.md)
|
||||
|
||||
* [文字聊天](dev/client/CHAT.md)
|
||||
|
||||
* [文件传输](dev/client/FILE.md)
|
||||
|
||||
* [多人音视频](dev/client/VIDEO.md)
|
||||
|
||||
* [多人屏幕共享](dev/client/SCREEN.md)
|
||||
|
||||
* [单人直播](dev/client/LIVE.md)
|
||||
|
||||
* [多人语音](dev/client/AUDIO.md)
|
||||
|
||||
* [文件暂存/取件码](dev/client/FILE_CODE.md)
|
||||
|
||||
* [多人画笔](dev/client/DRAW.md)
|
||||
|
||||
* [屏幕录制](dev/client/RECODE.md)
|
||||
|
||||
* [服务端](dev/SVR.md)
|
||||
|
||||
* [文字聊天](dev/svr/CHAT.md)
|
||||
|
||||
* [文件传输](dev/svr/FILE.md)
|
||||
|
||||
* [多人音视频](dev/svr/VIDEO.md)
|
||||
|
||||
* [多人屏幕共享](dev/svr/SCREEN.md)
|
||||
|
||||
* [单人直播](dev/svr/LIVE.md)
|
||||
|
||||
* [多人语音](dev/svr/AUDIO.md)
|
||||
|
||||
* [文件暂存/取件码](dev/svr/FILE_CODE.md)
|
||||
|
||||
* [多人画笔](dev/svr/DRAW.md)
|
||||
|
||||
* [屏幕录制](dev/svr/RECODE.md)
|
||||
|
||||
* [tl-rtc-file-常见问题列表](FAQ.md)
|
14
doc/gitbook/book.json
Normal file
14
doc/gitbook/book.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"title": "tl-rtc-file",
|
||||
"language": "zh-hans",
|
||||
"author": "iamtsm",
|
||||
"links": {
|
||||
"sidebar": {
|
||||
"iamtsm": "https://im.iamtsm.cn"
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"-search",
|
||||
"-sharing"
|
||||
]
|
||||
}
|
BIN
doc/gitbook/coffee.jpeg
Normal file
BIN
doc/gitbook/coffee.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
1
doc/gitbook/dev/CLIENT.md
Normal file
1
doc/gitbook/dev/CLIENT.md
Normal file
@@ -0,0 +1 @@
|
||||
# 客户端代码 (浏览器)
|
1
doc/gitbook/dev/INTRO.md
Normal file
1
doc/gitbook/dev/INTRO.md
Normal file
@@ -0,0 +1 @@
|
||||
# 项目设计简介
|
1
doc/gitbook/dev/SVR.md
Normal file
1
doc/gitbook/dev/SVR.md
Normal file
@@ -0,0 +1 @@
|
||||
# 服务端
|
1
doc/gitbook/dev/client/AUDIO.md
Normal file
1
doc/gitbook/dev/client/AUDIO.md
Normal file
@@ -0,0 +1 @@
|
||||
# 多人语音
|
1
doc/gitbook/dev/client/CHAT.md
Normal file
1
doc/gitbook/dev/client/CHAT.md
Normal file
@@ -0,0 +1 @@
|
||||
# 文字聊天
|
1
doc/gitbook/dev/client/DRAW.md
Normal file
1
doc/gitbook/dev/client/DRAW.md
Normal file
@@ -0,0 +1 @@
|
||||
# 多人画笔
|
1
doc/gitbook/dev/client/FILE.md
Normal file
1
doc/gitbook/dev/client/FILE.md
Normal file
@@ -0,0 +1 @@
|
||||
# 文件传输
|
1
doc/gitbook/dev/client/FILE_CODE.md
Normal file
1
doc/gitbook/dev/client/FILE_CODE.md
Normal file
@@ -0,0 +1 @@
|
||||
# 文件暂存/取件码
|
1
doc/gitbook/dev/client/LIVE.md
Normal file
1
doc/gitbook/dev/client/LIVE.md
Normal file
@@ -0,0 +1 @@
|
||||
# 单人直播
|
1
doc/gitbook/dev/client/RECODE.md
Normal file
1
doc/gitbook/dev/client/RECODE.md
Normal file
@@ -0,0 +1 @@
|
||||
# 屏幕录制
|
1
doc/gitbook/dev/client/SCREEN.md
Normal file
1
doc/gitbook/dev/client/SCREEN.md
Normal file
@@ -0,0 +1 @@
|
||||
# 多人屏幕共享
|
1
doc/gitbook/dev/client/VIDEO.md
Normal file
1
doc/gitbook/dev/client/VIDEO.md
Normal file
@@ -0,0 +1 @@
|
||||
# 多人音视频
|
1
doc/gitbook/dev/svr/AUDIO.md
Normal file
1
doc/gitbook/dev/svr/AUDIO.md
Normal file
@@ -0,0 +1 @@
|
||||
# 多人语音
|
1
doc/gitbook/dev/svr/CHAT.md
Normal file
1
doc/gitbook/dev/svr/CHAT.md
Normal file
@@ -0,0 +1 @@
|
||||
# 文字聊天
|
1
doc/gitbook/dev/svr/DRAW.md
Normal file
1
doc/gitbook/dev/svr/DRAW.md
Normal file
@@ -0,0 +1 @@
|
||||
# 多人画笔
|
1
doc/gitbook/dev/svr/FILE.md
Normal file
1
doc/gitbook/dev/svr/FILE.md
Normal file
@@ -0,0 +1 @@
|
||||
# 文件传输
|
1
doc/gitbook/dev/svr/FILE_CODE.md
Normal file
1
doc/gitbook/dev/svr/FILE_CODE.md
Normal file
@@ -0,0 +1 @@
|
||||
# 文件暂存/取件码
|
1
doc/gitbook/dev/svr/LIVE.md
Normal file
1
doc/gitbook/dev/svr/LIVE.md
Normal file
@@ -0,0 +1 @@
|
||||
# 单人直播
|
1
doc/gitbook/dev/svr/RECODE.md
Normal file
1
doc/gitbook/dev/svr/RECODE.md
Normal file
@@ -0,0 +1 @@
|
||||
# 屏幕录制
|
1
doc/gitbook/dev/svr/SCREEN.md
Normal file
1
doc/gitbook/dev/svr/SCREEN.md
Normal file
@@ -0,0 +1 @@
|
||||
# 多人屏幕共享
|
1
doc/gitbook/dev/svr/VIDEO.md
Normal file
1
doc/gitbook/dev/svr/VIDEO.md
Normal file
@@ -0,0 +1 @@
|
||||
# 多人音视频
|
26
doc/gitbook/install/INSTALL.md
Normal file
26
doc/gitbook/install/INSTALL.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# 选择你的私有部署形式
|
||||
|
||||
无论是哪种模式部署,都需要先修改好 `tlrtcfile.env`, 具体配置说明在配置部分进行查看, 再进行安装,启动。
|
||||
|
||||
目前支持一下几种自行部署方式,可根据你的需要选择一种,进行私有化部署,具体安装细节流程参考具体模式下的文档
|
||||
|
||||
- `命令部署` : 支持windows, linux, macos各种系统。
|
||||
|
||||
- `服务器一键脚本部署` : 支持ubuntu16/18/20系统一键脚本, windows一键脚本
|
||||
|
||||
- `官方docker镜像一键脚本` : 支持arm64, amd64/v8两种架构的系统
|
||||
|
||||
- `官方docker-compose挂载配置部署` : 支持arm64, amd64/v8两种架构的系统
|
||||
|
||||
- `官方docker-compose内置配置部署` : 支持arm64, amd64/v8两种架构的系统
|
||||
|
||||
- `自行打包镜像-docker-compose部署` : 支持arm64, amd64/v8两种架构的系统,以及其他架构的系统(取决于你的打包机器所属的架构)
|
||||
|
||||
- `托管平台-zeabur部署` : 目前支持基础的服务(api, socket)的一键部署
|
||||
|
||||
|
||||
在选择模式部署之前,可以先查看阅读一下配置相关文档
|
||||
|
||||
[项目配置统计](../CONF_ALL.md)
|
||||
|
||||
[配置简要说明](../ENV_SETTING.md)
|
200
doc/gitbook/install/INSTALL_BY_COMMAND.md
Normal file
200
doc/gitbook/install/INSTALL_BY_COMMAND.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# 通过命令部署
|
||||
|
||||
本项目依赖的基础环境为 nodejs, npm, 有了这两个就可以运行 `api服务`, `socket服务` 这两个基础服务了。
|
||||
|
||||
先下载项目,可以去github下载源码包(优先推荐),也可以在gitee下载源码包(可能不是最新版),也可以通过git下载源码包
|
||||
|
||||
git下载:
|
||||
|
||||

|
||||
|
||||
### 第一步: 安装node
|
||||
|
||||
可以直接去nodejs官方网站下载版本,建议nodejs版本选在 `14.21.x` 以上,`18.x` 以内,因为其他版本没测试过,也许/可能/存在问题
|
||||
|
||||
安装完nodejs后,npm也是配套会安装好,windows/linux/macos用户操作都一样,网络上安装nodejs教程很多,这里不再阐述。
|
||||
|
||||
### 第二步: 修改配置
|
||||
|
||||
这种模式下,只需要修改 `tlrtcfile.env` 配置文件。以下是配置示例供参考
|
||||
|
||||
|
||||
```
|
||||
#-----------------以下为基础配置-----------------#
|
||||
## api服务端口
|
||||
tl_rtc_file_api_port=9092
|
||||
## websocket服务端口
|
||||
tl_rtc_file_socket_port=8444
|
||||
## websocket服务地址
|
||||
tl_rtc_file_socket_host=127.0.0.1:8444
|
||||
```
|
||||
|
||||
`tl_rtc_file_socket_host` 这个配置需要注意填写的内容
|
||||
|
||||
- 填 `127.0.0.1:8444` 只能在自己的机器上访问,局域网和公网都访问不了。
|
||||
- 填 `局域网ip:8444` 或者 `局域网域名:8444` 只能在自己机器和局域网访问,公网访问不了
|
||||
- 填 `公网ip:8444` 或者 `公网域名:8444`, 自己机器,局域网,公网都可以访问
|
||||
|
||||

|
||||
|
||||
### 第三步: 进入svr目录
|
||||
|
||||
```
|
||||
cd svr/
|
||||
```
|
||||
|
||||
后续的 npm 命令都是在svr目录下,进行操作
|
||||
|
||||
|
||||
安装依赖
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 第四步: 压缩静态资源文件
|
||||
|
||||
```
|
||||
npm run build:pro
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 第五步: 启动api服务
|
||||
|
||||
浏览器只允许在localhost和https环境下获取摄像头/屏幕共享权限,所以优先推荐 `npm run https-api` 启动
|
||||
|
||||
```
|
||||
npm run http-api
|
||||
```
|
||||
|
||||

|
||||
|
||||
或者
|
||||
|
||||
```
|
||||
npm run https-api
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 第六步: 启动socket服务
|
||||
|
||||
浏览器只允许在localhost和https环境下获取摄像头/屏幕共享权限,所以优先推荐 `npm run https-socket` 启动
|
||||
|
||||
```
|
||||
npm run http-socket
|
||||
```
|
||||
|
||||

|
||||
|
||||
或者
|
||||
|
||||
```
|
||||
npm run https-socket
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 第七步: 访问api服务
|
||||
|
||||
打开你的浏览器输入你的api服务所在的机器ip或者域名即可进行访问
|
||||
|
||||
`http://localhost:9092` 或者 `http://局域网ip:9092` 或者 `http://局域网域名:9092` 或者 `http://公网ip:9092` 或者 `http://公网域名:9092`
|
||||
|
||||

|
||||
|
||||
当然,我这里只是示例,实际操作下,可以对外提供一个域名,将域名反向代理到内部服务的9092端口,这样就可以对外用一个专有域名进行访问了
|
||||
|
||||
### pm2扩展服务安装
|
||||
|
||||

|
||||
|
||||
由于npm启动的命令,会将进程挂在终端中,关闭终端,服务就退出了,所以这个时候我们需要将服务挂在后台运行,我们可以安装pm2进行服务管理
|
||||
|
||||
全局安装pm2
|
||||
|
||||
```
|
||||
npm install pm2 -g
|
||||
```
|
||||
|
||||

|
||||
|
||||
安装完毕后,执行命令
|
||||
|
||||
**启动api服务**
|
||||
|
||||
```
|
||||
pm2 start npm --name=tl-rtc-file-api -- run http-api
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```
|
||||
pm2 start npm --name=tl-rtc-file-api -- run https-api
|
||||
```
|
||||
|
||||
**启动socket服务**
|
||||
|
||||
```
|
||||
pm2 start npm --name=tl-rtc-file-socket -- run http-socket
|
||||
```
|
||||
|
||||
或者
|
||||
|
||||
```
|
||||
pm2 start npm --name=tl-rtc-file-socket -- run https-socket
|
||||
```
|
||||
|
||||

|
||||
|
||||
### mysql扩展服务安装
|
||||
|
||||
恰好你你需要后台管理功能,那么推荐你配置好mysql服务,这样你可以对你的数据进行统计管理,还有更加详细的管理功能等待你探索,如果你没有安装过mysql,那么可以先安装mysql,这里就不再阐述,网络很多教程。
|
||||
|
||||
安装完mysql后,需要新建一个数据库,名称为 `webchat`(也可以自定义),然后根据你数据库的设置项,同步修改好 `tlrtcfile.env` 中的数据库相关配置
|
||||
|
||||
```
|
||||
#-----------------以下为mysql数据库相关配置-----------------#
|
||||
## 是否开启数据库
|
||||
tl_rtc_file_db_open=true
|
||||
## 数据库地址
|
||||
tl_rtc_file_db_mysql_host=填写你的数据库地址,比如 127.0.0.1
|
||||
## 数据库端口
|
||||
tl_rtc_file_db_mysql_port=3306
|
||||
## 数据库名称
|
||||
tl_rtc_file_db_mysql_dbName=填写你的数据库名称, 比如 webchat
|
||||
## 数据库用户名
|
||||
tl_rtc_file_db_mysql_user=填写你的数据库用户名称
|
||||
## 数据库密码
|
||||
tl_rtc_file_db_mysql_password=填写你的数据库用户密码
|
||||
```
|
||||
|
||||
### coturn扩展服务安装
|
||||
|
||||
如果你发现部署之后的服务,在某些用户的某些情况下,出现了文件发送不了,直播看不到,音视频打不通,屏幕共享没反应,用户连接显示 disconnected。
|
||||
有很大情况是由于p2p直连失败,这个时候就需要依赖coturn服务进行数据中转了。
|
||||
|
||||
由于环境和系统的情况因素太多,网上也有很多教程, coturn的安装我也不阐述了
|
||||
|
||||
但是配置文件还是优先推荐使用我这个项目中默认提供的两个配置(请使用有效帐号模式)
|
||||
|
||||
`docker/coturn/turnserver-with-secret-user.conf`, 修改好这个配置后,使用这个配置进行启动,启动后按照配置同步修改好 `tlrtcfile.env` 中的webrtc相关配置
|
||||
|
||||
```
|
||||
#-----------------以下为webrtc相关配置-----------------#
|
||||
## webrtc-stun中继服务地址
|
||||
tl_rtc_file_webrtc_stun_host=stun:127.0.0.1:3478
|
||||
## webrtc-turn中继服务地址
|
||||
tl_rtc_file_webrtc_turn_host=turn:127.0.0.1:3478?transport=udp
|
||||
## webrtc中继服务用户名
|
||||
tl_rtc_file_webrtc_turn_username=tlrtcfile
|
||||
## webrtc中继服务密码
|
||||
tl_rtc_file_webrtc_turn_credential=tlrtcfile
|
||||
## webrtc中继服务Secret
|
||||
tl_rtc_file_webrtc_turn_secret=tlrtcfile
|
||||
## webrtc中继服务帐号过期时间 (毫秒)
|
||||
tl_rtc_file_webrtc_turn_expire=86400000
|
||||
```
|
62
doc/gitbook/install/INSTALL_BY_COMMAND_SHELL.md
Normal file
62
doc/gitbook/install/INSTALL_BY_COMMAND_SHELL.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# 通过命令一键脚本部署
|
||||
|
||||
由于每个人的机器/环境都是有细微区别的,但是脚本能处理的情况有限,所以选择这种模式,有一定几率不能正常运行。但是可以遇到具体情况具体分析,或者可以加群反馈问题或者建议, QQ群 : 624214498
|
||||
|
||||
目前支持 `ubuntu16`, `ubuntu18`, `ubuntu20`, `windows` 这几种自动脚本。
|
||||
|
||||
### ubuntu16/18/20/macos
|
||||
|
||||
脚本放在 bin/ubuntu相关目录下,进入 bin/ubuntu对应的目录,有五个脚本,分别作用如下:
|
||||
|
||||
- `auto-check-install-http.sh` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-http.sh`** 服务脚本
|
||||
- `auto-check-install-https.sh` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-https.sh`** 服务脚本
|
||||
- `auto-start-http.sh` pm2后台启动 **http** 服务脚本
|
||||
- `auto-start-https.sh` pm2后台启动 **https** 服务脚本
|
||||
- `auto-stop.sh` pm2删除服务进程
|
||||
|
||||
这里相应的 **http** 和 **https** 脚本选一种执行就好,下面的文档以 **http** 模式进行说明,如果需要以 **https** 脚本操作,**http** 替换为 **https** 即可
|
||||
|
||||
#### 初次安装启动
|
||||
|
||||
使用 `auto-check-install-http.sh` 脚本
|
||||
|
||||
```
|
||||
./auto-check-install-http.sh
|
||||
```
|
||||
|
||||
如果没有执行权限,先给脚本添加权限 `chmod +x ./auto-check-install-http.sh`
|
||||
|
||||

|
||||
|
||||
#### 停止服务
|
||||
|
||||
使用 `auto-stop.sh` 脚本
|
||||
|
||||
```
|
||||
./auto-stop.sh
|
||||
```
|
||||
|
||||
如果没有执行权限,先给脚本添加权限 `chmod +x ./auto-stop.sh`
|
||||
|
||||

|
||||
|
||||
#### 非初次安装启动
|
||||
|
||||
使用 `auto-start-http.sh` 脚本, 也可以沿用之前的 `auto-check-install-http.sh` 脚本
|
||||
|
||||
```
|
||||
./auto-check-install-http.sh
|
||||
```
|
||||
|
||||
### windows
|
||||
|
||||
- `auto-check-install-http.bat` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-http.bat`** 服务脚本
|
||||
- `auto-check-install-https.bat` 自动检测环境 + 安装环境 + 检测端口占用 + 调用 **`auto-start-https.bat`** 服务脚本
|
||||
- `auto-start-http.bat` pm2后台启动 **http** 服务脚本
|
||||
- `auto-start-https.bat` pm2后台启动 **https** 服务脚本
|
||||
|
||||
具体操作如ubuntu所示例,脚本内容如有问题,请反馈
|
||||
|
||||
### centeros
|
||||
|
||||
自动脚本待补充...
|
57
doc/gitbook/install/INSTALL_BY_DOCKER_COMPOSE.md
Normal file
57
doc/gitbook/install/INSTALL_BY_DOCKER_COMPOSE.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# docker-compose官方镜像部署
|
||||
|
||||
项目默认提供了已经打包好的镜像放在dockerhub,供大家下载使用。并提供了 `docker-compose.yml` 配置,供大家使用
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 需要安装好docker-compose,V1或者V2都可以
|
||||
|
||||
- DockerHub需要科学上网
|
||||
|
||||
- 需要下载源码包
|
||||
|
||||
### 准备启动
|
||||
|
||||
根据你的 `Docker Compose` 版本在 `主目录` 执行如下对应的命令
|
||||
|
||||
- 对于 `Docker Compose V1`
|
||||
|
||||
```
|
||||
docker-compose --profile=http up -d
|
||||
```
|
||||
|
||||
- 对于 `Docker Compose V2`
|
||||
|
||||
```
|
||||
docker compose --profile=http up -d
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
由于项目的服务需要依赖一些配置,所以容器方式都挂载了一些配置文件。
|
||||
|
||||
api容器和socket容器 挂载配置如下
|
||||
|
||||
```
|
||||
volumes:
|
||||
- ./tlrtcfile.env:/tlrtcfile/tlrtcfile.env
|
||||
```
|
||||
|
||||
mysql容器的挂载配置如下,作用是: 将mysql容器内的数据文件挂载出来进行保存
|
||||
|
||||
```
|
||||
volumes:
|
||||
- ./docker/mysql/data/db:/var/lib/mysql
|
||||
- ./docker/mysql/data/my.cnf:/etc/mysql/conf.d/my.cnf
|
||||
- ./docker/mysql/data/log:/var/log/mysql
|
||||
- ./docker/mysql/data/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
```
|
||||
|
||||
coturn的挂载配置,作用是: 将项目默认提供的turn的配置,提供给coturn容器使用
|
||||
|
||||
```
|
||||
volumes:
|
||||
- ./docker/coturn/turnserver-with-secret-user.conf:/etc/turnserver.conf
|
||||
```
|
||||
|
||||
如果觉得这种方式麻烦,或者不合适自己的场景,可以选择另外一种 `内置配置的docker-compose`
|
63
doc/gitbook/install/INSTALL_BY_DOCKER_COMPOSE_BUILD_CODE.md
Normal file
63
doc/gitbook/install/INSTALL_BY_DOCKER_COMPOSE_BUILD_CODE.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# docker-compose自行打包部署
|
||||
|
||||
项目默认提供了 `docker/docker-compose-build-code.yml` 配置,供大家自行打包源码镜像使用。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 需要安装好docker-compose,V1或者V2都可以
|
||||
|
||||
- DockerHub需要科学上网
|
||||
|
||||
- 需要下载源码包
|
||||
|
||||
### 准备打包
|
||||
|
||||
进入 `docker/` 目录后根据你的 `Docker Compose` 版本在主目录执行如下对应的命令
|
||||
|
||||
- 对于 `Docker Compose V1`
|
||||
|
||||
```
|
||||
docker-compose -f docker-compose-build-code.yml build
|
||||
```
|
||||
|
||||
- 对于 `Docker Compose V2`
|
||||
|
||||
```
|
||||
docker compose -f docker-compose-build-code.yml build
|
||||
```
|
||||
|
||||
### 启动容器
|
||||
|
||||
按照默认配置打包好镜像后,随后可以参照 `官方docker镜像一键脚本` 文档,进行启动即可。
|
||||
|
||||
**如果有变动镜像名称或者容器名称之类的操作,需要注意同步修改相关启动配置**
|
||||
|
||||
### 注意事项
|
||||
|
||||
由于项目的服务需要依赖一些配置,所以容器方式都挂载了一些配置文件。
|
||||
|
||||
api容器和socket容器 挂载配置如下
|
||||
|
||||
```
|
||||
volumes:
|
||||
- ./tlrtcfile.env:/tlrtcfile/tlrtcfile.env
|
||||
```
|
||||
|
||||
mysql容器的挂载配置如下,作用是: 将mysql容器内的数据文件挂载出来进行保存
|
||||
|
||||
```
|
||||
volumes:
|
||||
- ./docker/mysql/data/db:/var/lib/mysql
|
||||
- ./docker/mysql/data/my.cnf:/etc/mysql/conf.d/my.cnf
|
||||
- ./docker/mysql/data/log:/var/log/mysql
|
||||
- ./docker/mysql/data/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
```
|
||||
|
||||
coturn的挂载配置,作用是: 将项目默认提供的turn的配置,提供给coturn容器使用
|
||||
|
||||
```
|
||||
volumes:
|
||||
- ./docker/coturn/turnserver-with-secret-user.conf:/etc/turnserver.conf
|
||||
```
|
||||
|
||||
如果觉得这种方式麻烦,或者不合适自己的场景,可以选择另外一种 `内置配置的docker-compose`
|
56
doc/gitbook/install/INSTALL_BY_DOCKER_COMPOSE_WITH_ENV.md
Normal file
56
doc/gitbook/install/INSTALL_BY_DOCKER_COMPOSE_WITH_ENV.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# docker-compose官方镜像部署 - 内置配置文件形式
|
||||
|
||||
项目默认提供了已经打包好的镜像放在dockerhub,供大家下载使用。并提供了 `docker-compose-with-all-env.yml` 配置,供大家使用。
|
||||
|
||||
这种方式和前一种 `docker-compose.yml` 的唯一区别就是,这个方式内置了大部分配置,无需依赖挂载文件。
|
||||
|
||||
即使不下载源码,单独下载这个yml文件,也可以部署。
|
||||
|
||||
### 前置条件
|
||||
|
||||
- 需要安装好docker-compose,V1或者V2都可以
|
||||
|
||||
- DockerHub需要科学上网
|
||||
|
||||
- 需要修改好内置配置的数据,具体可以参考 [配置简要说明](../ENV_SETTING.md)
|
||||
|
||||
- 如果不下载源码包,且你需要配置挂载文件/目录的时候,需要按需下载/设置好挂载依赖的目录/文件,如果下载源码包则可以忽略这一条件
|
||||
|
||||
### 准备启动
|
||||
|
||||
根据你的 `Docker Compose` 版本在 `主目录` 执行如下对应的命令
|
||||
|
||||
- 对于 `Docker Compose V1`
|
||||
|
||||
```
|
||||
docker-compose --profile=http up -d
|
||||
```
|
||||
|
||||
- 对于 `Docker Compose V2`
|
||||
|
||||
```
|
||||
docker compose --profile=http up -d
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
虽然内置了大部分配置,但是还有部分配置依然是需要通过挂载的形式,但挂载的配置不是必需存在,如不需要可以注释挂载代码,目前有两个容器有挂载配置。
|
||||
|
||||
但是注释挂载设置后,将会带来一些小问题,如果你可以接受的话。
|
||||
|
||||
mysql容器的挂载配置如下,作用是: 将mysql容器内的数据文件挂载出来进行保存
|
||||
|
||||
```
|
||||
volumes:
|
||||
- ./docker/mysql/data/db:/var/lib/mysql
|
||||
- ./docker/mysql/data/my.cnf:/etc/mysql/conf.d/my.cnf
|
||||
- ./docker/mysql/data/log:/var/log/mysql
|
||||
- ./docker/mysql/data/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
```
|
||||
|
||||
coturn的挂载配置,作用是: 将项目默认提供的turn的配置,提供给coturn容器使用
|
||||
|
||||
```
|
||||
volumes:
|
||||
- ./docker/coturn/turnserver-with-secret-user.conf:/etc/turnserver.conf
|
||||
```
|
25
doc/gitbook/install/INSTALL_BY_DOCKER_SHELL.md
Normal file
25
doc/gitbook/install/INSTALL_BY_DOCKER_SHELL.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# docker命令一键脚本部署
|
||||
|
||||
为了简便大家的部署操作,项目默认提供了docker命令的一键部署脚本,macos, ubuntu都可以用
|
||||
|
||||
脚本放在 `bin/` 目录中,脚本名称为 `auto-pull-and-start-docker.sh`
|
||||
|
||||
### 进入脚本目录
|
||||
|
||||
```
|
||||
cd bin/
|
||||
```
|
||||
|
||||
### 给脚本执行权限
|
||||
|
||||
```
|
||||
chmod +x ./auto-pull-and-start-docker.sh
|
||||
```
|
||||
|
||||
### 执行脚本
|
||||
|
||||
```
|
||||
./auto-pull-and-start-docker.sh
|
||||
```
|
||||
|
||||

|
9
doc/gitbook/install/INSTALL_BY_ZEABUR.md
Normal file
9
doc/gitbook/install/INSTALL_BY_ZEABUR.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 通过zeabur托管平台部署
|
||||
|
||||
这种托管平台一键部署的形式,目前仅仅开发支持了 `api服务` 和 `socket服务` 仅提供基础功能可用性
|
||||
|
||||
`mysql服务` 和 `coturn服务`都是暂不提供的,并且代码分支可能存在同步不及时的情况。
|
||||
|
||||
所以这就导致了,托管平台一键部署的有一些使用局限性
|
||||
|
||||
后面有时间会继续同步更新这种部署模式
|
@@ -1,3 +0,0 @@
|
||||
# 配置修改
|
||||
|
||||
无论是哪种模式安装,都需要先修改配置,再进行安装,启动。
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "10.4.6",
|
||||
"version": "10.4.7",
|
||||
"socket": {
|
||||
"port": "请到 tlrtcfile.env 中进行配置",
|
||||
"host": "请到 tlrtcfile.env 中进行配置"
|
||||
|
@@ -39,8 +39,7 @@
|
||||
.tl-rtc-file-github-logo{
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
margin-top: 16px;
|
||||
margin-left: -30px;
|
||||
}
|
||||
.tl-rtc-file-intro-content{
|
||||
@@ -129,13 +128,25 @@
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
.layui-nav .layui-nav-item a {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.layui-nav{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<body class="layui-layout-body">
|
||||
|
||||
<a href="#" class="tl-rtc-file-logo" > tl-rtc-file </a>
|
||||
<ul class="layui-nav">
|
||||
<li class="layui-nav-item"><a target="_blank" onclick="donate()" style="color: black; font-weight: bold;">获取捐赠版</a></li>
|
||||
<li class="layui-nav-item" ><a style="color: black; font-weight: bold;" href="/document">文档</a></li>
|
||||
<li class="layui-nav-item"><a target="_blank" onclick="donate()" style="color: black; font-weight: bold;">捐赠</a></li>
|
||||
<li class="layui-nav-item"><a target="_blank" onclick="donateDev()" style="color: black; font-weight: bold;">定制开发</a></li>
|
||||
<li class="layui-nav-item" ><a style="color: black; font-weight: bold;" href="home_en.html">EN</a></li>
|
||||
|
||||
<li class="layui-nav-item">
|
||||
<a href="https://github.com/iamtsm/tl-rtc-file" style="margin-left: 20px;" target="_blank">
|
||||
<svg class="tl-rtc-file-github-logo" viewBox="0 0 1024 1024" p-id="1341" width="128" height="128">
|
||||
@@ -143,7 +154,6 @@
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-nav-item" ><a style="color: black; font-weight: bold;" href="home_en.html">EN</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tl-rtc-file-intro-content">
|
||||
@@ -189,6 +199,20 @@
|
||||
window.layer = layui.layer;
|
||||
});
|
||||
window.donate = function(){
|
||||
if(window.layer){
|
||||
layer.open({
|
||||
type: 1
|
||||
, title: false
|
||||
, closeBtn: false
|
||||
, area: ["300px","300px"]
|
||||
, shade: 0.5
|
||||
, shadeClose : true
|
||||
, id: 'layui-info-msg'
|
||||
, content: '<img src="https://im.iamtsm.cn/image/coffee.jpeg" style="width: 100%; height: 100%;"></img>'
|
||||
});
|
||||
}
|
||||
}
|
||||
window.donateDev = function(){
|
||||
if(window.layer){
|
||||
layer.open({
|
||||
type: 2
|
||||
|
@@ -39,8 +39,7 @@
|
||||
.tl-rtc-file-github-logo{
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
margin-top: 16px;
|
||||
margin-left: -30px;
|
||||
}
|
||||
.tl-rtc-file-intro-content{
|
||||
@@ -128,13 +127,24 @@
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
|
||||
.layui-nav .layui-nav-item a {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.layui-nav{
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body class="layui-layout-body">
|
||||
|
||||
<a href="#" class="tl-rtc-file-logo"> tl-rtc-file </a>
|
||||
<ul class="layui-nav">
|
||||
<li class="layui-nav-item" ><a style="color: black; font-weight: bold;" href="/document">Doc</a></li>
|
||||
<li class="layui-nav-item"><a target="_blank" onclick="donate()" style="color: black; font-weight: bold;">Donat</a></li>
|
||||
<li class="layui-nav-item"><a target="_blank" onclick="donateDev()" style="color: black; font-weight: bold;">Pro</a></li>
|
||||
<li class="layui-nav-item"><a style="color: black; font-weight: bold;" href="home.html">中文</a></li>
|
||||
<li class="layui-nav-item">
|
||||
<a href="https://github.com/iamtsm/tl-rtc-file" style="margin-left: 20px;" target="_blank">
|
||||
<svg class="tl-rtc-file-github-logo" viewBox="0 0 1024 1024" p-id="1341" width="128" height="128">
|
||||
@@ -142,12 +152,11 @@
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-nav-item"><a style="color: black; font-weight: bold;" href="home.html">中文</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tl-rtc-file-intro-content">
|
||||
<div class="tl-rtc-file-intro-main">
|
||||
webrtc based media streaming tool
|
||||
media streaming tool based webrtc
|
||||
</div>
|
||||
<div style="margin-top: 30px;">
|
||||
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/tl-open-source/tl-rtc-file?style=social">
|
||||
@@ -191,6 +200,20 @@
|
||||
document.querySelector(".layui-layer-content").style.borderRadius = "15px"
|
||||
});
|
||||
window.donate = function(){
|
||||
if(window.layer){
|
||||
layer.open({
|
||||
type: 1
|
||||
, title: false
|
||||
, closeBtn: false
|
||||
, area: ["300px","300px"]
|
||||
, shade: 0.5
|
||||
, shadeClose : true
|
||||
, id: 'layui-info-msg'
|
||||
, content: '<img src="https://im.iamtsm.cn/image/coffee.jpeg" style="width: 100%; height: 100%;"></img>'
|
||||
});
|
||||
}
|
||||
}
|
||||
window.donateDev = function(){
|
||||
if(window.layer){
|
||||
layer.open({
|
||||
type: 2
|
||||
|
@@ -108,12 +108,18 @@
|
||||
|
||||
<!-- 侧边图标 -->
|
||||
<div class="tl-rtc-file-side-tool">
|
||||
|
||||
<a href="https://im.iamtsm.cn/document" target="_blank">
|
||||
<svg class="icon" aria-hidden="true" style="width: 24px;height: 24px;margin-right: 10px;">
|
||||
<use xlink:href="#icon-rtc-file-shu"></use>
|
||||
</svg>
|
||||
</a>
|
||||
|
||||
<a :title="lang.notice" @click="clickNotice" v-show="switchData.openNotice">
|
||||
<i class="layui-icon layui-icon-notice">
|
||||
<span v-show="switchData.noticeMsgList && switchData.noticeMsgList.length > 0"
|
||||
class="layui-badge tl-rtc-file-msg-dot"
|
||||
style="right: 7px;top: 20px; width: 7px; height: 7px;"></span>
|
||||
style="right: 7px;top: 65px; width: 7px; height: 7px;"></span>
|
||||
</i>
|
||||
</a>
|
||||
|
||||
@@ -343,7 +349,9 @@
|
||||
<b v-show="owner" style="color: #7375e9;">【{{lang.owner}}】-</b>【{{lang.self}}】- {{nickName}}
|
||||
</b>
|
||||
<b class="tl-rtc-file-user-body-left-id">
|
||||
{{socketId}}
|
||||
{{socketId}} - {{lang.websocketState}} :
|
||||
<b v-if="socketHeartbeatFaild > 0" style="font-weight: bold;color: red;">{{lang.websocketDisConnected}}</b>
|
||||
<b v-else style="font-weight: bold;color: #69d31e;">{{lang.websocketConnected}}</b>
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
@@ -529,14 +537,14 @@
|
||||
style="top: -2px; margin-right: 5px; left: -5px; position: relative;">[{{lang.sending}}]</span>
|
||||
</a>
|
||||
|
||||
<a v-show="!file.done && file.progress === 0 && !isSending && !isSendAllWaiting"
|
||||
<a v-show="!file.done && file.progress === 0 && !isSending && !chooseFileRecoderAutoNext"
|
||||
:title="lang.send_alone" @click="sendFileToSingle(file)">
|
||||
<i class="layui-icon layui-icon-release"></i>
|
||||
<span
|
||||
style="top: -2px; margin-right: 5px; left: -5px; position: relative;">[{{lang.send}}]</span>
|
||||
</a>
|
||||
|
||||
<a v-show="!file.done && file.progress === 0 && (isSending || isSendAllWaiting)"
|
||||
<a v-show="!file.done && file.progress === 0 && (isSending || chooseFileRecoderAutoNext)"
|
||||
:title="lang.send_alone">
|
||||
<i class="layui-icon layui-icon-release layui-disabled"></i>
|
||||
<span class="layui-disabled"
|
||||
@@ -604,8 +612,8 @@
|
||||
<!-- 一键发送 -->
|
||||
<div>
|
||||
<div v-show="canSendFile">
|
||||
<button v-if="!isSending && !isSendAllWaiting" class="tl-rtc-file-send-file-list-btn"
|
||||
@click="initSendFile">
|
||||
<button v-if="!isSending && !chooseFileRecoderAutoNext" class="tl-rtc-file-send-file-list-btn"
|
||||
@click="sendFileToAll()">
|
||||
<i class="layui-icon layui-icon-release"></i>
|
||||
<div>{{lang.send_all}}</div>
|
||||
</button>
|
||||
@@ -867,7 +875,7 @@
|
||||
{{lang.file_from}}: {{file.nickName}} - {{file.id}} - {{file.progress}}% -
|
||||
{{file.cost}}s
|
||||
</b>
|
||||
<b class="tl-rtc-file-send-body-left-icon">
|
||||
<b class="tl-rtc-file-send-body-left-icon" v-if="file.done">
|
||||
<a :href="file.href" :download="file.name" :title="lang.download">
|
||||
<i class="layui-icon layui-icon-download-circle"></i>
|
||||
<span
|
||||
@@ -879,6 +887,14 @@
|
||||
style="top: -2px; margin-right: 5px; left: -5px; position: relative;">[{{lang.preview}}]</span>
|
||||
</a>
|
||||
</b>
|
||||
<b class="tl-rtc-file-send-body-left-icon layui-disabled" v-else>
|
||||
<a :title="lang.wait_for_file">
|
||||
<i
|
||||
class="layui-icon layui-disabled layui-icon-loading layui-anim layui-anim-rotate layui-anim-loop"></i>
|
||||
<span class="layui-disabled"
|
||||
style="top: -2px; margin-right: 5px; left: -5px; position: relative;">[{{lang.wait_for_file}}]</span>
|
||||
</a>
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tl-rtc-file-send-body-right">
|
||||
@@ -1029,7 +1045,7 @@
|
||||
<div style="text-align: center;font-weight: bold;">{{nickName}}</div>
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video v-show="isCameraEnabled" id="selfMediaShareVideo"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true" controls
|
||||
playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5"
|
||||
x5-video-player-fullscreen="true" x5-video-orientation="portraint"></video>
|
||||
<svg v-show="!isCameraEnabled" class="icon" aria-hidden="true" style="width: 100%;height: 100%;">
|
||||
@@ -1082,7 +1098,7 @@
|
||||
<div style="text-align: center;font-weight: bold;">{{nickName}}</div>
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video v-show="isCameraEnabled" id="selfMediaShareScreen"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true" controls
|
||||
playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5"
|
||||
x5-video-player-fullscreen="true" x5-video-orientation="portraint"></video>
|
||||
<svg v-show="!isCameraEnabled" class="icon" aria-hidden="true" style="width: 100%;height: 100%;">
|
||||
@@ -1124,7 +1140,7 @@
|
||||
<div v-show="isLiveShare && owner" style="text-align: center;font-weight: bold;">{{nickName}}</div>
|
||||
<div class="tl-rtc-file-mask-media-video" v-show="owner">
|
||||
<video v-show="isCameraEnabled" id="selfMediaShareLive"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true" controls
|
||||
playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5"
|
||||
x5-video-player-fullscreen="true" x5-video-orientation="portraint"></video>
|
||||
<svg v-show="!isCameraEnabled" class="icon" aria-hidden="true" style="width: 100%;height: 100%;">
|
||||
@@ -1190,7 +1206,7 @@
|
||||
<div style="text-align: center;font-weight: bold;">{{nickName}}</div>
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video v-show="false" id="selfMediaShareAudio"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true"
|
||||
preload="auto" autoplay="autoplay" x-webkit-airplay="true" controls
|
||||
playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5"
|
||||
x5-video-player-fullscreen="true" x5-video-orientation="portraint"></video>
|
||||
<svg v-show="isAudioEnabled" class="icon layui-anim layui-anim-scaleSpring layui-anim-loop" aria-hidden="true" style="width: 100%;height: 100%;animation-duration:.7s;max-width:50%;color:cadetblue;">
|
||||
|
@@ -57,9 +57,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
owner : false, //本人是否是房主
|
||||
isJoined: false, // 是否加入房间
|
||||
openRoomInput : false, //是否打开房间号输入框
|
||||
isSendFileToSingleSocket : false, //是否是单独发送文件给某个socket
|
||||
isMouseDrag : false, //是否正在拖拽鼠标
|
||||
isSendAllWaiting : false, //一键发送文件时,有1秒时间间隔,这个记录当前是否是一键发送文件等待中
|
||||
// isSendAllWaiting : false, //一键发送文件时,自动排队时,有1秒时间间隔,这个记录当前是否是一键发送文件等待中
|
||||
isShareJoin : false, //是否是分享加入房间
|
||||
isCameraEnabled : true, //音视频场景下自己的摄像头是否开启
|
||||
isAudioEnabled : true, //音视频场景下自己的麦克风是否开启
|
||||
@@ -105,12 +103,17 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
|
||||
chunkSize: 16 * 1024, // 一块16kb 最大应该可以设置到64kb
|
||||
currentSendAllSize: 0, // 统计发送文件总大小 (流量统计)
|
||||
fileInfoBufferHeaderLen : 256, //分片文件信息头大小 默认256byte
|
||||
uploadCodeFileProgress: 0, // 上传暂存文件的进度
|
||||
previewFileMaxSize : 1024 * 1024 * 5, // 5M以内允许预览
|
||||
uploadCodeFileMaxSize : 1024 * 1024 * 10, // 10M以内允许暂存
|
||||
socketHeartbeatFaild : 0, //socket心跳失败次数
|
||||
bigFileMaxSize: 20 * 1024 * 1024, //并发发送时大文件认定规则
|
||||
bigFileMaxCount: 1, //并发发送时大文件认定规则
|
||||
longFileQueueMaxSize: 10, //并发发送时,长列表认定规则
|
||||
|
||||
currentChooseFileRecoder : null, //当前进行发送的文件记录
|
||||
currentChooseFile: null, //当前发送中的文件
|
||||
chooseFileRecoderAutoNext : false, //是否是一键发送模式下的自动排队发送
|
||||
chooseFileRecoderList : [], //当前进行发送的文件记录列表 (多记录并行发送多文件)
|
||||
chooseFileList: [], //选择的文件列表
|
||||
sendFileRecoderList: [], //发送文件的列表
|
||||
sendFileRecoderHistoryList: [], //发送过文件的列表记录
|
||||
@@ -197,7 +200,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
},
|
||||
roomTypeName: function(){
|
||||
return window.tlrtcfile.getRoomTypeZh(this.roomType)
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
isAiAnswering: function (newV, oldV) {
|
||||
@@ -252,6 +255,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
formType: 0,
|
||||
title: that.lang.changeNickName,
|
||||
value: "",
|
||||
maxlength : 10,
|
||||
}, function (value, index, elem) {
|
||||
if(value.length > 10 || tlrtcfile.containSymbol(value)){
|
||||
layer.msg(that.lang.changeNickNameLimit)
|
||||
@@ -658,14 +662,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.addUserLogs(this.lang.collapse_temporary);
|
||||
}
|
||||
},
|
||||
// 单独发送文件给用户
|
||||
sendFileToSingle: function(recoder){
|
||||
layer.msg(`${this.lang.send_to_user_separately} ${recoder.id}`)
|
||||
|
||||
this.isSendFileToSingleSocket = true;
|
||||
|
||||
this.initSendFile(recoder);
|
||||
},
|
||||
// 私聊弹窗
|
||||
startChatRoomSingle: function(event, remote){
|
||||
event.stopPropagation();
|
||||
@@ -981,9 +977,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
file : file,
|
||||
max : this.previewFileMaxSize,
|
||||
callback : function(msg){
|
||||
if(window.layer){
|
||||
layer.msg(msg)
|
||||
}
|
||||
layer.msg(msg)
|
||||
that.addUserLogs(msg)
|
||||
}
|
||||
})
|
||||
@@ -992,9 +986,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
file : file,
|
||||
max : this.previewFileMaxSize,
|
||||
callback : function(msg){
|
||||
if(window.layer){
|
||||
layer.msg(msg)
|
||||
}
|
||||
layer.msg(msg)
|
||||
that.addUserLogs(msg)
|
||||
}
|
||||
})
|
||||
@@ -1003,9 +995,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
file : file,
|
||||
max : this.previewFileMaxSize,
|
||||
callback : function(msg){
|
||||
if(window.layer){
|
||||
layer.msg(msg)
|
||||
}
|
||||
layer.msg(msg)
|
||||
that.addUserLogs(msg)
|
||||
}
|
||||
})
|
||||
@@ -1014,9 +1004,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
file : file,
|
||||
max : this.previewFileMaxSize,
|
||||
callback : function(msg){
|
||||
if(window.layer){
|
||||
layer.msg(msg)
|
||||
}
|
||||
layer.msg(msg)
|
||||
that.addUserLogs(msg)
|
||||
}
|
||||
})
|
||||
@@ -1366,14 +1354,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
<cite>${this.lang.webrtc_check}</cite>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-col-xs4" >
|
||||
<a title="${this.lang.p2p_check}" onclick="p2pCheck()">
|
||||
<svg class="icon" aria-hidden="true" style="width:42px;height:50px;" id="p2pCheck">
|
||||
<use xlink:href="#icon-rtc-file-PP-"></use>
|
||||
</svg>
|
||||
<cite>${this.lang.p2p_check}</cite>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-col-xs4" style="${this.switchData.openTurnServer ? '' : 'display:none;'}">
|
||||
<a title="${this.lang.relay_setting}" onclick="relaySetting()">
|
||||
<svg class="icon" aria-hidden="true" style="width:42px;height:50px;">
|
||||
@@ -2286,7 +2266,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
};
|
||||
if(that.roomType === 'live'){
|
||||
shareArgs.lsm = that.liveShareMode;
|
||||
shareArgs.lsr = that.liveShareRole;
|
||||
shareArgs.lsr = 'viewer';
|
||||
}
|
||||
let content = window.tlrtcfile.addUrlHashParams(shareArgs);
|
||||
document.querySelector(".layui-layer-title").style.borderTopRightRadius = "8px";
|
||||
@@ -2750,6 +2730,12 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
|
||||
rtcConnect.oniceconnectionstatechange = (e) => {
|
||||
that.addSysLogs("iceConnectionState: " + rtcConnect.iceConnectionState);
|
||||
|
||||
//如果是断开连接,并且没有使用turn服务器,提示开启turn服务器
|
||||
if(rtcConnect.iceConnectionState === 'disconnected' && !this.useTurn){
|
||||
layer.msg(that.lang.please_use_turn_server);
|
||||
that.addSysLogs(that.lang.please_use_turn_server);
|
||||
}
|
||||
that.setRemoteInfo(id, {
|
||||
iceConnectionState : rtcConnect.iceConnectionState
|
||||
})
|
||||
@@ -2849,7 +2835,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
$(`#mediaAudioRoomList`).append(`
|
||||
<div style="text-align: center;font-weight: bold;">${remoteName}</div>
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="otherMediaAudioShare${id}" preload="auto" autoplay="autoplay" x-webkit-airplay="true" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<video id="otherMediaAudioShare${id}" controls preload="auto" autoplay="autoplay" x-webkit-airplay="true" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<svg id="otherMediaAudioShareAudioOpenAnimSvg${id}" class="icon layui-anim layui-anim-scaleSpring layui-anim-loop" aria-hidden="true" style="width: auto; height: auto; position: absolute;animation-duration:.7s;max-width:50%;color:cadetblue;">
|
||||
<use xlink:href="#icon-rtc-file-shengboyuyinxiaoxi"></use>
|
||||
</svg>
|
||||
@@ -2871,7 +2857,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
$(`#mediaVideoRoomList`).append(`
|
||||
<div style="text-align: center;font-weight: bold;">${remoteName}</div>
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="otherMediaVideoShare${id}" preload="auto" autoplay="autoplay" x-webkit-airplay="true" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<video id="otherMediaVideoShare${id}" controls preload="auto" autoplay="autoplay" x-webkit-airplay="true" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<svg id="otherMediaVideoShareVideoSvg${id}" class="icon" aria-hidden="true" style="width: 100%;height: 100%;display:none;">
|
||||
<use xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
</svg>
|
||||
@@ -2888,7 +2874,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
$(`#mediaScreenRoomList`).append(`
|
||||
<div style="text-align: center;font-weight: bold;">${remoteName}</div>
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="otherMediaScreenShare${id}" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<video id="otherMediaScreenShare${id}" controls playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<svg id="otherMediaScreenShareVideoSvg${id}" class="icon" aria-hidden="true" style="width: 100%;height: 100%;display:none;">
|
||||
<use xlink:href="#icon-rtc-file-guanbipingmu"></use>
|
||||
</svg>
|
||||
@@ -2901,7 +2887,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
$(`#mediaLiveRoomList`).append(`
|
||||
<div style="text-align: center;font-weight: bold;">${remoteName}</div>
|
||||
<div class="tl-rtc-file-mask-media-video">
|
||||
<video id="otherMediaLiveShare${id}" playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<video id="otherMediaLiveShare${id}" controls playsinline ="true" webkit-playsinline ="true" x5-video-player-type="h5" x5-video-player-fullscreen="true" x5-video-orientation="portraint" ></video>
|
||||
<svg id="otherMediaLiveShareVideoSvg${id}" class="icon" aria-hidden="true" style="width: 100%;height: 100%;display:none;">
|
||||
<use xlink:href="#icon-rtc-file-shexiangtou_guanbi"></use>
|
||||
</svg>
|
||||
@@ -2936,142 +2922,213 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
video.play();
|
||||
}
|
||||
},
|
||||
// 初始发送
|
||||
// pickRecoder : 指定发送记录进行发送
|
||||
initSendFile: function (pickRecoder) {
|
||||
//编码组装发送文件数据,设置好每次分片的文件头信息
|
||||
encodeSendFileBuffer: function ({recoder, buffer, fragment, event}) {
|
||||
let fileInfoString = JSON.stringify({
|
||||
i: recoder.index, //当前文件块所属的索引
|
||||
f: fragment, //当前buffer所处的分片块
|
||||
});
|
||||
//填充
|
||||
const paddedFileInfoString = fileInfoString.padEnd(this.fileInfoBufferHeaderLen, '\0');
|
||||
const combindBuffer = new ArrayBuffer(this.fileInfoBufferHeaderLen + buffer.byteLength);
|
||||
|
||||
const combinedUint8Array = new Uint8Array(combindBuffer);
|
||||
for (let i = 0; i < paddedFileInfoString.length; i++) {
|
||||
combinedUint8Array[i] = paddedFileInfoString.charCodeAt(i);
|
||||
}
|
||||
combinedUint8Array.set(new Uint8Array(buffer), this.fileInfoBufferHeaderLen);
|
||||
|
||||
return combindBuffer;
|
||||
},
|
||||
//解码组装接收文件数据
|
||||
decodeReceiveFileBuffer: function (buffer) {
|
||||
const receivedUint8Array = new Uint8Array(buffer);
|
||||
const fileInfoString = String.fromCharCode(...receivedUint8Array.slice(0, this.fileInfoBufferHeaderLen));
|
||||
const trimmedFileInfoString = fileInfoString.replace(/\0/g, '');
|
||||
const fileInfo = JSON.parse(trimmedFileInfoString);
|
||||
return {
|
||||
buffer: receivedUint8Array.slice(this.fileInfoBufferHeaderLen),
|
||||
index: fileInfo.i,
|
||||
fragment: fileInfo.f
|
||||
}
|
||||
},
|
||||
//每个记录发送完毕后都检查下是否全部发送完
|
||||
allFileSendedCheckHandler : function(){
|
||||
let allDone = this.sendFileRecoderList.filter(item => {
|
||||
return item.done;
|
||||
}).length === this.sendFileRecoderList.length;
|
||||
|
||||
// 全部发完
|
||||
if(allDone){
|
||||
this.chooseFileList = []
|
||||
this.sendFileRecoderList = []
|
||||
this.chooseFileRecoderList = []
|
||||
this.chooseFileRecoderAutoNext = false;
|
||||
this.addPopup({
|
||||
title : this.lang.send_file,
|
||||
msg : this.lang.file_send_done
|
||||
});
|
||||
this.addSysLogs(this.lang.file_send_done)
|
||||
this.allSended = true;
|
||||
this.isSending = false;
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//在每次发送完后的检查时, 过滤掉已经发送完毕的记录
|
||||
this.chooseFileRecoderList = this.chooseFileRecoderList.filter(item=>{
|
||||
return !item.done;
|
||||
});
|
||||
|
||||
// 在每次发送完后的检查时,如果是开启了自动排队发送,调用自动发送
|
||||
if(this.chooseFileRecoderAutoNext){
|
||||
this.sendFileToSingleAuto();
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
// 指定单独发送文件给用户
|
||||
sendFileToSingle: function(recoder){
|
||||
layer.msg(`${this.lang.send_to_user_separately} ${recoder.id}`)
|
||||
|
||||
if(!this.hasManInRoom){
|
||||
layer.msg(this.lang.room_least_two_can_send_content)
|
||||
return
|
||||
}
|
||||
//选中一个记录进行发送
|
||||
this.chooseSendFileRecoder(pickRecoder);
|
||||
|
||||
this.chooseFileRecoderList = [recoder];
|
||||
this.sendFileRecoderInfo();
|
||||
},
|
||||
// 选一个未发送的文件进行发送,如有下一个,切换下一个文件
|
||||
// pickRecoder : 指定发送记录进行发送
|
||||
chooseSendFileRecoder: async function (pickRecoder) {
|
||||
let chooseFile = null;
|
||||
let chooseFileRecoder = null;
|
||||
// 自动单独发送文件给用户
|
||||
sendFileToSingleAuto: function(){
|
||||
if(!this.hasManInRoom){
|
||||
layer.msg(this.lang.room_least_two_can_send_content)
|
||||
return
|
||||
}
|
||||
|
||||
if(this.isSendFileToSingleSocket){
|
||||
|
||||
chooseFileRecoder = pickRecoder;
|
||||
|
||||
}else{
|
||||
this.addSysLogs(this.lang.select_wait_send_record)
|
||||
for (let i = 0; i < this.sendFileRecoderList.length; i++) {
|
||||
let recoder = this.sendFileRecoderList[i]
|
||||
if (!recoder.done) {
|
||||
chooseFileRecoder = recoder;
|
||||
break;
|
||||
}
|
||||
//当前自动切换文件是开启的
|
||||
if(this.chooseFileRecoderAutoNext){
|
||||
let chooseRecoder = this.sendFileRecoderList.filter(item=>{
|
||||
return !item.done;
|
||||
}).shift();
|
||||
|
||||
if(chooseRecoder){
|
||||
setTimeout(() => {
|
||||
this.chooseFileRecoderList = [chooseRecoder];
|
||||
this.sendFileRecoderInfo();
|
||||
}, 1000);
|
||||
}
|
||||
// 如果没有,说明全部发完
|
||||
if(!chooseFileRecoder){
|
||||
this.chooseFileList = []
|
||||
this.sendFileRecoderList = []
|
||||
this.addPopup({
|
||||
title : this.lang.send_file,
|
||||
msg : this.lang.file_send_done
|
||||
});
|
||||
this.addSysLogs(this.lang.file_send_done)
|
||||
this.isSending = false;
|
||||
this.allSended = true;
|
||||
}
|
||||
},
|
||||
// 一键发送 , 根据设置的规则自动选择发送模式,支持自动排队发送,并发发送
|
||||
sendFileToAll: function(){
|
||||
layer.msg(`${this.lang.send_to_all_user}`)
|
||||
|
||||
if(!this.hasManInRoom){
|
||||
layer.msg(this.lang.room_least_two_can_send_content)
|
||||
return
|
||||
}
|
||||
|
||||
let hasMoreBigFile = this.sendFileRecoderList.filter(item=>{
|
||||
return item.size > this.bigFileMaxSize
|
||||
}).length > this.bigFileMaxCount;
|
||||
|
||||
let hasLongFileQueue = this.sendFileRecoderList.filter(item=>{
|
||||
return !item.done;
|
||||
}).length > this.longFileQueueMaxSize;
|
||||
|
||||
//超过规则范围,自动排队发送
|
||||
if(hasMoreBigFile || hasLongFileQueue){
|
||||
this.chooseFileRecoderAutoNext = true;
|
||||
this.sendFileToSingleAuto();
|
||||
return
|
||||
}
|
||||
|
||||
//如果不是单独发送某个记录,就需要处理全部记录
|
||||
this.chooseFileRecoderList = this.sendFileRecoderList.filter(item=>{
|
||||
return !item.done;
|
||||
})
|
||||
this.sendFileRecoderInfo();
|
||||
},
|
||||
// 多记录并发发送文件基本信息
|
||||
sendFileRecoderInfo : function(){
|
||||
// 提前发送文件基础信息
|
||||
this.chooseFileRecoderList.forEach(chooseRecoder => {
|
||||
this.socket.emit('message', {
|
||||
emitType: "sendFileInfo",
|
||||
index: chooseRecoder.index,
|
||||
name: chooseRecoder.name,
|
||||
type: chooseRecoder.type,
|
||||
size: chooseRecoder.size,
|
||||
room: this.roomId,
|
||||
from: this.socketId,
|
||||
nickName : this.nickName,
|
||||
to: chooseRecoder.id,
|
||||
recoderId: this.recoderId
|
||||
});
|
||||
})
|
||||
},
|
||||
// 多记录并行发送文件数据
|
||||
sendFileRecoderData: function () {
|
||||
let that = this;
|
||||
|
||||
this.chooseFileRecoderList.forEach(chooseRecoder => {
|
||||
let filterFile = that.chooseFileList.filter(item => {
|
||||
return item.index === chooseRecoder.index;
|
||||
});
|
||||
if(filterFile.length === 0){
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 还有没有发送的记录,根据file index 找到文件
|
||||
let filterFile = this.chooseFileList.filter(item=>{
|
||||
return item.index === chooseFileRecoder.index;
|
||||
});
|
||||
let chooseFile = filterFile[0];
|
||||
let fileReader = chooseRecoder.reader;
|
||||
|
||||
if(filterFile.length === 0){
|
||||
this.addUserLogs(this.lang.failed_find_file);
|
||||
layer.msg(this.lang.failed_find_file);
|
||||
return
|
||||
}
|
||||
chooseFile = filterFile[0];
|
||||
fileReader.addEventListener('loadend', (event) => {
|
||||
that.sendFileToRemoteByRecoder(event.target.result, chooseRecoder, chooseFile);
|
||||
});
|
||||
|
||||
fileReader.addEventListener('error', error => {
|
||||
that.addSysLogs(that.lang.read_file_error + " : " + error);
|
||||
});
|
||||
|
||||
fileReader.addEventListener('abort', event => {
|
||||
that.addSysLogs(that.lang.read_file_interrupt + " : " + event);
|
||||
});
|
||||
|
||||
if(chooseFile == null){
|
||||
this.addUserLogs(this.lang.failed_find_file);
|
||||
layer.msg(this.lang.failed_find_file);
|
||||
return
|
||||
}
|
||||
|
||||
this.currentChooseFile = chooseFile;
|
||||
this.currentChooseFileRecoder = chooseFileRecoder;
|
||||
|
||||
this.socket.emit('message', {
|
||||
emitType: "sendFileInfo",
|
||||
index: this.currentChooseFile.index,
|
||||
name: this.currentChooseFile.name,
|
||||
type: this.currentChooseFile.type,
|
||||
size: this.currentChooseFile.size,
|
||||
room: this.roomId,
|
||||
from: this.socketId,
|
||||
nickName : this.nickName,
|
||||
to: this.currentChooseFileRecoder.id,
|
||||
recoderId: this.recoderId
|
||||
});
|
||||
|
||||
this.isSending = true;
|
||||
|
||||
let remote = this.remoteMap[this.currentChooseFileRecoder.id]
|
||||
let fileReader = remote[this.currentChooseFile.index + "reader"];
|
||||
|
||||
fileReader.addEventListener('loadend', this.sendFileToRemoteByLoop);
|
||||
|
||||
fileReader.addEventListener('error', error => {
|
||||
that.addSysLogs(this.lang.read_file_error + " : " + error);
|
||||
});
|
||||
|
||||
fileReader.addEventListener('abort', event => {
|
||||
that.addSysLogs(this.lang.read_file_interrupt + " : " + event);
|
||||
});
|
||||
|
||||
this.readSlice(0);
|
||||
that.readAsArrayBufferByOffset(0, chooseFile, chooseRecoder)
|
||||
})
|
||||
},
|
||||
//一次发送一个文件给一个用户
|
||||
sendFileToRemoteByLoop: function (event) {
|
||||
// 一次发送一个文件给一个用户
|
||||
sendFileToRemoteByRecoder: function (buffer, chooseRecoder, chooseFile) {
|
||||
let that = this;
|
||||
|
||||
if (!this.currentChooseFileRecoder) {
|
||||
if (!chooseRecoder) {
|
||||
return
|
||||
}
|
||||
|
||||
let remote = this.remoteMap[this.currentChooseFileRecoder.id];
|
||||
let fileOffset = remote[this.currentChooseFile.index + "offset"]
|
||||
let remote = this.getRemoteInfo(chooseRecoder.id);
|
||||
let fileOffset = remote[chooseRecoder.index + "offset"]
|
||||
let sendFileDataChannel = remote.sendFileDataChannel;
|
||||
if (!sendFileDataChannel || sendFileDataChannel.readyState !== 'open') {
|
||||
this.addSysLogs(this.lang.file_send_channel_not_establish)
|
||||
return;
|
||||
}
|
||||
|
||||
let sendFileInfoAck = remote[this.currentChooseFile.index + "ack"]
|
||||
|
||||
// 还不能进行发送,等一下
|
||||
if (!sendFileInfoAck) {
|
||||
this.addSysLogs(this.lang.wait_ack)
|
||||
setTimeout(() => {
|
||||
that.sendFileToRemoteByLoop(event)
|
||||
}, 500);
|
||||
return
|
||||
}
|
||||
|
||||
this.setRemoteInfo(this.currentChooseFileRecoder.id, {
|
||||
[this.currentChooseFile.index + "status"]: 'sending'
|
||||
this.setRemoteInfo(chooseRecoder.id, {
|
||||
[chooseRecoder.index + "status"]: 'sending'
|
||||
})
|
||||
|
||||
this.isSending = true;
|
||||
|
||||
// 开始发送通知
|
||||
if (fileOffset === 0) {
|
||||
this.addPopup({
|
||||
title : this.lang.send_file,
|
||||
msg : this.lang.sending_to + this.currentChooseFileRecoder.id.substr(0, 4) + ",0%。"
|
||||
msg : this.lang.sending_to + chooseRecoder.id.substr(0, 4) + ",0%。"
|
||||
});
|
||||
this.addSysLogs(this.lang.sending_to + this.currentChooseFileRecoder.id.substr(0, 4) + ",0%。")
|
||||
this.updateSendFileRecoderProgress(this.currentChooseFileRecoder.id, {
|
||||
this.addSysLogs(this.lang.sending_to + chooseRecoder.id.substr(0, 4) + ",0%。")
|
||||
this.updateSendFileRecoderProgress(chooseRecoder.id, {
|
||||
start: Date.now()
|
||||
})
|
||||
}, chooseRecoder)
|
||||
}
|
||||
|
||||
//缓冲区暂定 256kb
|
||||
@@ -3089,101 +3146,68 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
sendFileDataChannel.bufferedAmount
|
||||
)
|
||||
sendFileDataChannel.onbufferedamountlow = null;
|
||||
that.sendFileToRemoteByLoop(event);
|
||||
that.sendFileToRemoteByRecoder(buffer, chooseRecoder, chooseFile);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送数据
|
||||
sendFileDataChannel.send(event.target.result);
|
||||
fileOffset += event.target.result.byteLength;
|
||||
remote[this.currentChooseFile.index + "offset"] = fileOffset
|
||||
this.currentSendAllSize += event.target.result.byteLength;
|
||||
sendFileDataChannel.send(this.encodeSendFileBuffer({
|
||||
recoder : chooseRecoder,
|
||||
fragment : parseInt(fileOffset / this.chunkSize),
|
||||
buffer : buffer,
|
||||
}));
|
||||
|
||||
fileOffset += buffer.byteLength;
|
||||
remote[chooseRecoder.index + "offset"] = fileOffset
|
||||
this.currentSendAllSize += buffer.byteLength;
|
||||
|
||||
//更新发送进度
|
||||
this.updateSendFileRecoderProgress(this.currentChooseFileRecoder.id, {
|
||||
progress: ((fileOffset / this.currentChooseFile.size) * 100).toFixed(3) || 0
|
||||
})
|
||||
this.updateSendFileRecoderProgress(chooseRecoder.id, {
|
||||
progress: ((fileOffset / chooseRecoder.size) * 100).toFixed(3) || 0
|
||||
}, chooseRecoder)
|
||||
|
||||
//发送完一份重置相关数据
|
||||
if (fileOffset === this.currentChooseFile.size) {
|
||||
if (fileOffset === chooseRecoder.size) {
|
||||
this.addPopup({
|
||||
title : this.lang.send_file,
|
||||
msg : this.lang.sending_to + this.currentChooseFileRecoder.id.substr(0, 4) + ",100%。"
|
||||
msg : this.lang.sending_to + chooseRecoder.id.substr(0, 4) + ",100%。"
|
||||
});
|
||||
this.addSysLogs(this.lang.sending_to + this.currentChooseFileRecoder.id.substr(0, 4) + ",100%。")
|
||||
this.addSysLogs(this.lang.sending_to + chooseRecoder.id.substr(0, 4) + ",100%。")
|
||||
this.socket.emit('message', {
|
||||
emitType: "sendDone",
|
||||
room: this.roomId,
|
||||
from: this.socketId,
|
||||
size: this.currentChooseFile.size,
|
||||
name: this.currentChooseFile.name,
|
||||
type: this.currentChooseFile.type,
|
||||
to: this.currentChooseFileRecoder.id
|
||||
size: chooseRecoder.size,
|
||||
name: chooseRecoder.name,
|
||||
type: chooseRecoder.type,
|
||||
to: chooseRecoder.id
|
||||
});
|
||||
//更新发送进度
|
||||
this.updateSendFileRecoderProgress(this.currentChooseFileRecoder.id, {
|
||||
this.updateSendFileRecoderProgress(chooseRecoder.id, {
|
||||
progress: 100,
|
||||
done: true
|
||||
})
|
||||
}, chooseRecoder)
|
||||
|
||||
this.setRemoteInfo(this.currentChooseFileRecoder.id, {
|
||||
[this.currentChooseFile.index + "status"]: 'done'
|
||||
this.setRemoteInfo(chooseRecoder.id, {
|
||||
[chooseRecoder.index + "status"]: 'done'
|
||||
})
|
||||
|
||||
this.isSending = false;
|
||||
|
||||
//发完一条记录,继续下一条
|
||||
this.currentChooseFile = null;
|
||||
this.currentChooseFileRecoder = null;
|
||||
|
||||
//如果是单独发送给某个用户,发完直接退出发送逻辑
|
||||
if(this.isSendFileToSingleSocket){
|
||||
this.isSendFileToSingleSocket = false;
|
||||
|
||||
setTimeout(() => {
|
||||
let allDone = this.sendFileRecoderList.filter(item => {
|
||||
return item.done;
|
||||
}).length === this.sendFileRecoderList.length;
|
||||
|
||||
// 全部发完
|
||||
if(allDone){
|
||||
this.chooseFileList = []
|
||||
this.sendFileRecoderList = []
|
||||
this.addPopup({
|
||||
title : this.lang.send_file,
|
||||
msg : this.lang.file_send_done
|
||||
});
|
||||
this.addSysLogs(this.lang.file_send_done)
|
||||
this.allSended = true;
|
||||
return
|
||||
}
|
||||
}, 1000);
|
||||
}else{
|
||||
this.isSendAllWaiting = true;
|
||||
setTimeout(() => {
|
||||
//如果不是点击单独发送,继续下一个记录, 缓冲一秒钟
|
||||
this.initSendFile()
|
||||
this.isSendAllWaiting = false;
|
||||
}, 1000);
|
||||
}
|
||||
return
|
||||
//检查全部发送完毕
|
||||
this.allFileSendedCheckHandler()
|
||||
}
|
||||
|
||||
// 继续下一个分片
|
||||
if (fileOffset < this.currentChooseFile.size) {
|
||||
this.readSlice(fileOffset + this.chunkSize)
|
||||
if(fileOffset < chooseRecoder.size){
|
||||
this.readAsArrayBufferByOffset(fileOffset, chooseFile, chooseRecoder);
|
||||
}
|
||||
},
|
||||
//文件分片 -- 发送
|
||||
readSlice: function (offset) {
|
||||
if (this.currentChooseFileRecoder) {
|
||||
let remote = this.remoteMap[this.currentChooseFileRecoder.id]
|
||||
let fileOffset = remote[this.currentChooseFile.index + "offset"]
|
||||
let fileReader = remote[this.currentChooseFile.index + "reader"]
|
||||
let slice = this.currentChooseFile.slice(fileOffset, offset + this.chunkSize);
|
||||
fileReader.readAsArrayBuffer(slice);
|
||||
}
|
||||
// 分片读取文件
|
||||
readAsArrayBufferByOffset: function ( offset, chooseFile, chooseRecoder) {
|
||||
let slice = chooseFile.slice(offset, offset + this.chunkSize);
|
||||
let fileReader = chooseRecoder.reader;
|
||||
fileReader.readAsArrayBuffer(slice);
|
||||
},
|
||||
//初始化接收数据事件
|
||||
initReceiveDataChannel: function (event, id) {
|
||||
@@ -3242,25 +3266,36 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
return;
|
||||
}
|
||||
let currentRtc = this.getRemoteInfo(id);
|
||||
let receiveFiles = currentRtc.receiveFiles;
|
||||
if(!currentRtc){
|
||||
return;
|
||||
}
|
||||
|
||||
let name = receiveFiles.name;
|
||||
let size = receiveFiles.size;
|
||||
let type = receiveFiles.type;
|
||||
//解析数据
|
||||
let { index, fragment, buffer } = this.decodeReceiveFileBuffer(event.data);
|
||||
let receiveFileMap = currentRtc.receiveFileMap;
|
||||
|
||||
//获取数据存下本地
|
||||
let receiveBuffer = currentRtc.receiveBuffer || new Array();
|
||||
let receivedSize = currentRtc.receivedSize || 0;
|
||||
//当前接收的文件基本信息
|
||||
let receiveRecoder = receiveFileMap[index];
|
||||
let name = receiveRecoder.name;
|
||||
let size = receiveRecoder.size;
|
||||
let type = receiveRecoder.type;
|
||||
|
||||
receiveBuffer.push(event.data);
|
||||
receivedSize += event.data.byteLength;
|
||||
//当前接收的文件相关数据/大小
|
||||
let receivedBuffer = receiveRecoder.receivedBuffer;
|
||||
let receivedSize = receiveRecoder.receivedSize;
|
||||
|
||||
this.setRemoteInfo(id, { receiveBuffer: receiveBuffer, receivedSize: receivedSize })
|
||||
//接收数据
|
||||
receivedBuffer.push(buffer);
|
||||
receivedSize += buffer.byteLength;
|
||||
|
||||
receiveFileMap[index].receivedBuffer = receivedBuffer;
|
||||
receiveFileMap[index].receivedSize = receivedSize;
|
||||
this.setRemoteInfo(id, { receiveFileMap : receiveFileMap})
|
||||
|
||||
//更新接收进度
|
||||
this.updateReceiveProgress(id, {
|
||||
progress: ((receivedSize / size) * 100).toFixed(3) || 0
|
||||
});
|
||||
}, receiveRecoder);
|
||||
|
||||
if (receivedSize === size) {
|
||||
this.addSysLogs(name + this.lang.receive_done);
|
||||
@@ -3273,12 +3308,14 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.updateReceiveProgress(id, {
|
||||
style: 'color: #ff5722;text-decoration: underline;',
|
||||
progress: 100,
|
||||
href: URL.createObjectURL(new Blob(receiveBuffer), { type: type }),
|
||||
href: URL.createObjectURL(new Blob(receivedBuffer), { type: type }),
|
||||
done: true
|
||||
});
|
||||
}, receiveRecoder);
|
||||
|
||||
//清除接收的数据缓存
|
||||
this.setRemoteInfo(id, { receiveBuffer: new Array(), receivedSize: 0 })
|
||||
receiveFileMap[index].receivedBuffer = new Array();
|
||||
receiveFileMap[index].receivedSize = 0;
|
||||
this.setRemoteInfo(id, { receiveFileMap })
|
||||
}
|
||||
},
|
||||
//关闭连接
|
||||
@@ -3317,10 +3354,10 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
}
|
||||
},
|
||||
//更新接收进度
|
||||
updateReceiveProgress: function (id, data) {
|
||||
updateReceiveProgress: function (id, data, recoder) {
|
||||
for (let i = 0; i < this.receiveFileRecoderList.length; i++) {
|
||||
let item = this.receiveFileRecoderList[i];
|
||||
if (item.id === id && !item.done) {
|
||||
if (item.id === id && item.index === recoder.index && !item.done) {
|
||||
if (item.start === 0) {
|
||||
item.start = Date.now();
|
||||
}
|
||||
@@ -3331,10 +3368,10 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.$forceUpdate();
|
||||
},
|
||||
//更新文件发送进度
|
||||
updateSendFileRecoderProgress: function (id, data) {
|
||||
updateSendFileRecoderProgress: function (id, data, recoder) {
|
||||
for (let i = 0; i < this.sendFileRecoderList.length; i++) {
|
||||
let item = this.sendFileRecoderList[i];
|
||||
if (item.id === id && item.index === this.currentChooseFile.index && !item.done) {
|
||||
if (item.id === id && item.index === recoder.index && !item.done) {
|
||||
data.cost = ((Date.now() - item.start) / 1000).toFixed(3);
|
||||
Object.assign(this.sendFileRecoderList[i], data);
|
||||
|
||||
@@ -3365,7 +3402,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
//移除rtc连接
|
||||
removeStream: function (rtcConnect, id, event) {
|
||||
this.getOrCreateRtcConnect(id).close;
|
||||
const remoteInfo = this.remoteMap[id] || {};
|
||||
const remoteInfo = this.getRemoteInfo(id) || {};
|
||||
const removeIsOwner = remoteInfo.owner;
|
||||
|
||||
delete this.rtcConns[id];
|
||||
@@ -3453,18 +3490,26 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
|
||||
this.socket.on("heartbeat", data => {
|
||||
if(data.status === 'ok'){
|
||||
that.addSysLogs("心跳检查正常 : " + data.status);
|
||||
that.addSysLogs(that.lang.websocketHeartBeatCheckOk + ": " + data.status);
|
||||
//心跳检测失败次数大于0,说明之前是失败的,现在恢复了,刷新页面
|
||||
if(that.socketHeartbeatFaild > 0){
|
||||
window.location.reload();
|
||||
}
|
||||
that.socketHeartbeatFaild = 0;
|
||||
}else{
|
||||
that.addSysLogs("socket心跳检查失败," + JSON.stringify(data));
|
||||
that.socketHeartbeatFaild += 1;
|
||||
that.addSysLogs(that.lang.websocketHeartBeatCheckFail + ": " + JSON.stringify(data));
|
||||
}
|
||||
})
|
||||
|
||||
this.socket.on('connect_error', error => {
|
||||
console.error('connect_error', error);
|
||||
if(error){
|
||||
layer.msg("socket服务连接失败,请检查socket服务是否正常启动 " + error.message);
|
||||
that.addSysLogs("socket服务连接失败,请检查socket服务是否正常启动" + error.message);
|
||||
layer.msg(that.lang.socketConnectFail + error.message);
|
||||
that.addSysLogs(that.lang.socketConnectFail + error.message);
|
||||
}
|
||||
that.socketHeartbeatFaild += 1;
|
||||
that.addSysLogs(that.lang.websocketHeartBeatCheckFail + ": " + that.socketHeartbeatFaild);
|
||||
});
|
||||
|
||||
// created作用是让自己去和其他人建立rtc连接
|
||||
@@ -3656,7 +3701,14 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
//选中文件时发送给接收方
|
||||
this.socket.on('sendFileInfo', function (data) {
|
||||
let fromId = data.from;
|
||||
that.setRemoteInfo(fromId, { receiveFiles: data });
|
||||
let { receiveFileMap = {} } = that.getRemoteInfo(fromId);
|
||||
receiveFileMap[data.index] = Object.assign({
|
||||
receivedBuffer : new Array(),
|
||||
receivedSize : 0
|
||||
},data);
|
||||
|
||||
that.setRemoteInfo(fromId, { receiveFileMap });
|
||||
|
||||
that.addPopup({
|
||||
title : that.lang.send_file,
|
||||
msg : data.from + that.lang.selected_file + "[ " + data.name + " ], "+that.lang.will_send
|
||||
@@ -3682,7 +3734,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
emitType : "sendFileInfoAck",
|
||||
room: that.roomId,
|
||||
from: that.socketId, // from代表自己发出去的回执
|
||||
to: fromId // 谁发过来的sendFileInfo事件就回执给谁
|
||||
to: fromId, // 谁发过来的sendFileInfo事件就回执给谁
|
||||
index: data.index, //具体的recoder记录文件的索引
|
||||
})
|
||||
});
|
||||
|
||||
@@ -3692,11 +3745,23 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.socket.on('sendFileInfoAck', function (data) {
|
||||
let to = data.to;
|
||||
let fromId = data.from;
|
||||
let index = data.index;
|
||||
if (to === that.socketId) { // 是自己发出去的文件ack回执
|
||||
that.addSysLogs(that.lang.receive_ack + fromId)
|
||||
that.setRemoteInfo(fromId, {
|
||||
[that.currentChooseFile.index + "ack"]: true
|
||||
})
|
||||
that.setRemoteInfo(fromId, { [index + "ack"]: true })
|
||||
|
||||
//确保所有人都收到基础文件信息,否则,轮训等待
|
||||
for(let i = 0; i < that.chooseFileRecoderList.length; i++){
|
||||
let chooseRecoder = that.chooseFileRecoderList[i];
|
||||
let remote = that.getRemoteInfo(chooseRecoder.id);
|
||||
let ack = remote[chooseRecoder.index + "ack"]
|
||||
if (!ack) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
//所有人都收到了基础文件信息,开始发送文件
|
||||
that.sendFileRecoderData()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4081,29 +4146,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
}, 50)
|
||||
}
|
||||
},
|
||||
// 打开p2p检测面板
|
||||
p2pCheck: function () {
|
||||
let that = this;
|
||||
$("#p2pCheck").removeClass("layui-anim-rotate")
|
||||
setTimeout(() => {
|
||||
$("#p2pCheck").addClass("layui-anim-rotate")
|
||||
let msg = "<p style='font-size:16px;margin-bottom:10px'>"+that.lang.p2p_check_principle+": </p> "
|
||||
msg += "<p style='margin-bottom:5px'> "+that.lang.p2p_check_principle_detail+" <b> "+that.lang.p2p_check_principle_detail_2+" </b></p>"
|
||||
msg += "<p style='font-size:16px;margin-bottom:10px;margin-top: 10px;'>"+that.lang.p2p_check_principle_detail_3+": </p> "
|
||||
msg += "<p style='margin-bottom:5px'> "+that.lang.p2p_check_principle_detail_4+"<b> "+that.lang.p2p_check_principle_detail_5+" </b>"+that.lang.p2p_check_principle_detail_6+"</p>"
|
||||
msg += "<p style='margin-bottom:5px'> "+that.lang.p2p_check_principle_detail_7+" </p>"
|
||||
msg += "<p style='margin-bottom:5px'> "+that.lang.p2p_check_principle_detail_8+" chrome://flags/ , "+that.lang.p2p_check_principle_detail_9+" </p>"
|
||||
msg += "<p style='margin-bottom:5px'> "+that.lang.p2p_check_principle_detail_10+" </p>"
|
||||
layer.confirm(msg, (index) => {
|
||||
layer.closeAll(() => {
|
||||
that.clickLogs()
|
||||
})
|
||||
}, (index) => {
|
||||
layer.close(index)
|
||||
})
|
||||
that.addUserLogs(`${that.lang.your_ip_list} : ${JSON.stringify(this.ips)}`)
|
||||
}, 50)
|
||||
},
|
||||
// 自动监听窗口变化,更新css
|
||||
reCaculateWindowSize: function () {
|
||||
this.clientWidth = document.body.clientWidth;
|
||||
@@ -4262,9 +4304,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
window.Bus.$on("webrtcCheck", (res) => {
|
||||
this.webrtcCheck()
|
||||
})
|
||||
window.Bus.$on("p2pCheck", (res) => {
|
||||
this.p2pCheck()
|
||||
})
|
||||
window.Bus.$on("sendBugs", (res) => {
|
||||
this.sendBugs()
|
||||
})
|
||||
@@ -4275,9 +4314,79 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.addSysLogs(res)
|
||||
})
|
||||
},
|
||||
// 选择文件后的处理函数
|
||||
chooseFileCallback: function(obj){
|
||||
this.allSended = false;
|
||||
//清空上次选择的文件和记录
|
||||
this.chooseFileList = [];
|
||||
this.sendFileRecoderList = [];
|
||||
|
||||
//这是改动layui源码补充的方法 : 清空文件列表
|
||||
obj.clearAllFile();
|
||||
|
||||
//重新生成文件记录
|
||||
let files = obj.pushFile();
|
||||
for(let index in files){
|
||||
let file = files[index];
|
||||
|
||||
//是否存在选择的文件
|
||||
let hasChooseFile = this.chooseFileList.filter((item) => {
|
||||
return item.name === file.name && item.size === file.size &&
|
||||
item.fileLastModified === file.lastModified && item.type === file.type;
|
||||
}).length > 0;
|
||||
|
||||
//如果文件已经存在,就不再添加了
|
||||
if(hasChooseFile){
|
||||
this.addUserLogs(`${this.lang.selected_file_exist} : ${file.name}, ${this.lang.size} : ${this.getFileSizeStr(file.size)}, ${this.lang.type} : ${file.type}`);
|
||||
continue
|
||||
}
|
||||
|
||||
this.chooseFileList.push(
|
||||
Object.assign(file, {
|
||||
fileLastModified : file.lastModified,
|
||||
index : index,
|
||||
})
|
||||
)
|
||||
|
||||
//根据房间内的用户,生成文件发送记录
|
||||
for (let remoteId in this.remoteMap) {
|
||||
let hasFileRecoder = this.sendFileRecoderList.filter(recoder => {
|
||||
return file.name === recoder.name && file.size === recoder.size &&
|
||||
file.type === recoder.type && recoder.id === remoteId;
|
||||
}).length > 0;
|
||||
|
||||
//如果已经存在记录了,就不再添加了
|
||||
if (hasFileRecoder) {
|
||||
this.addUserLogs(`${this.lang.send_file_record_exist} : ${file.name} : ${this.remoteMap[remoteId].nickName}`);
|
||||
continue
|
||||
}
|
||||
|
||||
this.setRemoteInfo(remoteId, {
|
||||
[index + "offset"]: 0,
|
||||
[index + "status"]: 'wait'
|
||||
})
|
||||
|
||||
this.sendFileRecoderList.unshift({
|
||||
index: index,
|
||||
id: remoteId,
|
||||
nickName : this.remoteMap[remoteId].nickName,
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
progress: 0,
|
||||
done: false,
|
||||
start: 0,
|
||||
cost: 0,
|
||||
upload : 'wait',
|
||||
reader : new FileReader(),
|
||||
});
|
||||
|
||||
this.addUserLogs(`${this.lang.generate_send_file_record} : ${file.name}, ${this.lang.size} : ${this.getFileSizeStr(file.size)}, ${this.lang.type} : ${file.type}, : ${this.remoteMap[remoteId].nickName}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 初始化选择文件面板
|
||||
renderChooseFileComp: function () {
|
||||
let that = this;
|
||||
if (window.upload) {
|
||||
upload.render({
|
||||
elem: '#chooseFileList',
|
||||
@@ -4285,78 +4394,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
auto: false,
|
||||
drag: true,
|
||||
multiple: true,
|
||||
choose: async function (obj) {
|
||||
that.allSended = false;
|
||||
//清空上次选择的文件和记录
|
||||
that.chooseFileList = [];
|
||||
that.sendFileRecoderList = [];
|
||||
|
||||
//这是改动layui源码补充的方法 : 清空文件列表
|
||||
obj.clearAllFile();
|
||||
|
||||
//重新生成文件记录
|
||||
let files = obj.pushFile();
|
||||
for(let index in files){
|
||||
let file = files[index];
|
||||
|
||||
//是否存在选择的文件
|
||||
let hasChooseFile = that.chooseFileList.filter((item) => {
|
||||
return item.name === file.name && item.size === file.size &&
|
||||
item.fileLastModified === file.lastModified && item.type === file.type;
|
||||
}).length > 0;
|
||||
|
||||
//如果文件已经存在,就不再添加了
|
||||
if(hasChooseFile){
|
||||
that.addUserLogs(`${that.lang.selected_file_exist} : ${file.name}, ${that.lang.size} : ${that.getFileSizeStr(file.size)}, ${that.lang.type} : ${file.type}`);
|
||||
continue
|
||||
}
|
||||
|
||||
that.chooseFileList.push(
|
||||
Object.assign(file, {
|
||||
fileLastModified : file.lastModified,
|
||||
index : index,
|
||||
offset : 0
|
||||
})
|
||||
)
|
||||
|
||||
//根据房间内的用户,生成文件发送记录
|
||||
for (let remoteId in that.remoteMap) {
|
||||
let hasFileRecoder = that.sendFileRecoderList.filter(recoder => {
|
||||
return file.name === recoder.name && file.size === recoder.size &&
|
||||
file.type === recoder.type && recoder.id === remoteId;
|
||||
}).length > 0;
|
||||
|
||||
//如果已经存在记录了,就不再添加了
|
||||
if (hasFileRecoder) {
|
||||
that.addUserLogs(`${that.lang.send_file_record_exist} : ${file.name} : ${that.remoteMap[remoteId].nickName}`);
|
||||
continue
|
||||
}
|
||||
|
||||
that.setRemoteInfo(remoteId, {
|
||||
[index + "offset"]: 0,
|
||||
[index + "status"]: 'wait',
|
||||
[index + "file"]: file,
|
||||
[index + "reader"]: new FileReader()
|
||||
})
|
||||
|
||||
that.sendFileRecoderList.unshift({
|
||||
index: index,
|
||||
id: remoteId,
|
||||
nickName : that.remoteMap[remoteId].nickName,
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type,
|
||||
progress: 0,
|
||||
done: false,
|
||||
start: 0,
|
||||
cost: 0,
|
||||
upload : 'wait'
|
||||
});
|
||||
|
||||
that.addUserLogs(`${that.lang.generate_send_file_record} : ${file.name}, ${that.lang.size} : ${that.getFileSizeStr(file.size)}, ${that.lang.type} : ${file.type}, : ${that.remoteMap[remoteId].nickName}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
choose: this.chooseFileCallback
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -4488,9 +4526,6 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
window.webrtcCheck = function () {
|
||||
window.Bus.$emit("webrtcCheck", {})
|
||||
}
|
||||
window.p2pCheck = function () {
|
||||
window.Bus.$emit("p2pCheck", {})
|
||||
}
|
||||
window.sendBugs = function () {
|
||||
window.Bus.$emit("sendBugs", {})
|
||||
}
|
||||
|
@@ -8,6 +8,15 @@
|
||||
|
||||
const local_lang = {
|
||||
"en": {
|
||||
"wait_for_file" : "Waiting for the other party to prepare the file",
|
||||
"send_to_all_user": "Send to all user",
|
||||
"please_use_turn_server" : "Please use turn server in settings",
|
||||
"socketConnectFail" : "Socket service connection failed, please check whether the socket service is started normally",
|
||||
"websocketHeartBeatCheckOk" : "websocket heartbeat check ok",
|
||||
"websocketHeartBeatCheckFail" : "websocket heartbeat check fail",
|
||||
"websocketDisConnected" : "disconnected",
|
||||
"websocketConnected" : "connected",
|
||||
"websocketState" : "Websocket state",
|
||||
"changeNickNameTo" : "Change nickname to",
|
||||
"changeNickNameLimit" : "The nickname can only be Chinese or English within 10 characters",
|
||||
"changeNickName" : "Change nickname",
|
||||
@@ -177,18 +186,6 @@ const local_lang = {
|
||||
"open_share_pickup_code": "Open the window to share the pickup code",
|
||||
"other_language": "Other language",
|
||||
"owner" : "Owner",
|
||||
"p2p_check": "P2p check",
|
||||
"p2p_check_principle": "P2p detection principle",
|
||||
"p2p_check_principle_detail": "This project is based on webrtc, and webrtc's p2p is limited by the connection",
|
||||
"p2p_check_principle_detail_10": "Please search for the keyword 'IP' in the execution log to see if there is a similar intranet format IP",
|
||||
"p2p_check_principle_detail_2": "The network NAT type and browser restrictions of both parties",
|
||||
"p2p_check_principle_detail_3": "Self-check steps",
|
||||
"p2p_check_principle_detail_4": "Before verifying p2p,",
|
||||
"p2p_check_principle_detail_5": "Please turn off the relay service switch first",
|
||||
"p2p_check_principle_detail_6": ", Both parties enter the room, and then perform self-check",
|
||||
"p2p_check_principle_detail_7": "After both parties join the room, if the client can obtain the intranet IP of both parties in the room, it is highly probable that p2p transmission can be performed",
|
||||
"p2p_check_principle_detail_8": "If it is chrome PC version, you can open",
|
||||
"p2p_check_principle_detail_9": "Then search for the keyword 'mdns', turn on the switch and restart",
|
||||
"password": "Password",
|
||||
"password_room": "Password",
|
||||
"password_too_long": "The password is too long",
|
||||
@@ -348,6 +345,14 @@ const local_lang = {
|
||||
"webrtc_ice_state" : "webrtc state"
|
||||
},
|
||||
"zh": {
|
||||
"wait_for_file": "对方准备文件中",
|
||||
"please_use_turn_server" : "当前网络环境不稳定,建议在设置中打开中继服务开关",
|
||||
"socketConnectFail" : "socket服务连接失败,请检查socket服务是否正常启动",
|
||||
"websocketHeartBeatCheckOk" : "websocket心跳检测通过",
|
||||
"websocketHeartBeatCheckFail" : "websocket心跳检测失败",
|
||||
"websocketDisConnected" : "连接断开",
|
||||
"websocketConnected" : "连接成功",
|
||||
"websocketState" : "websocket状态",
|
||||
"changeNickNameTo" : "更新了昵称为",
|
||||
"changeNickName" : "修改昵称",
|
||||
"changeNickNameLimit" : "昵称长度限制为10个以内中英文字符",
|
||||
@@ -527,18 +532,6 @@ const local_lang = {
|
||||
"open_share_pickup_code": "打开分享取件码窗口",
|
||||
"other_language": "其他语言",
|
||||
"owner" : "房主",
|
||||
"p2p_check": "P2p检测",
|
||||
"p2p_check_principle": "p2p检测原理",
|
||||
"p2p_check_principle_detail": "本项目是基于webrtc实现的,webrtc的p2p受限于连接",
|
||||
"p2p_check_principle_detail_10": "具体是否有获取到双方内网IP,请自行在执行日志中搜索关键字 “IP”,查看是否有类似内网格式的IP即可",
|
||||
"p2p_check_principle_detail_2": "双方的网络NAT类型和浏览器限制",
|
||||
"p2p_check_principle_detail_3": "自检步骤",
|
||||
"p2p_check_principle_detail_4": "在验证p2p之前,",
|
||||
"p2p_check_principle_detail_5": "请先关闭中继服务开关后",
|
||||
"p2p_check_principle_detail_6": ",双方进入后房间,再进行自检",
|
||||
"p2p_check_principle_detail_7": "双方加入房间后,如果客户端可以获取到房间内双方的内网IP,大概率就可以进行p2p传输",
|
||||
"p2p_check_principle_detail_8": "如果是chrome电脑版,可以打开",
|
||||
"p2p_check_principle_detail_9": "然后搜索 'mdns' 关键字,打开开关后重启即可",
|
||||
"password": "密码",
|
||||
"password_room": "密码房间",
|
||||
"password_too_long": "密码过长",
|
||||
@@ -629,6 +622,7 @@ const local_lang = {
|
||||
"send_text": "发送文本",
|
||||
"send_to": "发送至",
|
||||
"send_to_user_separately": "分别发送给用户",
|
||||
"send_to_all_user": "发送给所有用户",
|
||||
"sending": "发送中",
|
||||
"sending_history": "文件发送历史",
|
||||
"sending_to": "正在发送给",
|
||||
|
@@ -65,8 +65,7 @@ function sendFileDoneNotify(data) {
|
||||
function sendChangeNickNameNotify(data) {
|
||||
let notifyMsg = `## <font color='info'>文件传输通知</font> - <font color="warning">${data.title}</font>` +
|
||||
` - <font color="comment">${data.room}</font>\n` +
|
||||
`库记录ID: ${data.recoderId}\n` +
|
||||
`旧的昵称: ${data.oldNickName}\n` +
|
||||
`旧的昵称: ${data.preNickName}\n` +
|
||||
`新的昵称: ${data.nickName}\n` +
|
||||
`当前时间: ${utils.formateDateTime(new Date(), "yyyy-MM-dd hh:mm:ss")}\n` +
|
||||
`访问IP: ${data.ip}\n` +
|
||||
|
@@ -26,6 +26,8 @@ async function changeNickName(io, socket, tables, dbClient, data){
|
||||
nickName = nickName.toString().substr(0, 9);
|
||||
}
|
||||
|
||||
data.nickName = check.contentFilter(nickName);
|
||||
|
||||
await daoDog.addDogData({
|
||||
name: "修改个人昵称",
|
||||
roomId: data.room || "",
|
||||
@@ -37,8 +39,6 @@ async function changeNickName(io, socket, tables, dbClient, data){
|
||||
ip: ip
|
||||
}, tables, dbClient);
|
||||
|
||||
data.nickName = check.contentFilter(nickName);
|
||||
|
||||
bussinessNotify.sendChangeNickNameNotify({
|
||||
title: "修改个人昵称",
|
||||
room: data.room,
|
||||
@@ -49,7 +49,7 @@ async function changeNickName(io, socket, tables, dbClient, data){
|
||||
})
|
||||
|
||||
//更新下服务端存下的昵称
|
||||
io.sockets.connected[socket.id].nickName = nickName;
|
||||
io.sockets.connected[socket.id].nickName = data.nickName;
|
||||
|
||||
// 指定发送
|
||||
if(data.to && data.to !== ''){
|
||||
|
@@ -89,7 +89,7 @@ function setChatingComm(data){
|
||||
* @returns
|
||||
*/
|
||||
function getCacheSwitchData(){
|
||||
return cacheSwitchData;
|
||||
return cacheSwitchData || {};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -54,6 +54,12 @@
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">书</div>
|
||||
<div class="code-name">&#xe762;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">相机反转</div>
|
||||
@@ -726,9 +732,9 @@
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1692448975663') format('woff2'),
|
||||
url('iconfont.woff?t=1692448975663') format('woff'),
|
||||
url('iconfont.ttf?t=1692448975663') format('truetype');
|
||||
src: url('iconfont.woff2?t=1692802578417') format('woff2'),
|
||||
url('iconfont.woff?t=1692802578417') format('woff'),
|
||||
url('iconfont.ttf?t=1692802578417') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
@@ -754,6 +760,15 @@
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-shu"></span>
|
||||
<div class="name">
|
||||
书
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-shu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-xiangjifanzhuan"></span>
|
||||
<div class="name">
|
||||
@@ -1762,6 +1777,14 @@
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-shu"></use>
|
||||
</svg>
|
||||
<div class="name">书</div>
|
||||
<div class="code-name">#icon-rtc-file-shu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-xiangjifanzhuan"></use>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4147343 */
|
||||
src: url('iconfont.woff2?t=1692448975663') format('woff2'),
|
||||
url('iconfont.woff?t=1692448975663') format('woff'),
|
||||
url('iconfont.ttf?t=1692448975663') format('truetype');
|
||||
src: url('iconfont.woff2?t=1692802578417') format('woff2'),
|
||||
url('iconfont.woff?t=1692802578417') format('woff'),
|
||||
url('iconfont.ttf?t=1692802578417') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,10 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-rtc-file-shu:before {
|
||||
content: "\e762";
|
||||
}
|
||||
|
||||
.icon-rtc-file-xiangjifanzhuan:before {
|
||||
content: "\e6de";
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -5,6 +5,13 @@
|
||||
"css_prefix_text": "icon-rtc-file-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "4292650",
|
||||
"name": "书",
|
||||
"font_class": "shu",
|
||||
"unicode": "e762",
|
||||
"unicode_decimal": 59234
|
||||
},
|
||||
{
|
||||
"icon_id": "3405264",
|
||||
"name": "相机反转",
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user