diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 7b452ca..d3e769f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -98,7 +98,7 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "bili-bot" -version = "1.2.2" +version = "1.2.3" dependencies = [ "fix-path-env", "serde", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index c767656..94f4e6c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bili-bot" -version = "1.2.2" +version = "1.2.3" description = "哔哩哔哩-直播间管家机器人" authors = ["半糖人类"] license = "MIT" diff --git a/src/api/live.ts b/src/api/live.ts index 5f2329b..b317fe9 100644 --- a/src/api/live.ts +++ b/src/api/live.ts @@ -22,16 +22,6 @@ const getLiveStatusApi = async (room_ids: string) => } ); -// 获取直播线路 -const getLiveStreamApi = async () => - await getQueryData( - `${LIVE_URL_PREFIX}/xlive/app-blink/v1/live/getWebUpStreamAddr`, - { - query: { platform: "pc" }, - headers: { cookie: await getStore(LOGIN_INFO.cookie) } - } - ); - // 获取身份码 const getLiveCodeApi = async () => await getQueryData( @@ -124,8 +114,8 @@ const sendMessageApi = async (message: SendMessage) => { }); }; -// 获取直播视频流 -const getLiveStreamUrlApi = async (qn: string = "0", roomid: string) => +// 获取直播视频流 flv格式 +const getLiveFlvUrlApi = async (qn: string = "0", roomid: string) => await getQueryData(`${LIVE_URL_PREFIX}/room/v1/Room/playUrl`, { query: { cid: roomid, @@ -134,6 +124,22 @@ const getLiveStreamUrlApi = async (qn: string = "0", roomid: string) => } }); +// 获取直播视频流 m3u8格式 +const getLiveM3U8UrlApi = async (qn: string = "0", roomid: string) => + await getQueryData(`${LIVE_URL_PREFIX}/xlive/web-room/v2/index/getRoomPlayInfo`, { + query: { + device: "pc", + platform: "web", + scale: "3", + build: qn, + protocol: "0,1", + format: "0,1,2", + codec: "0,1", + room_id: roomid + } + }) + + // 获取关注的主播列表 const getMyFollowLiveInfo = async (page: string = "1") => await getQueryData( @@ -152,11 +158,11 @@ const getMyFollowLiveInfo = async (page: string = "1") => export { getLiveCategoryApi, getLiveStatusApi, - getLiveStreamApi, getLiveCodeApi, getGiftApi, getEmojiApi, sendMessageApi, - getLiveStreamUrlApi, + getLiveFlvUrlApi, + getLiveM3U8UrlApi, getMyFollowLiveInfo }; diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 7d5e994..e44d12c 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -8,3 +8,9 @@ export interface SendMessage { dm_type?: string; isInitiative?: boolean; } + +export type Stream = { + type: string; + url: string; + ext: string; +}; diff --git a/src/utils/cmd.ts b/src/utils/cmd.ts index 65747e9..0f21362 100644 --- a/src/utils/cmd.ts +++ b/src/utils/cmd.ts @@ -9,13 +9,11 @@ export const recordPath = async (folder: string) => export const testFfmpge = () => new Command("ffmpeg-version").execute(); -export const newRecorder = async (streamUrl: string, folder: string, description: string) => { +export const newRecorder = async (streamUrl: string, folder: string, description: string, ext: string) => { // 片段时长 const partDuration = "1800"; // 片段名 const segment = `part%03d` - // 文件后缀 - const ext = ".mp4" // 当前时间 const timestamp = `${dayjs().format("YYYY-MM-DD HH")}点场`; // 直播录制存储路径 diff --git a/src/views/Robot/room.vue b/src/views/Robot/room.vue index 50e91a6..c36d6a7 100644 --- a/src/views/Robot/room.vue +++ b/src/views/Robot/room.vue @@ -2,7 +2,7 @@ import { connected, startWebsocket, stopWebsocket, active, msgList, manage } from "./message/robot"; import { ROOM_URL_PREFIX } from "@/constants"; import { computed, ref, watch } from "vue"; -import { getLiveStreamUrlApi, getLiveStatusApi } from "@/api"; +import { getLiveM3U8UrlApi, getLiveStatusApi } from "@/api"; import { newRecorder, testFfmpge, recordPath } from '@/utils/cmd' import { type Child, type Command } from "@tauri-apps/api/shell"; @@ -11,7 +11,9 @@ import Video from "./video.vue"; import { message } from "@tauri-apps/api/dialog"; import { open } from "@tauri-apps/api/shell"; -const url = ref(''); +import { Stream } from '@/types' + +const streams = ref([]); const isRecording = ref(false) const getUrl = async () => { @@ -22,16 +24,44 @@ const getUrl = async () => { message(`${uname}直播间未开播!`) return false } - const res = await getLiveStreamUrlApi("10000", manage.roomid); + + const res = await getLiveM3U8UrlApi('10000', manage.roomid); if (!res) return; - const urls = res.durl.map((item: any) => item.url); + const urls = [] as Stream[] + res.playurl_info.playurl.stream.forEach((stream: any) => { + if (stream.protocol_name === 'http_hls') { + // m3u8格式 + stream.format.forEach((format: any) => { + if (format.format_name === 'ts') { + const { base_url, url_info } = format.codec[0]; + urls.push({ + type: 'm3u8', + url: `${url_info[0].host}${base_url}${url_info[0].extra}`, + ext: '.mp4' + }) + } + }); + } else if (stream.protocol_name === 'http_stream') { + // flv格式 + stream.format.forEach((format: any) => { + if (format.format_name === 'flv') { + const { base_url, url_info } = format.codec[0]; + urls.push({ + type: 'flv', + url: `${url_info[0].host}${base_url}${url_info[0].extra}`, + ext: '.mp4' + }) + } + }); + } + }); return { urls, uname, title }; } const openPreview = async () => { const res = await getUrl(); if (!res || !res?.urls.length) return - url.value = res?.urls[0]; + streams.value = [...res?.urls]; }; const scrollRef = ref(); @@ -68,19 +98,19 @@ const startRecord = async (order = 0) => { } const res = await getUrl(); if (!res || !res?.urls.length) return - const streamUrl = res?.urls[order] + const streamUrl = res?.urls[order]; const folder = `${res?.uname}-[${manage.roomid}]` // 创建录制器 - recorder = await newRecorder(streamUrl, folder, res?.title) + recorder = await newRecorder(streamUrl?.url, folder, res?.title, streamUrl?.ext) recorder.on("close", async ({ code }) => { if (code) { - console.log(`${manage.roomid}链接-${order + 1} 失败`) - if (order === 1) { + console.log(`录制${manage.roomid}-${streamUrl?.type} 视频流失败`) + if (order === res.urls.length - 1) { await message('录制视频流失败') isRecording.value = false return } - startRecord(1) + startRecord(order + 1) } else { await message('录制完成') open(await recordPath(folder)) @@ -164,7 +194,7 @@ const stopRecord = async () => {
-
diff --git a/src/views/Robot/video.vue b/src/views/Robot/video.vue index 2842d47..fb7a086 100644 --- a/src/views/Robot/video.vue +++ b/src/views/Robot/video.vue @@ -1,12 +1,16 @@