返回 AiToEarn
useVideoPageStore.ts
根目录 / project / aitoearn-electron / src / views / publish / children / videoPage / useVideoPageStore.ts
1 import { create } from 'zustand';
2 import { combine } from 'zustand/middleware';
3 import {
4 IPubParams,
5 IVideoChooseItem,
6 } from '@/views/publish/children/videoPage/videoPage';
7 import { generateUUID } from '@/utils';
8 import { AccountInfo } from '@/views/account/comment';
9 import { getVideoFile, IVideoFile } from '@/components/Choose/VideoChoose';
10 import { accountLogin } from '@/icp/account';
11 import { PlatType } from '../../../../../commont/AccountEnum';
12 import { message } from 'antd';
13 import {
14 PubStatus,
15 VisibleTypeEnum,
16 } from '../../../../../commont/publish/PublishEnum';
17 import lodash from 'lodash';
18 import { VideoModel } from '../../../../../electron/db/models/video';
19 import { getImgFile, IImgFile } from '../../../../components/Choose/ImgChoose';
20 import { useAICreateTitleStore } from '../../components/AICreateTitle/useAICreateTitle';
21 import { usePubStroe } from '../../../../store/pubStroe';
22 import { PubRecordModel } from '../../comment';
23 import { useAccountStore } from '../../../../store/account';
24
25 export interface IVideoPageStore {
26 // 选择的视频数据
27 videoListChoose: IVideoChooseItem[];
28 // 视频发布设置弹框的 tab选择
29 currChooseAccountId: string;
30 // 视频发布设置弹框显示隐藏状态
31 videoPubSetModalOpen: boolean;
32 // 通用发布参数
33 commonPubParams: IPubParams;
34 // 在视频页的loading状态
35 loadingPageLoading: boolean;
36 /**
37 * 当前操作的ID
38 * 每次操作都会分配一个操作ID
39 */
40 operateId: string;
41 // 当前选择的账户数据
42 currChooseAccount?: IVideoChooseItem;
43 }
44
45 const store: IVideoPageStore = {
46 videoListChoose: [],
47 currChooseAccountId: '',
48 videoPubSetModalOpen: false,
49 commonPubParams: {
50 title: '',
51 describe: '',
52 cover: undefined,
53 visibleType: VisibleTypeEnum.Public,
54 topics: [],
55 timingTime: undefined,
56 mixInfo: undefined,
57 diffParams: {
58 [PlatType.Xhs]: {},
59 [PlatType.Douyin]: {
60 hotPoint: undefined,
61 selfDeclare: undefined,
62 activitys: [],
63 },
64 [PlatType.WxSph]: {
65 isOriginal: false,
66 extLink: undefined,
67 activity: undefined,
68 },
69 },
70 },
71 loadingPageLoading: false,
72 operateId: '',
73 currChooseAccount: undefined,
74 };
75
76 const getStore = () => {
77 return lodash.cloneDeep(store);
78 };
79
80 // 视频发布所有组件的共享状态和方法
81 export const useVideoPageStore = create(
82 combine(
83 {
84 ...getStore(),
85 },
86 (_set, get, storeApi) => {
87 const set = (data: Partial<IVideoPageStore>) => {
88 _set(data);
89 if (
90 (data.hasOwnProperty('videoListChoose') &&
91 data.videoListChoose!.length !== 0) ||
92 data.hasOwnProperty('commonPubParams')
93 ) {
94 usePubStroe.getState().setVideoPubSaveData(get());
95 }
96 };
97
98 const methods = {
99 // 设置当前的账户数据
100 setCurrChooseAccount(currChooseAccount: IVideoChooseItem) {
101 set({
102 currChooseAccount,
103 });
104 },
105
106 // 设置操作ID
107 setOperateId(operateId?: string) {
108 // if (get().operateId) return;
109 set({
110 operateId: operateId || generateUUID(),
111 });
112 },
113
114 // 设置在视频页的loading状态
115 setLoadingPageLoading(loadingPageLoading: boolean) {
116 set({
117 loadingPageLoading,
118 });
119 },
120
121 // 设置视频发布设置弹框显示隐藏状态
122 setVideoPubSetModalOpen(videoPubSetModalOpen: boolean) {
123 set({
124 videoPubSetModalOpen,
125 });
126 },
127
128 // 设置发布弹框设置tab选择
129 setCurrChooseAccountId(currChooseAccountId: string) {
130 set({
131 currChooseAccountId,
132 });
133 },
134
135 // 初始化发布参数
136 pubParamsInit(): IPubParams {
137 return lodash.cloneDeep(get().commonPubParams);
138 },
139
140 // 添加视频数据
141 addVideos(videoFiles: IVideoFile[]) {
142 const { videoListChoose } = get();
143 const newValue = [...get().videoListChoose];
144 // 是否所有数据的视频全部为空
145 const isAllVideoNull =
146 newValue.every((v) => !v.video) && videoFiles.length === 1;
147 // 记录视频
148 let videoPointer = 0;
149
150 videoListChoose.map((v) => {
151 // 有数据缺少视频就填视频
152 if (!v.video) {
153 if (isAllVideoNull) {
154 v.video = videoFiles[0];
155 } else {
156 const videoFile = videoFiles[videoPointer];
157 if (!videoFile) return;
158 v.video = videoFile;
159 }
160 videoPointer++;
161 }
162 });
163
164 // 填完剩下的视频添加数据
165 for (let i = videoPointer; i < videoFiles.length; i++) {
166 const videoFile = videoFiles[i];
167 const temp = {
168 video: videoFile,
169 pubParams: methods.pubParamsInit(),
170 id: generateUUID(),
171 };
172 newValue.push(temp);
173 }
174 set({
175 videoListChoose: newValue,
176 loadingPageLoading: false,
177 });
178 setTimeout(() => methods.setVideoCoverFirst(), 10);
179 },
180
181 /**
182 * 将所有视频设置为首帧封面
183 * @param force 不管该条数据的参数是否存在 cover 字段强制覆盖
184 */
185 setVideoCoverFirst(force?: boolean) {
186 const videoListChoose = [...get().videoListChoose];
187
188 videoListChoose.map((v) => {
189 if (force) {
190 v.pubParams.cover = v.video?.cover;
191 } else {
192 if (!get().commonPubParams.cover) {
193 v.pubParams.cover = v.video?.cover;
194 }
195 }
196 });
197
198 set({
199 videoListChoose,
200 });
201 },
202
203 // 添加账户数据
204 addAccount(accounts: AccountInfo[]) {
205 const newV = [...get().videoListChoose];
206 // 判断是否只有一个视频数据
207 const isAloneVideo = !!(newV.length === 1 && newV[0].video);
208 /**
209 * 已经存在的账户ID做个去重
210 * 因为accounts会将所有选择的账户数据传入
211 */
212 const existAccountsSet = new Set<number>();
213 newV.map((v) => v.account && existAccountsSet.add(v.account.id));
214 accounts = accounts.filter((v) => !existAccountsSet.has(v.id));
215
216 // 记录账户
217 let accountPointer = 0;
218 newV.map((v) => {
219 // 有数据缺少账户就填账户
220 if (!v.account) {
221 v.account = accounts[accountPointer];
222 accountPointer++;
223 }
224 });
225
226 // 填完剩下的账户添加数据
227 for (let i = accountPointer; i < accounts.length; i++) {
228 const video = newV[0].video;
229 newV.push({
230 account: accounts[i],
231 video: isAloneVideo ? video : undefined,
232 pubParams: methods.pubParamsInit(),
233 id: generateUUID(),
234 });
235 }
236
237 set({
238 videoListChoose: newV,
239 });
240 setTimeout(() => methods.setVideoCoverFirst(), 10);
241 },
242
243 /**
244 * 清空视频或账号
245 * @param type 0=清空视频,1=清空账号
246 */
247 clearVideoList(type: 0 | 1) {
248 let newValue = [...get().videoListChoose];
249 newValue = newValue
250 .map((v) => {
251 if (type === 0) {
252 v.video = undefined;
253 return !v.account ? undefined : v;
254 } else {
255 v.account = undefined;
256 return !v.video ? undefined : v;
257 }
258 })
259 .filter(Boolean) as IVideoChooseItem[];
260 set({
261 videoListChoose: newValue,
262 });
263 },
264
265 // 清空所有数据
266 clear() {
267 set({
268 ...getStore(),
269 });
270 useAICreateTitleStore.getState().clear();
271 },
272
273 // 删除某个视频
274 deleteAloneVideo(id: string) {
275 const newValue = [...get().videoListChoose];
276 for (const videoChooseItem of newValue) {
277 if (id === videoChooseItem.id) {
278 videoChooseItem.video = undefined;
279 break;
280 }
281 }
282 set({
283 videoListChoose: newValue.filter((v) => v.video || v.account),
284 });
285 },
286
287 // 删除某一条数据
288 deleteData(id: string) {
289 set({
290 videoListChoose: [...get().videoListChoose].filter(
291 (v) => v.id !== id,
292 ),
293 });
294 },
295
296 // 单个视频或者账户添加
297 aloneAdd({
298 video,
299 account,
300 id,
301 }: {
302 video?: IVideoFile;
303 account?: AccountInfo;
304 id: string;
305 }) {
306 const newValue = [...get().videoListChoose];
307
308 const vci = newValue.find((v) => v.id === id);
309 if (!vci) return console.error(`找不到id为 ${id} 个数据`);
310 if (video) vci.video = video;
311 if (account) vci.account = account;
312
313 set({
314 videoListChoose: newValue,
315 });
316 setTimeout(() => methods.setVideoCoverFirst(), 10);
317 },
318
319 // 账户数据批量更新
320 updateAccounts({ accounts }: { accounts: AccountInfo[] }) {
321 if (!accounts) return;
322
323 const newValue = [...get().videoListChoose];
324 if (newValue.length === 0) return;
325 // key=账户ID val= videoListChoose item
326 const videoListMap = new Map<number, IVideoChooseItem>();
327
328 newValue.map((v) => {
329 videoListMap.set(v.account?.id || 0, v);
330 });
331
332 accounts.map((v) => {
333 const videoItem = videoListMap.get(v.id);
334 if (videoItem) videoItem.account = v;
335 });
336
337 set({
338 videoListChoose: newValue,
339 });
340 },
341
342 // 设置所有视频数据的发布参数
343 setPubParams(pubParmas: IPubParams) {
344 const videoListChoose = [...get().videoListChoose];
345 const commonPubParams = { ...get().commonPubParams };
346
347 videoListChoose.map((v) => {
348 Object.keys(pubParmas).map((key) => {
349 if (pubParmas.hasOwnProperty(key)) {
350 v.pubParams[key as 'title'] = pubParmas[key as 'title'];
351 commonPubParams[key as 'title'] = pubParmas[key as 'title'];
352 }
353 });
354 });
355 set({
356 videoListChoose,
357 commonPubParams,
358 });
359 },
360
361 // 设置单条数据的发布参数
362 setOnePubParams(pubParmas: IPubParams, id?: string) {
363 const newValue = [...get().videoListChoose];
364
365 const findedData = newValue.find(
366 (v) => v.id === (id || get().currChooseAccount!.id),
367 );
368 if (findedData) {
369 for (const key in pubParmas) {
370 if (pubParmas.hasOwnProperty(key)) {
371 findedData.pubParams[key as 'title'] =
372 pubParmas[key as 'title'];
373 }
374 }
375 }
376 set({
377 videoListChoose: newValue,
378 });
379 },
380
381 // 根据发布记录重新发布设置参数
382 async restartPub(
383 pubRecordList: VideoModel[],
384 accounts: AccountInfo[],
385 pubRecord?: PubRecordModel,
386 ) {
387 set({
388 loadingPageLoading: true,
389 });
390
391 const videoListChoose: IVideoChooseItem[] = [];
392 let commonPubParams: IPubParams = { ...get().commonPubParams };
393
394 const error = (msg: string) => {
395 set({
396 loadingPageLoading: false,
397 });
398 message.error(msg);
399 };
400
401 try {
402 methods.setOperateId();
403
404 for (const key in commonPubParams) {
405 if (pubRecord?.[key as 'title']) {
406 commonPubParams[key as 'title'] = pubRecord![key as 'title'];
407 }
408 }
409 commonPubParams['describe'] = pubRecord?.desc;
410
411 if (pubRecord!.commonCoverPath) {
412 const cover = await getImgFile(pubRecord!.commonCoverPath);
413 commonPubParams = {
414 ...commonPubParams,
415 cover,
416 };
417 }
418
419 // key=视频路径 val=视频文件,防止多个相同视频重复取视频文件
420 const videoFileMap = new Map<string, IVideoFile>();
421 const coverFileMap = new Map<string, IImgFile>();
422 const accountList = useAccountStore.getState().accountList;
423 for (let i = 0; i < pubRecordList.length; i++) {
424 const pubRecord = pubRecordList[i];
425 const videoPath = pubRecord.videoPath!;
426 const coverPath = pubRecord.coverPath!;
427 // 账户数据更新
428 const account = accountList.find(
429 (account) => account.id === accounts[i].id,
430 );
431
432 // 视频获取
433 let video: void | IVideoFile;
434 if (videoFileMap.has(videoPath)) {
435 video = videoFileMap.get(videoPath);
436 } else {
437 video = await getVideoFile(videoPath).catch((e) => {
438 console.log(e);
439 });
440 }
441 // 封面获取
442 let cover: void | IImgFile;
443 if (coverFileMap.has(coverPath)) {
444 cover = coverFileMap.get(coverPath);
445 } else {
446 cover = await getImgFile(coverPath).catch((e) => {
447 console.log(e);
448 });
449 }
450
451 if (!video) return error(`视频获取失败:${videoPath}`);
452 if (!cover) return error(`封面获取失败:${coverPath}`);
453
454 videoFileMap.set(videoPath, video);
455 coverFileMap.set(coverPath, cover);
456
457 pubRecord.status = PubStatus.UNPUBLISH;
458 pubRecord.id = undefined;
459 pubRecord.failMsg = undefined;
460 pubRecord.dataId = undefined;
461 pubRecord.previewVideoLink = undefined;
462 pubRecord.previewVideoLink = undefined;
463
464 const pubParams = {
465 ...commonPubParams,
466 ...pubRecord,
467 cover: cover,
468 describe: pubRecord.desc,
469 id: undefined,
470 };
471
472 videoListChoose.push({
473 id: generateUUID(),
474 account,
475 video,
476 pubParams,
477 });
478 }
479 } catch (e) {
480 console.warn(e);
481 }
482
483 set({
484 videoListChoose,
485 loadingPageLoading: false,
486 commonPubParams,
487 });
488 },
489
490 // 根据视频发布的临时存储记录设置发布参数
491 async setTempSaveParams({
492 videoListChoose,
493 commonPubParams,
494 operateId,
495 }: {
496 videoListChoose: IVideoChooseItem[];
497 commonPubParams?: IPubParams;
498 operateId?: string;
499 }) {
500 set({
501 loadingPageLoading: true,
502 });
503 methods.setOperateId(operateId);
504
505 // key=视频路径 val=视频文件,防止多个相同视频重复取视频文件
506 const videoFileMap = new Map<string, IVideoFile>();
507 const coverFileMap = new Map<string, IImgFile>();
508 try {
509 const accountList = useAccountStore.getState().accountList;
510 // 账户数据更新
511 videoListChoose = videoListChoose.map((v) => {
512 v.account = accountList.find(
513 (account) => v.account?.id === account.id,
514 );
515 return v;
516 });
517
518 if (commonPubParams?.cover?.imgPath) {
519 commonPubParams.cover = await getImgFile(
520 commonPubParams.cover.imgPath,
521 );
522 }
523 for (const item of videoListChoose) {
524 const videoPath = item.video?.videoPath;
525 if (videoPath) {
526 if (videoFileMap.has(videoPath)) {
527 item.video = videoFileMap.get(videoPath);
528 } else {
529 item.video = await getVideoFile(videoPath);
530 videoFileMap.set(item.video.videoPath, item.video);
531 }
532 }
533
534 const imgPath = item.pubParams.cover?.imgPath;
535 if (imgPath) {
536 if (coverFileMap.has(imgPath)) {
537 item.pubParams.cover = coverFileMap.get(imgPath);
538 } else {
539 item.pubParams.cover = await getImgFile(imgPath);
540 coverFileMap.set(imgPath, item.pubParams.cover);
541 }
542 }
543 }
544 } catch (e) {
545 console.warn(e);
546 }
547
548 set({
549 ...(commonPubParams
550 ? {
551 commonPubParams,
552 }
553 : {}),
554 videoListChoose,
555 loadingPageLoading: false,
556 });
557 },
558
559 /**
560 * 账户重新登录。登录成功后会自动更新该条账户数据
561 */
562 async accountRestart(pType: PlatType) {
563 const res = await accountLogin(pType);
564 if (!res) return;
565 console.log(res);
566 message.success('登录成功!');
567 // 更新此条账户数据
568 methods.updateAccounts({ accounts: [res] });
569 },
570 };
571 return methods;
572 },
573 ),
574 );
575
575 lines TYPESCRIPT