返回 AiToEarn
media.service.ts
1 import { Injectable } from '@nestjs/common'
2 import { AssetsService } from '@yikart/assets'
3 import { FileUtil, TableDto, UserType } from '@yikart/common'
4 import { Asset, AssetType, Media, MediaRepository, MediaType } from '@yikart/mongodb'
5 import { config } from '../../config'
6 import { CreateMediaDto } from './media.dto'
7
8 @Injectable()
9 export class MediaService {
10 constructor(
11 private readonly assetsService: AssetsService,
12 private readonly mediaRepository: MediaRepository,
13 ) { }
14
15 async create(userId: string, newData: CreateMediaDto) {
16 const fullUrl = newData.url.startsWith('http://') || newData.url.startsWith('https://')
17 ? newData.url
18 : FileUtil.buildUrl(newData.url)
19
20 const url = new URL(fullUrl)
21 const isOurStorage = url.origin === config.assets.endpoint
22 || (config.assets.cdnEndpoint && url.origin === config.assets.cdnEndpoint)
23
24 let asset: Asset | null = null
25
26 if (isOurStorage) {
27 const objectPath = url.pathname.substring(1)
28 const existingAsset = await this.assetsService.getByPath(objectPath)
29 if (existingAsset?.userId === userId) {
30 asset = existingAsset
31 }
32 }
33
34 if (!asset) {
35 const result = await this.assetsService.uploadFromUrl(userId, {
36 url: fullUrl,
37 type: AssetType.UserMedia,
38 })
39 asset = result.asset
40 }
41
42 return await this.mediaRepository.create({
43 ...newData,
44 userId,
45 userType: UserType.User,
46 url: asset.path,
47 metadata: {
48 size: asset.size,
49 mimeType: asset.mimeType,
50 },
51 })
52 }
53
54 /**
55 * delete media
56 * @param id
57 * @returns
58 */
59 async del(id: string) {
60 const res = await this.mediaRepository.deleteById(id)
61 return res !== null
62 }
63
64 /**
65 * delete media
66 * @param ids
67 * @returns
68 */
69 async delByIds(userId: string, ids: string[]) {
70 const res = await this.mediaRepository.deleteManyByIds(ids, {
71 userType: UserType.User,
72 userId,
73 })
74 return res
75 }
76
77 /**
78 * delete media (TODO: 待优化)
79 * @param userId
80 * @param inFilter
81 * @returns
82 */
83 async delByFilter(
84 userId: string,
85 inFilter: {
86 groupId?: string
87 type?: MediaType
88 useCount?: number
89 },
90 ) {
91 const { groupId, type, useCount } = inFilter
92 const filter = {
93 userId,
94 userType: UserType.User,
95 ...(groupId && { groupId }),
96 ...(type && { type }),
97 ...(useCount !== undefined && { useCount: { $gte: useCount } }),
98 }
99 const res = await this.mediaRepository.deleteByFilter(filter)
100 return res
101 }
102
103 /**
104 * 获取素材信息
105 * @param id
106 * @returns
107 */
108 async getInfo(id: string): Promise<Media | null> {
109 const res = await this.mediaRepository.getInfo(id)
110 return res
111 }
112
113 /**
114 * 获取素材列表
115 * @param page
116 * @param filter
117 * @param filter.userId
118 * @param filter.groupId
119 * @param filter.type
120 * @returns
121 */
122 async getList(
123 page: TableDto,
124 filter: {
125 userId: string
126 groupId?: string
127 materialGroupId?: string
128 type?: MediaType
129 userType?: UserType
130 useCount?: number
131 },
132 ) {
133 const res = await this.mediaRepository.getList(filter, page)
134 return res
135 }
136
137 async getListByGroup(groupId: string) {
138 const res = await this.mediaRepository.getListByGroup(groupId)
139 return res
140 }
141
142 async transferToGroup(
143 userId: string,
144 ids: string[],
145 targetGroupId: string,
146 mode: 'move' | 'copy',
147 ): Promise<number> {
148 if (mode === 'move') {
149 return this.mediaRepository.updateMaterialGroupByIds(ids, targetGroupId, userId)
150 }
151
152 const mediaList = await this.mediaRepository.listByIdsAndUserId(ids, userId)
153 if (mediaList.length === 0) {
154 return 0
155 }
156
157 const copies = mediaList.map(({ id: _id, ...media }) => ({
158 ...media,
159 materialGroupId: targetGroupId,
160 useCount: 0,
161 }))
162 const created = await this.mediaRepository.createMany(copies)
163 return created.length
164 }
165
166 async addUseCountOfList(userId: string, ids: string[]): Promise<boolean> {
167 const res = await this.mediaRepository.updateManyUseCountByIds(ids, {
168 userId,
169 })
170 return res
171 }
172
173 async updateInfo(id: string, newData: Partial<Media>) {
174 const oldMedia = await this.mediaRepository.getInfo(id)
175 if (oldMedia?.url !== newData.url && newData.url) {
176 const objectPath = newData.url.startsWith('http://') || newData.url.startsWith('https://')
177 ? FileUtil.trimHost(newData.url)
178 : newData.url
179
180 const asset = await this.assetsService.getByPath(objectPath)
181 if (asset) {
182 newData.metadata = {
183 size: asset.size,
184 mimeType: asset.mimeType,
185 }
186
187 newData.url = objectPath
188 }
189 }
190
191 const res = await this.mediaRepository.updateInfo(id, newData)
192 return res
193 }
194
195 /**
196 * 检查指定组是否为空(不包含任何媒体文件)
197 * @param groupId 组ID
198 * @returns 如果组为空返回true,否则返回false
199 */
200 async checkIsEmptyGroup(groupId: string): Promise<boolean> {
201 const exists = await this.mediaRepository.getIsEmptyByGroup(groupId)
202 return exists
203 }
204
205 async addUseCount(id: string): Promise<boolean> {
206 const res = await this.mediaRepository.updateUseCountById(id)
207 return res
208 }
209 }
210
210 lines TYPESCRIPT