diff --git a/registry/lib/components/utils/download-emoticons/Widget.vue b/registry/lib/components/utils/download-emoticons/Widget.vue new file mode 100644 index 000000000..56068784f --- /dev/null +++ b/registry/lib/components/utils/download-emoticons/Widget.vue @@ -0,0 +1,64 @@ + + + diff --git a/registry/lib/components/utils/download-emoticons/emoticons.ts b/registry/lib/components/utils/download-emoticons/emoticons.ts new file mode 100644 index 000000000..c6b893d64 --- /dev/null +++ b/registry/lib/components/utils/download-emoticons/emoticons.ts @@ -0,0 +1,106 @@ +import { monkey } from '@/core/ajax' +import { useScopedConsole } from '@/core/utils/log' + +const logger = useScopedConsole('表情包下载') + +interface DownloadPromise { + pkg_name: string // 表情包名称 + emoticons: { emoji: string; blob: Promise }[] +} + +export class Emoticons { + private async getEmoticonsByRoomId(roomId: string) { + const response = await this.apiRequest( + `https://api.live.bilibili.com/xlive/web-ucenter/v2/emoticon/GetEmoticons?platform=pc&room_id=${roomId}`, + ) + return response + } + async downloadEmoticons(emoticonsArray) { + const downloadPromises: DownloadPromise[] = emoticonsArray.map(obj => { + const emoticons = obj.emoticons.map(item => { + try { + const blob = this.getImageBlob(item.url) + return { + emoji: item.emoji, + blob, + } + } catch (error) { + logger.error(`下载表情 ${item.emoji} 失败:`, error) + return null + } + }) + return { + emoticons, + pkg_name: obj.pkg_name, + } + }) + return downloadPromises + } + + async getEmoticonsArray(roomId: string): Promise> { + try { + let response = await this.getEmoticonsByRoomId(roomId) + if (response.data.data.length === 0) { + logger.log('获取表情包失败,尝试获取真实roomId') + const realRoomId = await this.getRealRoomId(roomId) + response = await this.getEmoticonsByRoomId(realRoomId) + } + return response.data.data.slice(2) + } catch (error) { + throw new Error('获取表情包失败') + } + } + + async batchDownload(tasks: DownloadPromise[]) { + const results: any[] = [] + for (const task of tasks) { + const promise = (async () => { + const blobs: Blob[] = [] + const blobPromises = task.emoticons.map(e => e.blob) + for (const blobPromise of blobPromises) { + const blob = await blobPromise + blobs.push(blob) + } + return { + pkg_name: task.pkg_name, + emoticons: task.emoticons.map((e, i) => ({ emoji: e.emoji, blob: blobs[i] })), + } + })() + results.push(promise) + } + return Promise.all(results) + } + + // 有的直播间id是超短id,需要获取到原始的roomid才能通过api获取到表情包 + async getRealRoomId(rid: string): Promise { + try { + const response = await this.apiRequest( + `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${rid}`, + ) + if (Object.keys(response.data).length === 0) { + throw new Error('获取直播间id失败') + } + return response.data.room_id + } catch (error) { + throw new Error('获取直播间id失败') + } + } + + apiRequest = (url: string): Promise => { + const details: MonkeyXhrBasicDetails = { + url, + method: 'GET', + responseType: 'json', + } + return monkey(details) + } + + getImageBlob = async (url: string) => { + const details: MonkeyXhrBasicDetails = { + url, + method: 'GET', + responseType: 'blob', + } + return monkey(details) + } +} diff --git a/registry/lib/components/utils/download-emoticons/index.md b/registry/lib/components/utils/download-emoticons/index.md new file mode 100644 index 000000000..7afd6ff1b --- /dev/null +++ b/registry/lib/components/utils/download-emoticons/index.md @@ -0,0 +1,3 @@ +## 下载up主专属表情包 + +支持下载up主专属表情包,处于任意直播间页面时,下载按钮会在`功能`面板显示,以压缩包形式保存。 \ No newline at end of file diff --git a/registry/lib/components/utils/download-emoticons/index.ts b/registry/lib/components/utils/download-emoticons/index.ts new file mode 100644 index 000000000..5fc1cfa95 --- /dev/null +++ b/registry/lib/components/utils/download-emoticons/index.ts @@ -0,0 +1,16 @@ +import { defineComponentMetadata } from '@/components/define' + +export const component = defineComponentMetadata({ + name: 'downloadEmoticons', + displayName: '下载表情', + tags: [componentsTags.utils, componentsTags.live], + entry: none, + author: { + name: 'Pencilqaq', + link: 'https://github.com/pencilqaq', + }, + widget: { + component: () => import('./Widget.vue').then(m => m.default), + }, + urlInclude: [/^https:\/\/live\.bilibili\.com\/(\d+)/], +})