fix: dtime (#1121)

* fix dtime

* change npm mirror

* 发布时间离当前时间必须≥4小时且≤15天

* 使用 artplayer 替换 西瓜播放器

* 自动切换播放器

* 控件修改为 ScrollList

* 移除 西瓜播放器
This commit is contained in:
XuanRan_Nyan authored and GitHub committed 2024-12-31 16:45:21 +00:00
1 parent bad5849a01
commit dfdd92ae60
9 files changed
+4216 -1842

No files matched your search

+9 -15
View File
@@ -1,27 +1,21 @@
'use client'
import {Button, ButtonGroup, Layout, List, Modal, Nav, Popconfirm, Typography} from "@douyinfe/semi-ui";
import { Layout, Modal, Nav, Typography } from "@douyinfe/semi-ui";
import {
IconDeleteStroked,
IconEdit2Stroked,
IconHelpCircle,
IconPlusCircle, IconUserCardVideo,
IconUserCardVideo,
IconVideoListStroked
} from "@douyinfe/semi-icons";
import TemplateModal from "../ui/TemplateModal";
import { Table, Avatar } from '@douyinfe/semi-ui';
import { IconMore } from '@douyinfe/semi-icons';
import { ColumnProps } from "@douyinfe/semi-ui/lib/es/table/interface";
import { Table } from '@douyinfe/semi-ui';
import { SortOrder } from "@douyinfe/semi-ui/lib/es/table";
import useSWR from "swr";
import { fetcher, FileList, StudioEntity } from "@/app/lib/api-streamer";
import { fetcher, FileList } from "@/app/lib/api-streamer";
import { useState } from "react";
import Player from 'xgplayer';
import 'xgplayer/dist/index.min.css';
import FlvPlugin from "xgplayer-flv";
import FlvJsPlugin from 'xgplayer-flv.js'
import Players from "@/app/ui/Player";
import dynamic from 'next/dynamic';
import { humDate } from "@/app/lib/utils";
const Players = dynamic(() => import("@/app/ui/Player"), {
ssr: false
});
export default function Home() {
const { Header, Footer, Sider, Content } = Layout;
const { data: data, error, isLoading } = useSWR<FileList[]>("/v1/videos", fetcher);
+90 -17
View File
@@ -1,25 +1,98 @@
import React, {useEffect} from "react";
import Player from 'xgplayer';
import 'xgplayer/dist/index.min.css';
import FlvPlugin from "xgplayer-flv";
import FlvJsPlugin from 'xgplayer-flv.js'
import React, { useEffect, useRef } from "react";
import Artplayer from 'artplayer';
import mpegts from 'mpegts.js';
const Players: React.FC<{url: string}> = ({url}) => {
useEffect(() => {
let player: Player | null = new Player({
id: 'mse',
type VideoPlayer = Artplayer | null;
interface PlayerConfig {
url: string;
height?: string;
width?: string;
}
function playFlv(video: HTMLVideoElement, url: string, art: Artplayer) {
if (mpegts.isSupported()) {
if (art.flv) {
art.flv.destroy();
art.flv = null;
}
const flv = mpegts.createPlayer({
type: 'flv',
url: url,
height: '100%',
plugins: [FlvPlugin],
// plugins: [FlvJsPlugin],
width: '100%',
});
art.flv = flv;
art.on('destroy', () => {
if (art.flv) {
art.flv.destroy();
art.flv = null;
}
});
flv.attachMediaElement(video);
flv.load();
} else {
art.notice.show = 'Unsupported playback format: flv';
}
}
const Players: React.FC<PlayerConfig> = ({ url, height = '100%', width = '100%' }) => {
const containerRef = useRef<HTMLDivElement>(null);
const playerRef = useRef<VideoPlayer>(null);
useEffect(() => {
if (!containerRef.current) return;
try {
if (playerRef.current) {
playerRef.current.destroy();
playerRef.current = null;
}
if (url.endsWith('.flv')) {
playerRef.current = new Artplayer({
container: containerRef.current,
url,
type: 'flv',
customType: {
flv: playFlv,
},
autoSize: true,
fullscreen: true,
fullscreenWeb: true,
autoOrientation: true,
plugins: [],
});
} else {
playerRef.current = new Artplayer({
container: containerRef.current,
url,
autoSize: true,
fullscreen: true,
fullscreenWeb: true,
autoOrientation: true,
plugins: [],
});
}
} catch (error) {
console.error('播放器初始化失败:', error);
}
return () => {
player?.destroy();
player = null;
if (playerRef.current) {
playerRef.current.destroy();
playerRef.current = null;
}
};
}, [url])
return <div id="mse"></div>;
}, [url, height, width]);
return (
<div
ref={containerRef}
style={{ width, height }}
/>
);
}
export default Players;
+69 -4
View File
@@ -1,7 +1,7 @@
import React, {useState} from "react";
import React, {useState, useMemo, useEffect} from "react";
import {FormFCChild} from "@douyinfe/semi-ui/lib/es/form";
import {IconChevronDown, IconChevronUp, IconMinusCircle, IconPlusCircle } from "@douyinfe/semi-icons";
import {Avatar, Button, Collapsible, Form, InputGroup, Space, Typography, ArrayField, Notification} from "@douyinfe/semi-ui";
import {Avatar, Button, Collapsible, Form, InputGroup, Space, Typography, ArrayField, Notification, ScrollList, ScrollItem} from "@douyinfe/semi-ui";
import useSWR from "swr";
import {BiliType, fetcher, StudioEntity} from "../lib/api-streamer";
import {useBiliUsers, useTypeTree} from "../lib/use-streamers";
@@ -92,6 +92,40 @@ const TemplateFields: React.FC<FormFCChild<StudioEntity & {isDtime: boolean}>> =
setOpen(!isOpen);
formApi.scrollToField('isDtime');
};
const scrollStyle = {
border: 'unset',
boxShadow: 'unset',
width: 300,
height: 300,
};
const allowedHoursList = new Array(24 * 15).fill(0).map((_, i) => {
return {
value: i,
text: `${i}小时`,
disabled: i < 4 || i >= 24 * 15,
};
});
const allowedMinutesList = new Array(60 / 5).fill(0).map((_, i) => {
return {
value: i * 5,
text: `${i * 5}分钟`,
};
});
const [selectedHours, setSelectedHours] = useState(4);
const [selectedMinutes, setSelectedMinutes] = useState(0);
useEffect(() => {
const dtime = formApi.getValue('dtime');
if (dtime) {
const hours = Math.floor(dtime / 3600);
const minutes = Math.floor((dtime % 3600) / 60);
if (hours >= 4 && hours < 24 * 15) {
setSelectedHours(hours);
setSelectedMinutes(Math.floor(minutes / 5) * 5);
}
}
}, [formApi]);
return(
<>
<Section text={'基本信息'}>
@@ -186,10 +220,41 @@ const TemplateFields: React.FC<FormFCChild<StudioEntity & {isDtime: boolean}>> =
<div style={{display: 'flex', alignItems: 'center', color: 'var(--semi-color-tertiary)'}}>
<Switch field='isDtime' label={{ text: '定时发布' }} checkedText="|" uncheckedText="〇"/>
<span style={{paddingLeft: 12, fontSize: 12}}>(当前+2小时 ≤ 可选时间 ≤ 当前+15天,转载稿件撞车判定以过审发布时间为准)</span>
<span style={{paddingLeft: 12, fontSize: 12}}>(当前+2小时 ≤ 可选时间 ≤ 当前+15天,转载稿件撞车判定以过审发布时间为准。上传速度不佳的机器请谨慎开启,或设置更大的延迟时间。)</span>
</div>
{values.isDtime ? (
<DatePicker field="dtime" label=' ' type='dateTime' fieldStyle={{ paddingTop: 0 }} />
<ScrollList style={scrollStyle} header={'延迟时间'} footer={
<Button size="small" type="primary" onClick={() => {
const delaySeconds = (selectedHours * 3600) + (selectedMinutes * 60);
formApi.setValue('dtime', delaySeconds);
Notification.success({
title: '保存成功',
content: `延迟时间:${selectedHours}小时${selectedMinutes}分钟`,
duration: 3,
position: 'top'
});
console.log(delaySeconds);
}}>
确认
</Button>
}>
<ScrollItem
mode="wheel"
cycled={true}
list={allowedHoursList}
type={1}
selectedIndex={allowedHoursList.findIndex(item => item.value === selectedHours)}
onSelect={(item) => setSelectedHours(item.value)}
/>
<ScrollItem
mode="wheel"
cycled={true}
list={allowedMinutesList}
type={2}
selectedIndex={allowedMinutesList.findIndex(item => item.value === selectedMinutes)}
onSelect={(item) => setSelectedMinutes(item.value)}
/>
</ScrollList>
) : null}
</Section>
+3 -2
View File
@@ -175,9 +175,10 @@ const Bilibili: React.FC<Props> = (props) => {
rules={[
{
validator: (rule, value) => {
value = value ?? [];
return Array.isArray(value) && value.every(item => /^(cn|ov)-gotcha\d+$/.test(item));
},
message: '例: cn-gotcha204, ov-gotcha05'
message: '例: cn-gotcha204,ov-gotcha05'
}
]}
/>
@@ -262,7 +263,7 @@ const Bilibili: React.FC<Props> = (props) => {
rules={[
{
validator: (rule, value) => {
value = value ?? (console.log(value), []);
value = value ?? [];
return Array.isArray(value) && value.every(item => /^cn-[a-z]{2,6}-[a-z]{2,4}(-[0-9]{2}){2}$/.test(item));
},
message: '例: cn-hjlheb-cu-01-01,cn-tj-ct-01-01'
+17 -9
View File
@@ -84,6 +84,13 @@ export default function Union() {
key: v.key,
};
});
const [transferData, setTransferData] = useState<(string | number)[]>([]);
const handleTransferChange = (values: (string | number)[], items: any[]) => {
setSelectFiles(values);
setTransferData(values);
};
return (<>
<UserList visible={visible} onCancel={change}></UserList>
<Modal
@@ -92,17 +99,18 @@ export default function Union() {
okText="上传"
visible={visibleModal}
onOk={handleOk}
afterClose={handleAfterClose} //>=1.16.0
afterClose={handleAfterClose}
onCancel={handleCancel}
closeOnEsc={true}
>
<Transfer
style={{ width: 568, height: 416 }}
dataSource={data}
draggable
onChange={(values, items) => setSelectFiles(values)}
/>
</Modal>
<Transfer
style={{ width: 568, height: 416 }}
dataSource={data}
draggable
value={transferData}
onChange={handleTransferChange}
/>
</Modal>
<Header style={{ backgroundColor: 'var(--semi-color-bg-1)' }}>
<nav
style={{
@@ -112,7 +120,7 @@ export default function Union() {
alignItems:'center',
justifyContent:'space-between',
flexWrap:'wrap',
boxShadow:'0 1px 2px 0 rgb(0 0 0 / 0.05)'
boxShadow:'0 1px 2px 0 rgb(0 0 0 / 0.05)'
}}
>
<div
+1 -1
View File
@@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
+4 -3
View File
@@ -1,9 +1,10 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
// reactStrictMode: true,
output: 'export',
images:{
unoptimized:true
}
images: {
unoptimized: true
},
}
module.exports = nextConfig
+4013 -1780
View File
File diff suppressed because it is too large. Load diff
+10 -11
View File
@@ -9,26 +9,25 @@
"lint": "next lint"
},
"dependencies": {
"@douyinfe/semi-ui": "^2.59.1",
"@douyinfe/semi-ui": "^2.72.0",
"artplayer": "5.1.7",
"mpegts.js": "^1.8.0",
"ncp": "^2.0.0",
"next": "^14.2.3",
"next": "^14.2.21",
"qrcode.react": "^3.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-json-tree": "^0.19.0",
"sass": "^1.77.4",
"swr": "^2.2.5",
"xgplayer": "^3.0.14",
"xgplayer-flv": "^3.0.18",
"xgplayer-flv.js": "^3.0.18"
"sass": "^1.83.0",
"swr": "^2.3.0"
},
"devDependencies": {
"@types/node": "^20.13.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"eslint": "^8.57.0",
"eslint-config-next": "^14.2.3",
"typescript": "^5.4.5",
"eslint-config-prettier": "^9.1.0"
"eslint": "^9.17.0",
"eslint-config-next": "^15.1.3",
"eslint-config-prettier": "^9.1.0",
"typescript": "^5.7.2"
}
}