shuisheng 1 年間 前
コミット
02f2e11107

+ 5 - 6
README.md

@@ -27,6 +27,7 @@ billd 直播间,目前实现了类似 [bilibili 的 Web 在线直播](https://
 | 直播间网页端 | [billd-live](https://github.com/galaxy-s10/billd-live)                           | [![github](https://img.shields.io/github/stars/galaxy-s10/billd-live?label=star&logo=GitHub)](https://github.com/galaxy-s10/billd-live) [![github](https://img.shields.io/github/forks/galaxy-s10/billd-live?label=fork&logo=GitHub)](https://github.com/galaxy-s10/billd-live)                                                     | [https://live.hsslive.cn](https://live.hsslive.cn)                             |
 | 远程桌面     | [billd-desk](https://github.com/galaxy-s10/billd-desk)                           | [![github](https://img.shields.io/github/stars/galaxy-s10/billd-desk?label=star&logo=GitHub)](https://github.com/galaxy-s10/billd-desk) [![github](https://img.shields.io/github/forks/galaxy-s10/billd-desk?label=fork&logo=GitHub)](https://github.com/galaxy-s10/billd-desk)                                                     | [https://live.hsslive.cn/remoteDeskTop](https://live.hsslive.cn/remoteDeskTop) |
 | 直播间移动端 | [billd-live-react-native](https://github.com/galaxy-s10/billd-live-react-native) | [![github](https://img.shields.io/github/stars/galaxy-s10/billd-live-react-native?label=star&logo=GitHub)](https://github.com/galaxy-s10/billd-live-react-native) [![github](https://img.shields.io/github/forks/galaxy-s10/billd-live-react-native?label=fork&logo=GitHub)](https://github.com/galaxy-s10/billd-live-react-native) |                                                                                |
+| 直播间客户端 | [billd-live-electron](https://github.com/galaxy-s10/billd-live-electron)         | [![github](https://img.shields.io/github/stars/galaxy-s10/billd-live-electron?label=star&logo=GitHub)](https://github.com/galaxy-s10/billd-live-flutter) [![github](https://img.shields.io/github/forks/galaxy-s10/billd-live-electron?label=fork&logo=GitHub)](https://github.com/galaxy-s10/billd-live-electron)                  |                                                                                |
 | 直播间移动端 | [billd-live-flutter](https://github.com/galaxy-s10/billd-live-flutter)           | [![github](https://img.shields.io/github/stars/galaxy-s10/billd-live-flutter?label=star&logo=GitHub)](https://github.com/galaxy-s10/billd-live-flutter) [![github](https://img.shields.io/github/forks/galaxy-s10/billd-live-flutter?label=fork&logo=GitHub)](https://github.com/galaxy-s10/billd-live-flutter)                     |                                                                                |
 | 直播间移动端 | [billd-live-kotlin](https://github.com/galaxy-s10/billd-live-kotlin)             | [![github](https://img.shields.io/github/stars/galaxy-s10/billd-live-kotlin?label=star&logo=GitHub)](https://github.com/galaxy-s10/billd-live-kotlin) [![github](https://img.shields.io/github/forks/galaxy-s10/billd-live-kotlin?label=fork&logo=GitHub)](https://github.com/galaxy-s10/billd-live-kotlin)                         |                                                                                |
 | 直播间后台   | [billd-live-admin](https://github.com/galaxy-s10/billd-live-admin)               | [![github](https://img.shields.io/github/stars/galaxy-s10/billd-live-admin?label=star&logo=GitHub)](https://github.com/galaxy-s10/billd-live-admin) [![github](https://img.shields.io/github/forks/galaxy-s10/billd-live-admin?label=fork&logo=GitHub)](https://github.com/galaxy-s10/billd-live-admin)                             | [https://live-admin.hsslive.cn](https://live-admin.hsslive.cn)                 |
@@ -39,6 +40,7 @@ billd 直播间,目前实现了类似 [bilibili 的 Web 在线直播](https://
 - [x] msr 推流,ffmpeg转码,`http-flv` 或 `hls`拉流
 - [x] 一对一打PK
 - [x] 一对多打PK
+- [x] 多对多打PK
 - [x] 前端混流
 - [x] 推流鉴权
 - [x] 拉流鉴权
@@ -54,7 +56,7 @@ billd 直播间,目前实现了类似 [bilibili 的 Web 在线直播](https://
 
 ## 技术栈
 
-- 前端相关:[Vue3](https://vuejs.org) 以及相关技术栈、`Typescript`、`WebRTC`、`Web Workder`、`Web Audio`、`Canvas`
+- 前端相关:[Vue3](https://vuejs.org) 以及相关技术栈、`Typescript`、`WebRTC`、`WebCodecs`、`Web Audio`、`Web Workder`、`Canvas`
 - 后端相关:[Nodejs](https://nodejs.org) 以及相关技术栈、`Koa2`、`Sequelize`、`Mysql`、`Redis`、`Socket.io`
 - 流媒体服务器相关:[SRS](https://ossrs.net)、 [FFmpeg](https://ffmpeg.org)、[Coturn](https://github.com/coturn/coturn)
 - Docker 相关:[Docker](https://www.docker.com)
@@ -63,12 +65,9 @@ billd 直播间,目前实现了类似 [bilibili 的 Web 在线直播](https://
 
 apifox:[https://apifox.com/apidoc/shared-c7556b54-17b2-494e-a039-572d83f103ed](https://apifox.com/apidoc/shared-c7556b54-17b2-494e-a039-572d83f103ed)
 
-## 客户端下载
+## 下载
 
-- 安卓端:[点击下载](https://resource.hsslive.cn/billd-live/image/e859ec0e1fac824ce92b6522104f53ed.apk)
-- 苹果端:TODO
-- Windows端:TODO
-- Mac端:TODO
+[https://live.hsslive.cn/download](https://live.hsslive.cn/download)
 
 ## 预览
 

+ 7 - 7
doc/流程.drawio

@@ -1,6 +1,6 @@
 <mxfile host="65bd71144e">
     <diagram id="dqmK7txivQjgTJvqpF2s" name="第 1 页">
-        <mxGraphModel dx="1738" dy="809" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
+        <mxGraphModel dx="945" dy="483" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
             <root>
                 <mxCell id="0"/>
                 <mxCell id="1" parent="0"/>
@@ -8,16 +8,16 @@
                     <mxGeometry relative="1" as="geometry"/>
                 </mxCell>
                 <mxCell id="2" value="安卓端" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
-                    <mxGeometry x="70" y="80" width="120" height="60" as="geometry"/>
+                    <mxGeometry x="80" y="80" width="120" height="60" as="geometry"/>
                 </mxCell>
                 <mxCell id="3" value="流媒体服务器" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
-                    <mxGeometry x="150" y="300" width="120" height="60" as="geometry"/>
+                    <mxGeometry x="200" y="300" width="120" height="60" as="geometry"/>
                 </mxCell>
                 <mxCell id="6" value="推流" style="edgeStyle=none;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="4" target="3" edge="1">
                     <mxGeometry relative="1" as="geometry"/>
                 </mxCell>
                 <mxCell id="4" value="浏览器" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
-                    <mxGeometry x="270" y="80" width="120" height="60" as="geometry"/>
+                    <mxGeometry x="280" y="80" width="120" height="60" as="geometry"/>
                 </mxCell>
                 <mxCell id="9" value="拉流" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="3" target="7" edge="1">
                     <mxGeometry relative="1" as="geometry">
@@ -25,12 +25,12 @@
                     </mxGeometry>
                 </mxCell>
                 <mxCell id="7" value="安卓端" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
-                    <mxGeometry x="70" y="510" width="120" height="60" as="geometry"/>
+                    <mxGeometry x="80" y="500" width="120" height="60" as="geometry"/>
                 </mxCell>
                 <mxCell id="8" value="浏览器" style="whiteSpace=wrap;html=1;" parent="1" vertex="1">
-                    <mxGeometry x="280" y="510" width="120" height="60" as="geometry"/>
+                    <mxGeometry x="320" y="500" width="120" height="60" as="geometry"/>
                 </mxCell>
-                <mxCell id="13" value="拉流" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="3" target="8">
+                <mxCell id="13" value="拉流" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="3" target="8" edge="1">
                     <mxGeometry relative="1" as="geometry">
                         <mxPoint x="220" y="339.9999999999998" as="sourcePoint"/>
                         <mxPoint x="140" y="520.0000000000002" as="targetPoint"/>

+ 1 - 0
src/components/SystemModal/index.vue

@@ -6,6 +6,7 @@
         title="提示"
         role="dialog"
         closable
+        @close="showModal = false"
       >
         <div>
           欢迎进入直播间,遇到问题请提<a

+ 13 - 3
src/constant.ts

@@ -57,11 +57,21 @@ export const QINIU_RESOURCE = {
 export const COMMON_URL = {
   apifox: `https://apifox.com/apidoc/shared-c7556b54-17b2-494e-a039-572d83f103ed/`,
   admin: `https://live-admin.${prodDomain}`,
-  androidApp: `${QINIU_RESOURCE.url}/billd-live/image/app-release.apk`,
-  windows: `${QINIU_RESOURCE.url}/test/billd-desk-Win-0.0.1-Installer.exe`,
-  macos: `${QINIU_RESOURCE.url}/test/billd-desk-Mac-0.0.1-Installer.dmg`,
   bilibiliCollectiondetail: `https://space.bilibili.com/381307133/channel/collectiondetail?sid=1458070&ctype=0`,
   payCoursesArticle: `https://www.${prodDomain}/article/151`,
+  download: {
+    androidFlutter: `${QINIU_RESOURCE.url}/billd-live/image/app-release.apk`,
+    deskWindows: `${QINIU_RESOURCE.url}/test/billd-desk-Win-0.0.1-Installer.exe`,
+    deskMacOS: `${QINIU_RESOURCE.url}/test/billd-desk-Mac-0.0.1-Installer.dmg`,
+  },
+  release: {
+    flutter: 'https://github.com/galaxy-s10/billd-live-flutter/releases',
+    kotlin: 'https://github.com/galaxy-s10/billd-live-kotlin/releases',
+    reactNative:
+      'https://github.com/galaxy-s10/billd-live-react-native/releases',
+    electron: 'https://github.com/galaxy-s10/billd-live-electron/releases',
+    desk: 'https://github.com/galaxy-s10/billd-desk/releases',
+  },
 };
 
 export const DEFAULT_AUTH_INFO = {

+ 7 - 47
src/layout/pc/head/index.vue

@@ -40,56 +40,16 @@
             {{ t('layout.liveAdmin') }}
           </a>
 
-          <span class="item">
-            <Dropdown
-              class="download"
-              v-if="!isMobile()"
-            >
-              <template #btn>
-                <div class="btn">
-                  <span>{{ t('layout.download') }}</span>
-                  <VPIconChevronDown class="icon"></VPIconChevronDown>
-                </div>
-              </template>
-              <template #list>
-                <div class="list">
-                  <a
-                    class="item"
-                    :href="COMMON_URL.androidApp"
-                    @click.prevent="openToTarget(COMMON_URL.androidApp)"
-                  >
-                    <div class="txt">{{ t('layout.android') }}</div>
-                    <VPIconExternalLink class="icon"></VPIconExternalLink>
-                  </a>
-                  <a
-                    class="item"
-                    @click.prevent="handleTip"
-                  >
-                    <div class="txt">{{ t('layout.ios') }}</div>
-                  </a>
-                  <a
-                    class="item"
-                    :href="COMMON_URL.windows"
-                    @click.prevent="openToTarget(COMMON_URL.windows)"
-                  >
-                    <div class="txt">{{ t('layout.windows') }}</div>
-                    <VPIconExternalLink class="icon"></VPIconExternalLink>
-                  </a>
-                  <a
-                    class="item"
-                    :href="COMMON_URL.macos"
-                    @click.prevent="openToTarget(COMMON_URL.macos)"
-                  >
-                    <div class="txt">{{ t('layout.macos') }}</div>
-                    <VPIconExternalLink class="icon"></VPIconExternalLink>
-                  </a>
-                </div>
-              </template>
-            </Dropdown>
+          <a
+            class="item"
+            v-if="!isMobile()"
+            @click.prevent="router.push({ name: routerName.download })"
+          >
+            {{ t('layout.download') }}
             <div class="badge">
               <div class="txt">new</div>
             </div>
-          </span>
+          </a>
 
           <a
             class="item"

+ 6 - 0
src/router/index.ts

@@ -46,6 +46,7 @@ export const routerName = {
   notFound: 'notFound',
   group: 'group',
   profile: 'profile',
+  download: 'download',
 
   pull: 'pull',
   push: 'push',
@@ -132,6 +133,11 @@ export const defaultRoutes: RouteRecordRaw[] = [
         path: '/profile/:userId',
         component: () => import('@/views/profile/index.vue'),
       },
+      {
+        name: routerName.download,
+        path: '/download',
+        component: () => import('@/views/download/index.vue'),
+      },
       {
         name: routerName.sponsors,
         path: '/sponsors',

+ 215 - 0
src/views/download/index.vue

@@ -0,0 +1,215 @@
+<template>
+  <div class="download-wrap">
+    <div class="content">
+      <h1 class="title">下载</h1>
+      <div class="hr"></div>
+      <div class="list">
+        <div class="item">
+          <h2>安卓版(flutter)</h2>
+          <p>
+            最新版本:<span
+              class="link"
+              @click="openToTarget(COMMON_URL.download.androidFlutter)"
+              >v0.0.1</span
+            >
+          </p>
+          <p>
+            历史版本:<span
+              class="link"
+              @click="openToTarget(COMMON_URL.release.flutter)"
+              >查看</span
+            >
+          </p>
+        </div>
+        <div class="hr"></div>
+        <div class="item">
+          <h2>安卓版(react-native)</h2>
+          <p>最新版本:<span>todo</span></p>
+          <p>
+            历史版本:<span
+              class="link"
+              @click="openToTarget(COMMON_URL.release.reactNative)"
+              >查看</span
+            >
+          </p>
+        </div>
+        <div class="hr"></div>
+        <div class="item">
+          <h2>安卓版(kotlin)</h2>
+          <p>最新版本:<span>todo</span></p>
+          <p>
+            历史版本:<span
+              class="link"
+              @click="openToTarget(COMMON_URL.release.kotlin)"
+              >查看</span
+            >
+          </p>
+        </div>
+        <div class="hr"></div>
+        <div class="item">
+          <h2>苹果版(flutter)</h2>
+          <p>最新版本:<span>todo</span></p>
+          <p>
+            历史版本:<span
+              class="link"
+              @click="openToTarget(COMMON_URL.release.flutter)"
+              >查看</span
+            >
+          </p>
+        </div>
+        <div class="hr"></div>
+        <div class="item">
+          <h2>苹果版(react-native)</h2>
+          <p>最新版本:<span>todo</span></p>
+          <p>
+            历史版本:<span
+              class="link"
+              @click="openToTarget(COMMON_URL.release.reactNative)"
+              >查看</span
+            >
+          </p>
+        </div>
+        <div class="hr"></div>
+        <div class="item">
+          <h2>Windows版(electron)</h2>
+          <p>最新版本:<span>todo</span></p>
+          <p>
+            历史版本:<span
+              class="link"
+              @click="openToTarget(COMMON_URL.release.electron)"
+              >查看</span
+            >
+          </p>
+        </div>
+        <div class="hr"></div>
+        <div class="item">
+          <h2>macOS版(electron)</h2>
+          <p>最新版本:<span>todo</span></p>
+          <p>
+            历史版本:<span
+              class="link"
+              @click="openToTarget(COMMON_URL.release.electron)"
+              >查看</span
+            >
+          </p>
+        </div>
+        <div class="hr"></div>
+        <div class="item">
+          <h2>Linux版(electron)</h2>
+          <p>最新版本:<span>todo</span></p>
+          <p>
+            历史版本:<span
+              class="link"
+              @click="openToTarget(COMMON_URL.release.electron)"
+              >查看</span
+            >
+          </p>
+        </div>
+      </div>
+    </div>
+    <div class="aside">
+      <div class="title">本页目录</div>
+      <div class="item">安卓版(flutter)</div>
+      <div class="item">安卓版(react-native)</div>
+      <div class="item">安卓版(kotlin)</div>
+      <div class="item">苹果版(flutter)</div>
+      <div class="item">苹果版(react-native)</div>
+      <div class="item">Windows版(electron)</div>
+      <div class="item">macOS版(electron)</div>
+      <div class="item">Linux版(electron)</div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { openToTarget } from 'billd-utils';
+import { onMounted, ref } from 'vue';
+
+import { fetchServerInfo } from '@/api/other';
+import { COMMON_URL } from '@/constant';
+import { BilldHtmlWebpackPluginLog, IServerInfo } from '@/interface';
+// @ts-ignore
+const billd: BilldHtmlWebpackPluginLog = process.env.BilldHtmlWebpackPlugin;
+
+const serverInfo = ref<IServerInfo>();
+const loading = ref(false);
+
+async function handleFetchServerInfo() {
+  try {
+    loading.value = true;
+    const res = await fetchServerInfo();
+    if (res.code === 200) {
+      serverInfo.value = res.data;
+    }
+  } catch (error) {
+    console.log(error);
+  } finally {
+    loading.value = false;
+  }
+}
+
+onMounted(() => {
+  handleFetchServerInfo();
+});
+</script>
+
+<style lang="scss" scoped>
+.download-wrap {
+  display: flex;
+  box-sizing: border-box;
+  margin: 0 auto;
+  padding-top: 50px;
+  width: 960px;
+  color: rgb(33, 53, 71);
+  .link {
+    cursor: pointer;
+    color: $theme-color-gold;
+  }
+
+  .content {
+    flex: 1;
+    .title {
+      margin: 0;
+      font-weight: 500;
+      font-size: 40px;
+      margin-bottom: 20px;
+    }
+    .hr {
+      margin: 60px 0 20px 0;
+      width: 100%;
+      height: 1px;
+      background-color: #e7e7e7;
+    }
+    .list {
+      h2 {
+        font-weight: 600;
+      }
+      .item {
+        position: relative;
+        font-size: 16px;
+      }
+    }
+  }
+
+  .aside {
+    padding-left: 90px;
+    width: 150px;
+
+    .title {
+      color: rgb(33, 53, 71);
+      font-weight: 700;
+      font-size: 12px;
+      margin-bottom: 8px;
+    }
+    .item {
+      margin-bottom: 8px;
+      color: rgba(60, 60, 60, 0.7);
+      font-size: 13px;
+      cursor: pointer;
+      &:hover {
+        color: #213547;
+      }
+    }
+  }
+}
+</style>

+ 10 - 0
src/views/push/index.vue

@@ -783,6 +783,16 @@ onUnmounted(() => {
   });
   clearFrame();
   worker.value?.terminate();
+
+  appStore.allTrack.forEach((v) => {
+    v.videoEl?.pause();
+    v.videoEl?.removeAttribute('src');
+    v.videoEl?.remove();
+    v.stream?.getTracks().forEach((track) => {
+      track.stop();
+      v.stream?.removeTrack(track);
+    });
+  });
 });
 
 async function initUserMedia() {

+ 23 - 1
src/views/remoteDesktop/index.vue

@@ -1,5 +1,26 @@
 <template>
   <div>
+    <n-space>
+      <n-button
+        type="primary"
+        @click="openToTarget(COMMON_URL.download.deskWindows)"
+      >
+        Windows版下载
+      </n-button>
+      <n-button
+        type="primary"
+        @click="openToTarget(COMMON_URL.download.deskMacOS)"
+      >
+        macOS版下载
+      </n-button>
+      <n-button
+        type="primary"
+        @click="openToTarget(COMMON_URL.release.desk)"
+      >
+        历史版本
+      </n-button>
+    </n-space>
+
     <h1 v-if="NODE_ENV === 'development'">
       我的id:{{ mySocketId }},<n-button @click="copyToClipBoard(mySocketId)">
         复制
@@ -45,9 +66,10 @@
 
 <script lang="ts" setup>
 import { Key } from '@nut-tree/shared';
-import { copyToClipBoard, getRandomString } from 'billd-utils';
+import { copyToClipBoard, getRandomString, openToTarget } from 'billd-utils';
 import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
 
+import { COMMON_URL } from '@/constant';
 import { usePull } from '@/hooks/use-pull';
 import { useTip } from '@/hooks/use-tip';
 import { useAppStore } from '@/store/app';