forked from 77media/video-flow
106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
import { ScriptSlice, ScriptSliceType } from "@/app/service/domain/valueObject";
|
||
|
||
export function parseScriptEntity(text: string): ScriptSlice {
|
||
const scriptSlice = new ScriptSlice(
|
||
// 生成唯一ID,单次使用即可
|
||
`${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`,
|
||
ScriptSliceType.text,
|
||
text,
|
||
{}
|
||
);
|
||
return scriptSlice;
|
||
}
|
||
|
||
/**
|
||
* @description 节流函数,限制函数在指定时间间隔内只执行一次
|
||
* @param {Function} func - 需要被节流的函数
|
||
* @param {number} delay - 节流时间间隔(毫秒)
|
||
* @returns {Function} - 节流后的新函数
|
||
* @throws {Error} - 如果参数类型不正确
|
||
* @example
|
||
* const throttledFn = throttle(() => { console.log('触发'); }, 1000);
|
||
* window.addEventListener('resize', throttledFn);
|
||
*/
|
||
export function throttle<T extends (...args: any[]) => any>(
|
||
func: T,
|
||
delay: number = 100
|
||
): (...args: Parameters<T>) => void {
|
||
if (typeof delay !== "number" || delay < 0) {
|
||
throw new Error("throttle: 第二个参数必须是非负数");
|
||
}
|
||
let lastCall = 0;
|
||
return (...args: Parameters<T>) => {
|
||
const now = Date.now();
|
||
if (now - lastCall >= delay) {
|
||
lastCall = now;
|
||
func(...args);
|
||
}
|
||
};
|
||
}
|
||
/**
|
||
* 创建1920x1080屏幕适配的CSS样式
|
||
* 自动计算缩放比例并应用到页面元素
|
||
*/
|
||
export function createScreenAdapter(): void {
|
||
// 获取当前窗口尺寸
|
||
const currentWidth = window.innerWidth;
|
||
const currentHeight = window.innerHeight;
|
||
// 计算缩放比例 (1920x1080)
|
||
const wScale = currentWidth / 1920;
|
||
const hScale = currentHeight / 1080;
|
||
|
||
// 检查app节点是否存在
|
||
const app = document.getElementById("app");
|
||
if (!app) {
|
||
console.error("未找到app节点");
|
||
return;
|
||
}
|
||
(window as any).Scale = {
|
||
wScale,
|
||
hScale,
|
||
};
|
||
// 创建样式元素
|
||
const style = document.createElement("style");
|
||
|
||
// 设置CSS样式
|
||
style.textContent = `
|
||
body {
|
||
#app {
|
||
transform-origin: top left;
|
||
transform: scale(${wScale}, ${hScale});
|
||
width: 1920px;
|
||
height: 1080px;
|
||
}
|
||
}
|
||
`;
|
||
|
||
// 将样式添加到head
|
||
document.head.appendChild(style);
|
||
}
|
||
|
||
export const downloadVideo = async (url: string) => {
|
||
try {
|
||
const response = await fetch(url);
|
||
const blob = await response.blob();
|
||
const blobUrl = window.URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = blobUrl;
|
||
a.download = url.split('/').pop() || 'video.mp4';
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
window.URL.revokeObjectURL(blobUrl);
|
||
} catch (error) {
|
||
console.error('下载视频失败:', error);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 下载所有视频
|
||
* @param urls 视频URL列表
|
||
*/
|
||
export const downloadAllVideos = async (urls: string[]) => {
|
||
for (const url of urls) {
|
||
await downloadVideo(url);
|
||
}
|
||
}; |