feat: expand platform adapters and preview tooling
This commit is contained in:
Generated
+1151
File diff suppressed because it is too large
Load Diff
@@ -20,6 +20,7 @@
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"typescript": "^5.7.3",
|
||||
"vite": "^6.2.0",
|
||||
"vite-plugin-vue-devtools": "^8.1.2",
|
||||
"vue-tsc": "^2.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import axios from "axios";
|
||||
import { ElNotification } from "element-plus";
|
||||
import { markBackendAvailable, markBackendUnavailable } from "@/composables/useBackendStatus";
|
||||
import { isNoBackendPreviewMode } from "@/utils/devPreview";
|
||||
|
||||
export const apiBaseUrl = import.meta.env.VITE_API_BASE_URL ?? "/api";
|
||||
|
||||
@@ -134,6 +135,10 @@ export function getApiErrorMessage(error: unknown, fallback = "请求失败,
|
||||
}
|
||||
|
||||
function notifyBackendUnavailable(message: string) {
|
||||
if (isNoBackendPreviewMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
if (now - lastBackendUnavailableNotificationAt < backendUnavailableNotificationIntervalMs) {
|
||||
return;
|
||||
|
||||
@@ -5,5 +5,8 @@ import "element-plus/dist/index.css";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import "./styles/main.css";
|
||||
import { ensurePreviewSession } from "./utils/devPreview";
|
||||
|
||||
ensurePreviewSession();
|
||||
|
||||
createApp(App).use(createPinia()).use(router).use(ElementPlus).mount("#app");
|
||||
|
||||
+67
-7
@@ -441,9 +441,7 @@ export interface SystemSettings {
|
||||
enableAutoUpload: boolean;
|
||||
deleteLocalFilesAfterUpload: boolean;
|
||||
uploadTarget: number;
|
||||
douyinProxy: PlatformProxySettings;
|
||||
bilibiliProxy: PlatformProxySettings;
|
||||
huyaProxy: PlatformProxySettings;
|
||||
platformRequestSettings: Record<string, PlatformRequestSettings>;
|
||||
webDavUpload: WebDavUploadSettings;
|
||||
s3Upload: S3UploadSettings;
|
||||
enableEventScripts: boolean;
|
||||
@@ -482,9 +480,6 @@ export interface SystemSettings {
|
||||
notifyWebhookOnLiveStarted: boolean;
|
||||
notifyWebhookOnException: boolean;
|
||||
webhookBodyTemplate: string;
|
||||
douyinUserAgent: string;
|
||||
douyinReferer: string;
|
||||
douyinCookie: string;
|
||||
}
|
||||
|
||||
export interface PlatformProxySettings {
|
||||
@@ -492,6 +487,63 @@ export interface PlatformProxySettings {
|
||||
proxyUrl: string;
|
||||
}
|
||||
|
||||
export interface PlatformRequestSettings {
|
||||
proxy: PlatformProxySettings;
|
||||
userAgent: string;
|
||||
referer: string;
|
||||
cookie: string;
|
||||
}
|
||||
|
||||
export interface PlatformOption {
|
||||
key: string;
|
||||
value: number;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export const platformOptionList: PlatformOption[] = [
|
||||
{ key: "douyin", value: 1, label: "Douyin" },
|
||||
{ key: "bilibili", value: 2, label: "Bilibili" },
|
||||
{ key: "huya", value: 3, label: "Huya" },
|
||||
{ key: "douyu", value: 4, label: "Douyu" },
|
||||
{ key: "kuaishou", value: 5, label: "Kuaishou" },
|
||||
{ key: "tiktok", value: 6, label: "TikTok" },
|
||||
{ key: "xiaohongshu", value: 7, label: "Xiaohongshu" },
|
||||
{ key: "youtube", value: 8, label: "YouTube" },
|
||||
{ key: "twitch", value: 9, label: "Twitch" },
|
||||
{ key: "pandatv", value: 10, label: "PandaTV" },
|
||||
{ key: "migu", value: 11, label: "Migu" }
|
||||
];
|
||||
|
||||
export function createDefaultPlatformRequestSettingsMap(): Record<string, PlatformRequestSettings> {
|
||||
const userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36 Edg/132.0.0.0";
|
||||
const referers: Record<string, string> = {
|
||||
douyin: "https://live.douyin.com/",
|
||||
bilibili: "https://live.bilibili.com/",
|
||||
huya: "https://www.huya.com/",
|
||||
douyu: "https://www.douyu.com/",
|
||||
kuaishou: "https://live.kuaishou.com/",
|
||||
tiktok: "https://www.tiktok.com/",
|
||||
xiaohongshu: "https://www.xiaohongshu.com/",
|
||||
youtube: "https://www.youtube.com/",
|
||||
twitch: "https://www.twitch.tv/",
|
||||
pandatv: "https://www.pandalive.co.kr/",
|
||||
migu: "https://www.miguvideo.com/"
|
||||
};
|
||||
|
||||
return platformOptionList.reduce<Record<string, PlatformRequestSettings>>((accumulator, platform) => {
|
||||
accumulator[platform.key] = {
|
||||
proxy: {
|
||||
enabled: false,
|
||||
proxyUrl: ""
|
||||
},
|
||||
userAgent,
|
||||
referer: referers[platform.key] ?? "",
|
||||
cookie: ""
|
||||
};
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export interface WebDavUploadSettings {
|
||||
endpoint: string;
|
||||
basePath: string;
|
||||
@@ -701,7 +753,15 @@ export const platformLabelMap: Record<number, string> = {
|
||||
0: "未知",
|
||||
1: "Douyin",
|
||||
2: "Bilibili",
|
||||
3: "Huya"
|
||||
3: "Huya",
|
||||
4: "Douyu",
|
||||
5: "Kuaishou",
|
||||
6: "TikTok",
|
||||
7: "Xiaohongshu",
|
||||
8: "YouTube",
|
||||
9: "Twitch",
|
||||
10: "PandaTV",
|
||||
11: "Migu"
|
||||
};
|
||||
|
||||
export const uploadTargetLabelMap: Record<number, string> = {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import type { AuthenticatedUser } from "@/types";
|
||||
|
||||
const TOKEN_STORAGE_KEY = "live-recorder-token";
|
||||
const USER_STORAGE_KEY = "live-recorder-user";
|
||||
const PREVIEW_TOKEN = "dev-preview-token";
|
||||
|
||||
const previewUser: AuthenticatedUser = {
|
||||
userId: "dev-preview",
|
||||
username: "preview",
|
||||
displayName: "UI Preview",
|
||||
token: PREVIEW_TOKEN,
|
||||
expiresAt: "2099-12-31T23:59:59.000Z"
|
||||
};
|
||||
|
||||
export const isNoBackendPreviewMode = import.meta.env.DEV && import.meta.env.VITE_PREVIEW_NO_BACKEND === "1";
|
||||
|
||||
function hasUsablePreviewUser(value: string | null) {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(value) as Partial<AuthenticatedUser>;
|
||||
return Boolean(parsed.userId && parsed.username && parsed.displayName);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function ensurePreviewSession() {
|
||||
if (!isNoBackendPreviewMode || typeof window === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!localStorage.getItem(TOKEN_STORAGE_KEY)) {
|
||||
localStorage.setItem(TOKEN_STORAGE_KEY, PREVIEW_TOKEN);
|
||||
}
|
||||
|
||||
if (!hasUsablePreviewUser(localStorage.getItem(USER_STORAGE_KEY))) {
|
||||
localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(previewUser));
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import VueDevTools from "vite-plugin-vue-devtools";
|
||||
import path from "node:path";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
export default defineConfig(({ command }) => ({
|
||||
plugins: [
|
||||
...(command === "serve" ? [VueDevTools()] : []),
|
||||
vue()
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "src")
|
||||
@@ -42,4 +46,4 @@ export default defineConfig({
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user