From cbaf1698ded29a88c552ac533a0c02dde7833467 Mon Sep 17 00:00:00 2001 From: hker9527 Date: Sat, 11 Nov 2023 20:11:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E3=81=86=E3=81=94=E3=82=A4=E3=83=A9?= =?UTF-8?q?=E3=81=AE=E3=82=B5=E3=83=9D=E3=83=BC=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.ts | 2 + src/options.ts | 6 ++ src/pixiv.test.ts | 12 +++ src/pixiv.ts | 27 +++++++ .../endpoints/v1/illust/ugoira/metadata.ts | 41 ++++++++++ src/types/pixiv-ugoira.ts | 74 +++++++++++++++++++ 6 files changed, 162 insertions(+) create mode 100644 src/types/endpoints/v1/illust/ugoira/metadata.ts create mode 100644 src/types/pixiv-ugoira.ts diff --git a/src/index.ts b/src/index.ts index da850ef1..e5991966 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ export * from './types/endpoints/v2/novel/bookmark/add' export * from './types/endpoints/v1/illust/detail' export * from './types/endpoints/v1/illust/recommended' export * from './types/endpoints/v1/illust/series' +export * from './types/endpoints/v1/illust/ugoira/metadata' export * from './types/endpoints/v1/manga/recommended' export * from './types/endpoints/v1/novel/recommended' export * from './types/endpoints/v1/novel/text' @@ -19,6 +20,7 @@ export * from './types/error-response' export * from './types/pixiv-common' export * from './types/pixiv-illust-series' export * from './types/pixiv-illust' +export * from './types/pixiv-ugoira' export * from './types/pixiv-novel-series' export * from './types/pixiv-novel' export * from './types/pixiv-user' diff --git a/src/options.ts b/src/options.ts index 513c58ec..ab585690 100644 --- a/src/options.ts +++ b/src/options.ts @@ -17,6 +17,7 @@ import { PostV2NovelBookmarkAddRequest } from './types/endpoints/v2/novel/bookma import { PostV1NovelBookmarkDeleteRequest } from './types/endpoints/v1/novel/bookmark/delete' import { GetV1UserBookmarksIllustRequest } from './types/endpoints/v1/user/bookmarks/illust' import { GetV1UserBookmarksNovelRequest } from './types/endpoints/v1/user/bookmarks/novel' +import { GetV1IllustUgoiraMetadataRequest } from './types/endpoints/v1/illust/ugoira/metadata' /** * 検索対象 @@ -189,6 +190,11 @@ export type MangaRecommendedOptions = SnakeToCamel< Partial > +/** + * うごイラ詳細取得オプション + */ +export type UgoiraDetailOptions = SnakeToCamel; + /** * 小説詳細取得オプション */ diff --git a/src/pixiv.test.ts b/src/pixiv.test.ts index 9165cc3c..4df8bb8b 100644 --- a/src/pixiv.test.ts +++ b/src/pixiv.test.ts @@ -159,6 +159,18 @@ describe('pixiv', () => { ).toMatchSnapshot() }) + it('ugoiraDetail:83638393[ugoira]', async () => { + const ugoiraDetail = await pixiv.ugoiraMetadata({ + illustId: 83_638_393 + }) + expect(ugoiraDetail.status).toBe(200) + // "medium": "https://i.pximg.net/img-zip-ugoira/img/2014/06/28/12/42/39/44360221_ugoira600x600.zip". + expect(ugoiraDetail.data.ugoira_metadata.zip_urls.medium).toMatch( + /^https:\/\/i\.pximg\.net\/img-zip-ugoira\/img\/.+_ugoira600x600\.zip$/ + ) + expect(ugoiraDetail.data.ugoira_metadata.frames).toHaveLength(96) + }) + it('searchIllust', async () => { const searchIllust = await pixiv.searchIllust({ word: 'ホロライブ', diff --git a/src/pixiv.ts b/src/pixiv.ts index 48a45e3d..b3912875 100644 --- a/src/pixiv.ts +++ b/src/pixiv.ts @@ -24,6 +24,7 @@ import { NovelBookmarkDeleteOptions, UserBookmarksIllustOptions, UserBookmarksNovelOptions, + UgoiraDetailOptions, } from './options' import { PixivApiError } from './types/error-response' import { @@ -94,6 +95,10 @@ import { GetV1UserBookmarksIllustRequest, GetV1UserBookmarksIllustResponse, } from './types/endpoints/v1/user/bookmarks/illust' +import { + GetV1IllustUgoiraMetadataRequest, + GetV1IllustUgoiraMetadataResponse, +} from './types/endpoints/v1/illust/ugoira/metadata' interface GetRequestOptions { method: 'GET' @@ -384,6 +389,28 @@ export default class Pixiv { }) } + // ---------- うごイラ ---------- // + + /** + * うごイラの詳細情報を取得する。 + * + * @param options オプション + * @returns レスポンス + */ + public async ugoiraMetadata(options: UgoiraDetailOptions) { + type RequestType = GetV1IllustUgoiraMetadataRequest + const parameters: RequestType = { + ...this.convertSnakeToCamel(options), + illust_id: options.illustId, + } + + return this.request({ + method: 'GET', + path: '/v1/ugoira/metadata', + params: parameters, + }) + } + // ---------- 小説 ---------- // /** diff --git a/src/types/endpoints/v1/illust/ugoira/metadata.ts b/src/types/endpoints/v1/illust/ugoira/metadata.ts new file mode 100644 index 00000000..d01a1c2f --- /dev/null +++ b/src/types/endpoints/v1/illust/ugoira/metadata.ts @@ -0,0 +1,41 @@ +import { BaseMultipleCheck, CheckFunctions } from '../../../../../checks' +import { PixivUgoiraItem, PixivUgoiraItemCheck } from '../../../../pixiv-ugoira' + +/** + * GET /v1/illust/ugoira/detail のリクエスト + */ +export interface GetV1IllustUgoiraMetadataRequest { + /** + * イラストID + */ + illust_id: number +} + +/** + * GET /v1/illust/ugoira/detail のレスポンス + */ +export interface GetV1IllustUgoiraMetadataResponse { + /** + * うごイラの詳細情報 + */ + ugoira_metadata: PixivUgoiraItem +} + +export class GetV1IllustUgoiraMetadataCheck extends BaseMultipleCheck< + GetV1IllustUgoiraMetadataRequest, + GetV1IllustUgoiraMetadataResponse +> { + requestChecks(): CheckFunctions { + return { + illust_id: (data) => typeof data.illust_id === 'number', + } + } + + responseChecks(): CheckFunctions { + return { + ugoira_metadata: (data) => + typeof data.ugoira_metadata === 'object' && + new PixivUgoiraItemCheck().throwIfFailed(data.ugoira_metadata), + } + } +} \ No newline at end of file diff --git a/src/types/pixiv-ugoira.ts b/src/types/pixiv-ugoira.ts new file mode 100644 index 00000000..f4d3e008 --- /dev/null +++ b/src/types/pixiv-ugoira.ts @@ -0,0 +1,74 @@ +import { BaseSimpleCheck, CheckFunctions } from "src/checks" + +/** + * 圧縮されたフレームのURL + * + * ここのURLも {@link ImageUrls} と同様に、適切なリファラを付与する必要がある + */ +export interface ZipUrls { + /** + * 長辺が最大 600px + * + * "600x600" を "1920x1080" に変換したらオリジナル画像が得られる? + */ + medium: string +} + +/** + * フレーム情報 + */ +export interface Frames { + /** + * フレームのファイル名 + */ + file: string + /** + * フレームの表示時間(ms) + */ + delay: number +} + +/** + * pixiv うごイラアイテム + */ +export interface PixivUgoiraItem { + /** + * 圧縮されたフレームのURL + */ + zip_urls: ZipUrls + /** + * フレーム情報 + */ + frames: Frames[] +} + +export class ZipUrlsCheck extends BaseSimpleCheck { + checks(): CheckFunctions { + return { + medium: (data) => typeof data.medium === 'string', + } + } +} + +export class FramesCheck extends BaseSimpleCheck { + checks(): CheckFunctions { + return { + file: (data) => typeof data.file === 'string', + delay: (data) => typeof data.delay === 'number', + } + } +} + +export class PixivUgoiraItemCheck extends BaseSimpleCheck { + checks(): CheckFunctions { + return { + zip_urls: (data) => + typeof data.zip_urls === 'object' && + new ZipUrlsCheck().throwIfFailed(data.zip_urls), + frames: (data) => + typeof data.frames === 'object' && + Array.isArray(data.frames) && + data.frames.every((frame) => new FramesCheck().throwIfFailed(frame)), + } + } +} \ No newline at end of file