返回 AiToEarn
draft-generation-platforms.ts
根目录 / project / aitoearn-backend / apps / aitoearn-ai / src / core / draft-generation / draft-generation-platforms.ts
1 import { AccountType } from '@yikart/common'
2
3 interface PlatformLimitRule {
4 titleMaxLength?: number
5 titleRequired?: boolean
6 descMaxLength?: number
7 descRequired?: boolean
8 topicsMaxCount?: number
9 topicsMinCount?: number
10 }
11
12 const PLATFORM_LIMIT_RULES: Record<string, PlatformLimitRule> = {
13 [AccountType.TikTok]: { descMaxLength: 2200, topicsMaxCount: 5 },
14 [AccountType.Instagram]: { descMaxLength: 2200 },
15 [AccountType.Douyin]: { titleMaxLength: 30, topicsMaxCount: 5 },
16 [AccountType.Bilibili]: { titleMaxLength: 80, titleRequired: true, descMaxLength: 250, topicsMaxCount: 10, topicsMinCount: 1 },
17 [AccountType.YouTube]: { titleMaxLength: 100, titleRequired: true, descMaxLength: 5000, descRequired: true },
18 [AccountType.Twitter]: { descMaxLength: 280, descRequired: true },
19 [AccountType.Facebook]: { descMaxLength: 5000 },
20 [AccountType.Threads]: { descMaxLength: 500, descRequired: true },
21 [AccountType.Pinterest]: { titleRequired: true },
22 [AccountType.Kwai]: { topicsMaxCount: 4 },
23 [AccountType.RedNote]: { descMaxLength: 1000 },
24 [AccountType.LinkedIn]: { titleMaxLength: 200, descMaxLength: 3000 },
25 }
26
27 function checkPlatformLimits(
28 platform: string,
29 content: { title?: string, desc?: string, topics?: string[] },
30 ): boolean {
31 const rule = PLATFORM_LIMIT_RULES[platform]
32 if (!rule)
33 return true
34
35 if (rule.titleRequired && !content.title?.trim())
36 return false
37 if (rule.titleMaxLength && (content.title?.length ?? 0) > rule.titleMaxLength)
38 return false
39 if (rule.descRequired && !content.desc?.trim())
40 return false
41 if (rule.descMaxLength && (content.desc?.length ?? 0) > rule.descMaxLength)
42 return false
43
44 const topicsCount = content.topics?.length ?? 0
45 if (rule.topicsMinCount && topicsCount < rule.topicsMinCount)
46 return false
47 if (rule.topicsMaxCount && topicsCount > rule.topicsMaxCount)
48 return false
49
50 return true
51 }
52
53 interface PlatformMediaConstraints {
54 video?: {
55 minDuration?: number
56 maxDuration?: number
57 supportedRatios?: string[]
58 }
59 image?: {
60 maxCount?: number
61 }
62 }
63
64 const PLATFORM_MEDIA_CONSTRAINTS: Partial<Record<AccountType, PlatformMediaConstraints>> = {
65 [AccountType.TikTok]: {
66 video: { minDuration: 3, maxDuration: 600 },
67 image: { maxCount: 10 },
68 },
69 [AccountType.Instagram]: {
70 video: { minDuration: 5, maxDuration: 900 },
71 image: { maxCount: 10 },
72 },
73 [AccountType.Douyin]: {
74 video: { maxDuration: 900, supportedRatios: ['9:16', '16:9', '1:1'] },
75 image: { maxCount: 9 },
76 },
77 [AccountType.Bilibili]: {
78 video: {},
79 },
80 [AccountType.YouTube]: {
81 video: { maxDuration: 43200 },
82 },
83 [AccountType.Twitter]: {
84 image: { maxCount: 4 },
85 },
86 [AccountType.Facebook]: {
87 video: { minDuration: 3, maxDuration: 14400 },
88 image: { maxCount: 10 },
89 },
90 [AccountType.Threads]: {
91 video: { maxDuration: 300 },
92 image: { maxCount: 20 },
93 },
94 [AccountType.Pinterest]: {
95 video: { minDuration: 4, maxDuration: 15 },
96 image: {},
97 },
98 [AccountType.Kwai]: {
99 video: { minDuration: 15, maxDuration: 180, supportedRatios: ['9:16'] },
100 },
101 [AccountType.RedNote]: {
102 video: { maxDuration: 900, supportedRatios: ['9:16', '3:4', '1:1', '16:9'] },
103 image: { maxCount: 9 },
104 },
105 [AccountType.WeChatOfficial]: {
106 image: {},
107 },
108 [AccountType.WeChatChannels]: {
109 video: {},
110 },
111 [AccountType.LinkedIn]: {
112 video: {},
113 image: {},
114 },
115 }
116
117 export interface DraftMediaInfo {
118 type: 'video' | 'article'
119 title?: string
120 desc?: string
121 topics?: string[]
122 duration?: number
123 aspectRatio?: string
124 imageCount?: number
125 }
126
127 export function getCompatibleAccountTypes(info: DraftMediaInfo): AccountType[] {
128 const platforms = Object.keys(PLATFORM_MEDIA_CONSTRAINTS) as AccountType[]
129
130 return platforms.filter((platform) => {
131 const mediaConstraints = PLATFORM_MEDIA_CONSTRAINTS[platform]
132 if (!mediaConstraints) {
133 return false
134 }
135
136 if (info.type === 'video') {
137 const videoRule = mediaConstraints.video
138 if (!videoRule) {
139 return false
140 }
141 if (info.duration !== undefined) {
142 if (videoRule.minDuration !== undefined && info.duration < videoRule.minDuration) {
143 return false
144 }
145 if (videoRule.maxDuration !== undefined && info.duration > videoRule.maxDuration) {
146 return false
147 }
148 }
149 if (info.aspectRatio && videoRule.supportedRatios && !videoRule.supportedRatios.includes(info.aspectRatio)) {
150 return false
151 }
152 }
153
154 if (info.type === 'article') {
155 const imageRule = mediaConstraints.image
156 if (!imageRule) {
157 return false
158 }
159 if (info.imageCount !== undefined && imageRule.maxCount !== undefined && info.imageCount > imageRule.maxCount) {
160 return false
161 }
162 }
163
164 return checkPlatformLimits(platform, {
165 title: info.title,
166 desc: info.desc,
167 topics: info.topics,
168 })
169 })
170 }
171
171 lines TYPESCRIPT