@book000/pixivts 0.48.58 → 0.48.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/checks.d.ts +19 -0
  2. package/dist/checks.d.ts.map +1 -0
  3. package/dist/checks.js +85 -0
  4. package/dist/checks.js.map +1 -0
  5. package/dist/checks.test.d.ts +2 -0
  6. package/dist/checks.test.d.ts.map +1 -0
  7. package/dist/checks.test.js +306 -0
  8. package/dist/checks.test.js.map +1 -0
  9. package/dist/index.d.ts +30 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +51 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/options.d.ts +248 -0
  14. package/dist/options.d.ts.map +1 -0
  15. package/dist/options.js +154 -0
  16. package/dist/options.js.map +1 -0
  17. package/dist/options.test.d.ts +2 -0
  18. package/dist/options.test.d.ts.map +1 -0
  19. package/dist/options.test.js +117 -0
  20. package/dist/options.test.js.map +1 -0
  21. package/dist/pixiv.d.ts +292 -0
  22. package/dist/pixiv.d.ts.map +1 -0
  23. package/dist/pixiv.js +759 -0
  24. package/dist/pixiv.js.map +1 -0
  25. package/dist/pixiv.test.d.ts +2 -0
  26. package/dist/pixiv.test.d.ts.map +1 -0
  27. package/dist/pixiv.test.js +705 -0
  28. package/dist/pixiv.test.js.map +1 -0
  29. package/dist/saving-responses/index.d.ts +119 -0
  30. package/dist/saving-responses/index.d.ts.map +1 -0
  31. package/dist/saving-responses/index.js +263 -0
  32. package/dist/saving-responses/index.js.map +1 -0
  33. package/dist/saving-responses/index.test.d.ts +2 -0
  34. package/dist/saving-responses/index.test.d.ts.map +1 -0
  35. package/dist/saving-responses/index.test.js +469 -0
  36. package/dist/saving-responses/index.test.js.map +1 -0
  37. package/dist/saving-responses/response-entity.d.ts +16 -0
  38. package/dist/saving-responses/response-entity.d.ts.map +1 -0
  39. package/dist/saving-responses/response-entity.js +101 -0
  40. package/dist/saving-responses/response-entity.js.map +1 -0
  41. package/dist/types/endpoints/v1/illust/bookmark/delete.d.ts +14 -0
  42. package/dist/types/endpoints/v1/illust/bookmark/delete.d.ts.map +1 -0
  43. package/dist/types/endpoints/v1/illust/bookmark/delete.js +3 -0
  44. package/dist/types/endpoints/v1/illust/bookmark/delete.js.map +1 -0
  45. package/dist/types/endpoints/v1/illust/detail.d.ts +25 -0
  46. package/dist/types/endpoints/v1/illust/detail.d.ts.map +1 -0
  47. package/dist/types/endpoints/v1/illust/detail.js +20 -0
  48. package/dist/types/endpoints/v1/illust/detail.js.map +1 -0
  49. package/dist/types/endpoints/v1/illust/ranking.d.ts +51 -0
  50. package/dist/types/endpoints/v1/illust/ranking.d.ts.map +1 -0
  51. package/dist/types/endpoints/v1/illust/ranking.js +40 -0
  52. package/dist/types/endpoints/v1/illust/ranking.js.map +1 -0
  53. package/dist/types/endpoints/v1/illust/recommended.d.ts +85 -0
  54. package/dist/types/endpoints/v1/illust/recommended.d.ts.map +1 -0
  55. package/dist/types/endpoints/v1/illust/recommended.js +37 -0
  56. package/dist/types/endpoints/v1/illust/recommended.js.map +1 -0
  57. package/dist/types/endpoints/v1/illust/series.d.ts +45 -0
  58. package/dist/types/endpoints/v1/illust/series.d.ts.map +1 -0
  59. package/dist/types/endpoints/v1/illust/series.js +31 -0
  60. package/dist/types/endpoints/v1/illust/series.js.map +1 -0
  61. package/dist/types/endpoints/v1/illust/ugoira/metadata.d.ts +25 -0
  62. package/dist/types/endpoints/v1/illust/ugoira/metadata.d.ts.map +1 -0
  63. package/dist/types/endpoints/v1/illust/ugoira/metadata.js +20 -0
  64. package/dist/types/endpoints/v1/illust/ugoira/metadata.js.map +1 -0
  65. package/dist/types/endpoints/v1/manga/recommended.d.ts +72 -0
  66. package/dist/types/endpoints/v1/manga/recommended.d.ts.map +1 -0
  67. package/dist/types/endpoints/v1/manga/recommended.js +33 -0
  68. package/dist/types/endpoints/v1/manga/recommended.js.map +1 -0
  69. package/dist/types/endpoints/v1/novel/bookmark/delete.d.ts +14 -0
  70. package/dist/types/endpoints/v1/novel/bookmark/delete.d.ts.map +1 -0
  71. package/dist/types/endpoints/v1/novel/bookmark/delete.js +3 -0
  72. package/dist/types/endpoints/v1/novel/bookmark/delete.js.map +1 -0
  73. package/dist/types/endpoints/v1/novel/ranking.d.ts +47 -0
  74. package/dist/types/endpoints/v1/novel/ranking.d.ts.map +1 -0
  75. package/dist/types/endpoints/v1/novel/ranking.js +39 -0
  76. package/dist/types/endpoints/v1/novel/ranking.js.map +1 -0
  77. package/dist/types/endpoints/v1/novel/recommended.d.ts +72 -0
  78. package/dist/types/endpoints/v1/novel/recommended.d.ts.map +1 -0
  79. package/dist/types/endpoints/v1/novel/recommended.js +32 -0
  80. package/dist/types/endpoints/v1/novel/recommended.js.map +1 -0
  81. package/dist/types/endpoints/v1/novel/related.d.ts +37 -0
  82. package/dist/types/endpoints/v1/novel/related.d.ts.map +1 -0
  83. package/dist/types/endpoints/v1/novel/related.js +24 -0
  84. package/dist/types/endpoints/v1/novel/related.js.map +1 -0
  85. package/dist/types/endpoints/v1/search/illust.d.ts +90 -0
  86. package/dist/types/endpoints/v1/search/illust.d.ts.map +1 -0
  87. package/dist/types/endpoints/v1/search/illust.js +35 -0
  88. package/dist/types/endpoints/v1/search/illust.js.map +1 -0
  89. package/dist/types/endpoints/v1/search/novel.d.ts +90 -0
  90. package/dist/types/endpoints/v1/search/novel.d.ts.map +1 -0
  91. package/dist/types/endpoints/v1/search/novel.js +35 -0
  92. package/dist/types/endpoints/v1/search/novel.js.map +1 -0
  93. package/dist/types/endpoints/v1/user/bookmarks/illust.d.ts +48 -0
  94. package/dist/types/endpoints/v1/user/bookmarks/illust.d.ts.map +1 -0
  95. package/dist/types/endpoints/v1/user/bookmarks/illust.js +31 -0
  96. package/dist/types/endpoints/v1/user/bookmarks/illust.js.map +1 -0
  97. package/dist/types/endpoints/v1/user/bookmarks/novel.d.ts +44 -0
  98. package/dist/types/endpoints/v1/user/bookmarks/novel.d.ts.map +1 -0
  99. package/dist/types/endpoints/v1/user/bookmarks/novel.js +28 -0
  100. package/dist/types/endpoints/v1/user/bookmarks/novel.js.map +1 -0
  101. package/dist/types/endpoints/v1/user/detail.d.ts +44 -0
  102. package/dist/types/endpoints/v1/user/detail.d.ts.map +1 -0
  103. package/dist/types/endpoints/v1/user/detail.js +26 -0
  104. package/dist/types/endpoints/v1/user/detail.js.map +1 -0
  105. package/dist/types/endpoints/v2/illust/bookmark/add.d.ts +28 -0
  106. package/dist/types/endpoints/v2/illust/bookmark/add.d.ts.map +1 -0
  107. package/dist/types/endpoints/v2/illust/bookmark/add.js +3 -0
  108. package/dist/types/endpoints/v2/illust/bookmark/add.js.map +1 -0
  109. package/dist/types/endpoints/v2/illust/related.d.ts +41 -0
  110. package/dist/types/endpoints/v2/illust/related.d.ts.map +1 -0
  111. package/dist/types/endpoints/v2/illust/related.js +29 -0
  112. package/dist/types/endpoints/v2/illust/related.js.map +1 -0
  113. package/dist/types/endpoints/v2/novel/bookmark/add.d.ts +27 -0
  114. package/dist/types/endpoints/v2/novel/bookmark/add.d.ts.map +1 -0
  115. package/dist/types/endpoints/v2/novel/bookmark/add.js +3 -0
  116. package/dist/types/endpoints/v2/novel/bookmark/add.js.map +1 -0
  117. package/dist/types/endpoints/v2/novel/detail.d.ts +25 -0
  118. package/dist/types/endpoints/v2/novel/detail.d.ts.map +1 -0
  119. package/dist/types/endpoints/v2/novel/detail.js +20 -0
  120. package/dist/types/endpoints/v2/novel/detail.js.map +1 -0
  121. package/dist/types/endpoints/v2/novel/series.d.ts +49 -0
  122. package/dist/types/endpoints/v2/novel/series.d.ts.map +1 -0
  123. package/dist/types/endpoints/v2/novel/series.js +31 -0
  124. package/dist/types/endpoints/v2/novel/series.js.map +1 -0
  125. package/dist/types/endpoints/webview/v2/novel.d.ts +21 -0
  126. package/dist/types/endpoints/webview/v2/novel.d.ts.map +1 -0
  127. package/dist/types/endpoints/webview/v2/novel.js +18 -0
  128. package/dist/types/endpoints/webview/v2/novel.js.map +1 -0
  129. package/dist/types/error-response.d.ts +29 -0
  130. package/dist/types/error-response.d.ts.map +1 -0
  131. package/dist/types/error-response.js +3 -0
  132. package/dist/types/error-response.js.map +1 -0
  133. package/dist/types/pixiv-common.d.ts +96 -0
  134. package/dist/types/pixiv-common.d.ts.map +1 -0
  135. package/dist/types/pixiv-common.js +66 -0
  136. package/dist/types/pixiv-common.js.map +1 -0
  137. package/dist/types/pixiv-illust-series.d.ts +56 -0
  138. package/dist/types/pixiv-illust-series.d.ts.map +1 -0
  139. package/dist/types/pixiv-illust-series.js +26 -0
  140. package/dist/types/pixiv-illust-series.js.map +1 -0
  141. package/dist/types/pixiv-illust.d.ts +167 -0
  142. package/dist/types/pixiv-illust.d.ts.map +1 -0
  143. package/dist/types/pixiv-illust.js +53 -0
  144. package/dist/types/pixiv-illust.js.map +1 -0
  145. package/dist/types/pixiv-novel-series.d.ts +84 -0
  146. package/dist/types/pixiv-novel-series.d.ts.map +1 -0
  147. package/dist/types/pixiv-novel-series.js +43 -0
  148. package/dist/types/pixiv-novel-series.js.map +1 -0
  149. package/dist/types/pixiv-novel.d.ts +122 -0
  150. package/dist/types/pixiv-novel.d.ts.map +1 -0
  151. package/dist/types/pixiv-novel.js +42 -0
  152. package/dist/types/pixiv-novel.js.map +1 -0
  153. package/dist/types/pixiv-ugoira.d.ts +50 -0
  154. package/dist/types/pixiv-ugoira.d.ts.map +1 -0
  155. package/dist/types/pixiv-ugoira.js +34 -0
  156. package/dist/types/pixiv-ugoira.js.map +1 -0
  157. package/dist/types/pixiv-user.d.ts +184 -0
  158. package/dist/types/pixiv-user.d.ts.map +1 -0
  159. package/dist/types/pixiv-user.js +97 -0
  160. package/dist/types/pixiv-user.js.map +1 -0
  161. package/dist/utils.d.ts +3 -0
  162. package/dist/utils.d.ts.map +1 -0
  163. package/dist/utils.js +15 -0
  164. package/dist/utils.js.map +1 -0
  165. package/dist/utils.test.d.ts +2 -0
  166. package/dist/utils.test.d.ts.map +1 -0
  167. package/dist/utils.test.js +15 -0
  168. package/dist/utils.test.js.map +1 -0
  169. package/package.json +3 -3
package/dist/pixiv.js ADDED
@@ -0,0 +1,759 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PixivHttpClient = void 0;
7
+ const node_crypto_1 = __importDefault(require("node:crypto"));
8
+ const qs_1 = __importDefault(require("qs"));
9
+ const options_1 = require("./options");
10
+ const saving_responses_1 = require("./saving-responses");
11
+ function headersToRecord(headers) {
12
+ const result = {};
13
+ for (const [key, value] of headers) {
14
+ result[key] = value;
15
+ }
16
+ return result;
17
+ }
18
+ class PixivHttpClient {
19
+ constructor(baseURL, headers) {
20
+ this.baseURL = baseURL;
21
+ this.defaultHeaders = headers;
22
+ }
23
+ async get(path, options = {}) {
24
+ let url = `${this.baseURL}${path}`;
25
+ if (options.params) {
26
+ const queryString = qs_1.default.stringify(options.params);
27
+ if (queryString)
28
+ url += `?${queryString}`;
29
+ }
30
+ const response = await fetch(url, {
31
+ headers: this.defaultHeaders,
32
+ });
33
+ const contentType = response.headers.get('content-type') ?? '';
34
+ const text = await response.text();
35
+ const data = (contentType.includes('application/json') ? JSON.parse(text) : text);
36
+ return {
37
+ data,
38
+ status: response.status,
39
+ headers: headersToRecord(response.headers),
40
+ requestHeaders: this.defaultHeaders,
41
+ requestBody: null,
42
+ responseUrl: response.url || undefined,
43
+ };
44
+ }
45
+ async post(path, body, options = {}) {
46
+ const url = `${this.baseURL}${path}`;
47
+ const headers = { ...this.defaultHeaders, ...options.headers };
48
+ const response = await fetch(url, {
49
+ method: 'POST',
50
+ headers,
51
+ body,
52
+ });
53
+ const contentType = response.headers.get('content-type') ?? '';
54
+ const text = await response.text();
55
+ const data = (contentType.includes('application/json') ? JSON.parse(text) : text);
56
+ return {
57
+ data,
58
+ status: response.status,
59
+ headers: headersToRecord(response.headers),
60
+ requestHeaders: headers,
61
+ requestBody: body,
62
+ responseUrl: response.url || undefined,
63
+ };
64
+ }
65
+ }
66
+ exports.PixivHttpClient = PixivHttpClient;
67
+ /**
68
+ * pixiv API
69
+ */
70
+ class Pixiv {
71
+ /**
72
+ * コンストラクタ。外部からインスタンス化できないので、of メソッドを使うこと。
73
+ *
74
+ * @param userId ユーザー ID
75
+ * @param accessToken アクセストークン
76
+ * @param refreshToken リフレッシュトークン
77
+ * @param pixivTsOptions Pixivts オプション
78
+ */
79
+ constructor(userId, accessToken, refreshToken, responseDatabase) {
80
+ this.hosts = 'https://app-api.pixiv.net';
81
+ this.userId = userId;
82
+ this.accessToken = accessToken;
83
+ this.refreshToken = refreshToken;
84
+ this.responseDatabase = responseDatabase ?? null;
85
+ this.http = new PixivHttpClient(this.hosts, {
86
+ Host: 'app-api.pixiv.net',
87
+ 'App-OS': 'ios',
88
+ 'App-OS-Version': '14.6',
89
+ 'User-Agent': 'PixivIOSApp/7.13.3 (iOS 14.6; iPhone13,2)',
90
+ 'Accept-Language': 'ja',
91
+ Authorization: `Bearer ${this.accessToken}`,
92
+ });
93
+ }
94
+ /**
95
+ * リフレッシュトークンからインスタンスを生成する。
96
+ *
97
+ * @param refreshToken リフレッシュトークン
98
+ * @returns Pixiv インスタンス
99
+ */
100
+ static async of(refreshToken, pixivTsOptions) {
101
+ // @see https://github.com/upbit/pixivpy/blob/master/pixivpy3/api.py#L120
102
+ // UTCで YYYY-MM-DDTHH:mm:ss+00:00 の形式で現在時刻を取得
103
+ const localTime = new Date().toISOString().replace(/Z$/, '+00:00');
104
+ const headers = {
105
+ 'x-client-time': localTime,
106
+ 'x-client-hash': this.hash(localTime),
107
+ 'app-os': 'ios',
108
+ 'app-os-version': '16.4.1',
109
+ 'user-agent': ' PixivIOSApp/7.16.9 (iOS 16.4.1; iPad13,4)',
110
+ header: 'application/x-www-form-urlencoded',
111
+ };
112
+ const authUrl = 'https://oauth.secure.pixiv.net/auth/token';
113
+ const data = qs_1.default.stringify({
114
+ client_id: this.clientId,
115
+ client_secret: this.clientSecret,
116
+ get_secure_url: 1,
117
+ grant_type: 'refresh_token',
118
+ refresh_token: refreshToken,
119
+ });
120
+ const response = await fetch(authUrl, {
121
+ method: 'POST',
122
+ headers: {
123
+ ...headers,
124
+ 'Content-Type': 'application/x-www-form-urlencoded',
125
+ },
126
+ body: data,
127
+ });
128
+ if (response.status !== 200) {
129
+ throw new Error('Failed to refresh token');
130
+ }
131
+ const responseData = (await response.json());
132
+ const options = {
133
+ userId: responseData.user.id,
134
+ accessToken: responseData.response.access_token,
135
+ refreshToken: responseData.response.refresh_token,
136
+ };
137
+ const responseDatabase = pixivTsOptions?.debugOptions?.outputResponse
138
+ ?.enable
139
+ ? new saving_responses_1.ResponseDatabase(pixivTsOptions.debugOptions.outputResponse.db)
140
+ : null;
141
+ if (responseDatabase) {
142
+ await responseDatabase.init();
143
+ await responseDatabase.migrate();
144
+ await responseDatabase.sync();
145
+ }
146
+ return new Pixiv(options.userId, options.accessToken, options.refreshToken, responseDatabase);
147
+ }
148
+ /**
149
+ * 画像取得用の `Response` を返す。
150
+ *
151
+ * 戻り値はストリームそのものではなく `Response` オブジェクトであり、
152
+ * ストリームとして読み取る場合は `response.body` を参照する。
153
+ * 必要に応じて `arrayBuffer()` などの `Response` の API も利用できる。
154
+ *
155
+ * @param url 画像 URL
156
+ * @returns 画像取得結果の `Response`
157
+ */
158
+ static async getImageStream(url) {
159
+ return fetch(url, {
160
+ headers: {
161
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36',
162
+ Referer: 'https://www.pixiv.net/',
163
+ },
164
+ });
165
+ }
166
+ // ---------- イラスト ---------- //
167
+ /**
168
+ * イラストの詳細情報を取得する。
169
+ *
170
+ * @param options オプション
171
+ * @returns レスポンス
172
+ */
173
+ async illustDetail(options) {
174
+ const parameters = {
175
+ ...this.convertCamelToSnake(options),
176
+ illust_id: options.illustId,
177
+ };
178
+ return this.request({
179
+ method: 'GET',
180
+ path: '/v1/illust/detail',
181
+ params: parameters,
182
+ });
183
+ }
184
+ /**
185
+ * イラストの関連イラストを取得する。
186
+ *
187
+ * @param options オプション
188
+ * @returns レスポンス
189
+ */
190
+ async illustRelated(options) {
191
+ this.checkRequiredOptions(options, ['illustId']);
192
+ const parameters = {
193
+ ...this.convertCamelToSnake(options),
194
+ illust_id: options.illustId,
195
+ seed_illust_ids: options.seedIllustIds,
196
+ viewed: options.viewed,
197
+ offset: options.offset,
198
+ };
199
+ return this.request({
200
+ method: 'GET',
201
+ path: '/v2/illust/related',
202
+ params: parameters,
203
+ });
204
+ }
205
+ /**
206
+ * イラストを検索する。
207
+ *
208
+ * @param options オプション
209
+ * @returns レスポンス
210
+ */
211
+ async searchIllust(options) {
212
+ this.checkRequiredOptions(options, ['word']);
213
+ const parameters = {
214
+ ...this.convertCamelToSnake(options),
215
+ word: options.word, // required
216
+ search_target: options.searchTarget ?? options_1.SearchTarget.PARTIAL_MATCH_FOR_TAGS,
217
+ sort: options.sort ?? options_1.SearchSort.DATE_DESC,
218
+ start_date: options.startDate,
219
+ end_date: options.endDate,
220
+ filter: options.filter ?? options_1.OSFilter.FOR_IOS,
221
+ offset: options.offset,
222
+ merge_plain_keyword_results: options.mergePlainKeywordResults ?? true,
223
+ include_translated_tag_results: options.includeTranslatedTagResults ?? true,
224
+ };
225
+ return this.request({
226
+ method: 'GET',
227
+ path: '/v1/search/illust',
228
+ params: parameters,
229
+ });
230
+ }
231
+ /**
232
+ * イラストランキングを取得する。
233
+ *
234
+ * @param options オプション
235
+ * @returns レスポンス
236
+ */
237
+ async illustRanking(options = {}) {
238
+ const parameters = {
239
+ ...this.convertCamelToSnake(options),
240
+ mode: options.mode ?? options_1.RankingMode.DAY,
241
+ filter: options.filter ?? options_1.OSFilter.FOR_IOS,
242
+ date: options.date ?? undefined,
243
+ offset: options.offset ?? undefined,
244
+ };
245
+ return this.request({
246
+ method: 'GET',
247
+ path: '/v1/illust/ranking',
248
+ params: parameters,
249
+ });
250
+ }
251
+ /**
252
+ * おすすめイラストを取得する。
253
+ *
254
+ * @param options オプション
255
+ * @returns レスポンス
256
+ */
257
+ async illustRecommended(options = {}) {
258
+ const parameters = {
259
+ ...this.convertCamelToSnake(options),
260
+ filter: options.filter ?? options_1.OSFilter.FOR_IOS,
261
+ include_ranking_illusts: options.includeRankingIllusts ?? true,
262
+ min_bookmark_id_for_recent_illust: options.minBookmarkIdForRecentIllust ?? undefined,
263
+ max_bookmark_id_for_recommend: options.maxBookmarkIdForRecommend ?? undefined,
264
+ offset: options.offset ?? undefined,
265
+ include_privacy_policy: options.includePrivacyPolicy ?? true,
266
+ };
267
+ return this.request({
268
+ method: 'GET',
269
+ path: '/v1/illust/recommended',
270
+ params: parameters,
271
+ });
272
+ }
273
+ /**
274
+ * イラストシリーズの詳細情報を取得する。
275
+ *
276
+ * @param options オプション
277
+ * @returns レスポンス
278
+ */
279
+ async illustSeries(options) {
280
+ this.checkRequiredOptions(options, ['illustSeriesId']);
281
+ const parameters = {
282
+ ...this.convertCamelToSnake(options),
283
+ illust_series_id: options.illustSeriesId,
284
+ filter: options.filter ?? options_1.OSFilter.FOR_IOS,
285
+ // offset: options.offset,
286
+ };
287
+ return this.request({
288
+ method: 'GET',
289
+ path: '/v1/illust/series',
290
+ params: parameters,
291
+ });
292
+ }
293
+ /**
294
+ * イラストをブックマークする。
295
+ *
296
+ * @param options オプション
297
+ * @returns レスポンス
298
+ */
299
+ async illustBookmarkAdd(options) {
300
+ this.checkRequiredOptions(options, ['illustId']);
301
+ const data = {
302
+ ...this.convertCamelToSnake(options),
303
+ illust_id: options.illustId,
304
+ restrict: options.restrict ?? options_1.BookmarkRestrict.PUBLIC,
305
+ tags: options.tags ?? [],
306
+ };
307
+ return this.request({
308
+ method: 'POST',
309
+ path: '/v2/illust/bookmark/add',
310
+ data,
311
+ });
312
+ }
313
+ /**
314
+ * イラストのブックマークを削除する。
315
+ *
316
+ * @param options オプション
317
+ * @returns レスポンス
318
+ */
319
+ async illustBookmarkDelete(options) {
320
+ this.checkRequiredOptions(options, ['illustId']);
321
+ const data = {
322
+ ...this.convertCamelToSnake(options),
323
+ illust_id: options.illustId,
324
+ };
325
+ return this.request({
326
+ method: 'POST',
327
+ path: '/v1/illust/bookmark/delete',
328
+ data,
329
+ });
330
+ }
331
+ // ---------- マンガ ---------- //
332
+ async mangaRecommended(options = {}) {
333
+ const parameters = {
334
+ ...this.convertCamelToSnake(options),
335
+ filter: options.filter ?? options_1.OSFilter.FOR_IOS,
336
+ include_ranking_illusts: options.includeRankingIllusts ?? true,
337
+ max_bookmark_id: options.maxBookmarkId ?? undefined,
338
+ offset: options.offset ?? undefined,
339
+ include_privacy_policy: options.includePrivacyPolicy ?? true,
340
+ };
341
+ return this.request({
342
+ method: 'GET',
343
+ path: '/v1/manga/recommended',
344
+ params: parameters,
345
+ });
346
+ }
347
+ // ---------- うごイラ ---------- //
348
+ /**
349
+ * うごイラの詳細情報を取得する。
350
+ *
351
+ * @param options オプション
352
+ * @returns レスポンス
353
+ */
354
+ async ugoiraMetadata(options) {
355
+ const parameters = {
356
+ ...this.convertCamelToSnake(options),
357
+ illust_id: options.illustId,
358
+ };
359
+ return this.request({
360
+ method: 'GET',
361
+ path: '/v1/ugoira/metadata',
362
+ params: parameters,
363
+ });
364
+ }
365
+ // ---------- 小説 ---------- //
366
+ /**
367
+ * 小説の詳細情報を取得する。
368
+ *
369
+ * @param options オプション
370
+ * @returns レスポンス
371
+ */
372
+ async novelDetail(options) {
373
+ this.checkRequiredOptions(options, ['novelId']);
374
+ const parameters = {
375
+ ...this.convertCamelToSnake(options),
376
+ novel_id: options.novelId,
377
+ };
378
+ return this.request({
379
+ method: 'GET',
380
+ path: '/v2/novel/detail',
381
+ params: parameters,
382
+ });
383
+ }
384
+ /**
385
+ * 小説の本文を取得する。
386
+ *
387
+ * @param options オプション
388
+ * @returns レスポンス
389
+ */
390
+ async novelText(options) {
391
+ this.checkRequiredOptions(options, ['id']);
392
+ const parameters = {
393
+ ...this.convertCamelToSnake(options),
394
+ id: options.id,
395
+ };
396
+ return await this.request({
397
+ method: 'GET',
398
+ path: '/webview/v2/novel',
399
+ params: parameters,
400
+ });
401
+ }
402
+ /**
403
+ * 小説の関連小説を取得する。
404
+ *
405
+ * @param options オプション
406
+ * @returns レスポンス
407
+ */
408
+ async novelRelated(options) {
409
+ this.checkRequiredOptions(options, ['novelId']);
410
+ const parameters = {
411
+ ...this.convertCamelToSnake(options),
412
+ novel_id: options.novelId,
413
+ seed_novel_ids: options.seedNovelIds,
414
+ viewed: options.viewed,
415
+ };
416
+ return this.request({
417
+ method: 'GET',
418
+ path: '/v1/novel/related',
419
+ params: parameters,
420
+ });
421
+ }
422
+ /**
423
+ * 小説を検索する。
424
+ *
425
+ * @param options オプション
426
+ * @returns レスポンス
427
+ */
428
+ async searchNovel(options) {
429
+ this.checkRequiredOptions(options, ['word']);
430
+ const parameters = {
431
+ ...this.convertCamelToSnake(options),
432
+ word: options.word, // required
433
+ search_target: options.searchTarget ?? options_1.SearchTarget.PARTIAL_MATCH_FOR_TAGS,
434
+ sort: options.sort ?? options_1.SearchSort.DATE_DESC,
435
+ startDate: options.startDate,
436
+ endDate: options.endDate,
437
+ filter: options.filter ?? options_1.OSFilter.FOR_IOS,
438
+ offset: options.offset,
439
+ merge_plain_keyword_results: options.mergePlainKeywordResults ?? true,
440
+ include_translated_tag_results: options.includeTranslatedTagResults ?? true,
441
+ };
442
+ return this.request({
443
+ method: 'GET',
444
+ path: '/v1/search/novel',
445
+ params: parameters,
446
+ });
447
+ }
448
+ /**
449
+ * 小説ランキングを取得する。
450
+ */
451
+ async novelRanking(options = {}) {
452
+ const parameters = {
453
+ ...this.convertCamelToSnake(options),
454
+ mode: options.mode ?? options_1.RankingMode.DAY,
455
+ date: options.date ?? undefined,
456
+ offset: options.offset ?? undefined,
457
+ };
458
+ return this.request({
459
+ method: 'GET',
460
+ path: '/v1/novel/ranking',
461
+ params: parameters,
462
+ });
463
+ }
464
+ /**
465
+ * おすすめ小説を取得する。
466
+ *
467
+ * @param options オプション
468
+ * @returns レスポンス
469
+ */
470
+ async novelRecommended(options = {}) {
471
+ const parameters = {
472
+ ...this.convertCamelToSnake(options),
473
+ // filter: options.filter ?? 'for_ios',
474
+ include_ranking_novels: options.includeRankingNovels ?? true,
475
+ already_recommended: options.alreadyRecommended
476
+ ? options.alreadyRecommended.join(',')
477
+ : undefined,
478
+ max_bookmark_id_for_recommend: options.maxBookmarkIdForRecommend ?? undefined,
479
+ offset: options.offset ?? undefined,
480
+ include_privacy_policy: options.includePrivacyPolicy ?? true,
481
+ };
482
+ return this.request({
483
+ method: 'GET',
484
+ path: '/v1/novel/recommended',
485
+ params: parameters,
486
+ });
487
+ }
488
+ /**
489
+ * 小説シリーズの詳細情報を取得する。
490
+ *
491
+ * @param options オプション
492
+ * @returns レスポンス
493
+ */
494
+ async novelSeries(options) {
495
+ this.checkRequiredOptions(options, ['seriesId']);
496
+ const parameters = {
497
+ ...this.convertCamelToSnake(options),
498
+ series_id: options.seriesId,
499
+ // filter: options.filter ?? 'for_ios',
500
+ last_order: options.lastOrder ?? undefined,
501
+ };
502
+ return this.request({
503
+ method: 'GET',
504
+ path: '/v2/novel/series',
505
+ params: parameters,
506
+ });
507
+ }
508
+ /**
509
+ * 小説をブックマークする。
510
+ *
511
+ * @param options オプション
512
+ * @returns レスポンス
513
+ */
514
+ async novelBookmarkAdd(options) {
515
+ this.checkRequiredOptions(options, ['novelId']);
516
+ const data = {
517
+ ...this.convertCamelToSnake(options),
518
+ novel_id: options.novelId,
519
+ restrict: options.restrict ?? options_1.BookmarkRestrict.PUBLIC,
520
+ tags: options.tags ?? [],
521
+ };
522
+ return this.request({
523
+ method: 'POST',
524
+ path: '/v2/novel/bookmark/add',
525
+ data,
526
+ });
527
+ }
528
+ /**
529
+ * 小説のブックマークを削除する。
530
+ *
531
+ * @param options オプション
532
+ * @returns レスポンス
533
+ */
534
+ async novelBookmarkDelete(options) {
535
+ this.checkRequiredOptions(options, ['novelId']);
536
+ const data = {
537
+ ...this.convertCamelToSnake(options),
538
+ novel_id: options.novelId,
539
+ };
540
+ return this.request({
541
+ method: 'POST',
542
+ path: '/v1/novel/bookmark/delete',
543
+ data,
544
+ });
545
+ }
546
+ // ---------- ユーザ ---------- //
547
+ /**
548
+ * ユーザの詳細情報を取得する。
549
+ *
550
+ * @param options オプション
551
+ * @returns レスポンス
552
+ */
553
+ async userDetail(options) {
554
+ this.checkRequiredOptions(options, ['userId']);
555
+ const parameters = {
556
+ ...this.convertCamelToSnake(options),
557
+ user_id: options.userId,
558
+ filter: options.filter ?? options_1.OSFilter.FOR_IOS,
559
+ };
560
+ return this.request({
561
+ method: 'GET',
562
+ path: '/v1/user/detail',
563
+ params: parameters,
564
+ });
565
+ }
566
+ /**
567
+ * ユーザのイラストブックマークを取得する。
568
+ *
569
+ * @param options オプション
570
+ * @returns レスポンス
571
+ */
572
+ async userBookmarksIllust(options) {
573
+ this.checkRequiredOptions(options, ['userId']);
574
+ const parameters = {
575
+ ...this.convertCamelToSnake(options),
576
+ user_id: options.userId,
577
+ restrict: options.restrict ?? options_1.BookmarkRestrict.PUBLIC,
578
+ filter: options.filter ?? options_1.OSFilter.FOR_IOS,
579
+ max_bookmark_id: options.maxBookmarkId ?? undefined,
580
+ tag: options.tag ?? undefined,
581
+ };
582
+ return this.request({
583
+ method: 'GET',
584
+ path: '/v1/user/bookmarks/illust',
585
+ params: parameters,
586
+ });
587
+ }
588
+ /**
589
+ * ユーザの小説ブックマークを取得する。
590
+ *
591
+ * @param options オプション
592
+ * @returns レスポンス
593
+ */
594
+ async userBookmarksNovel(options) {
595
+ this.checkRequiredOptions(options, ['userId']);
596
+ const parameters = {
597
+ ...this.convertCamelToSnake(options),
598
+ user_id: options.userId,
599
+ restrict: options.restrict ?? options_1.BookmarkRestrict.PUBLIC,
600
+ max_bookmark_id: options.maxBookmarkId ?? undefined,
601
+ tag: options.tag ?? undefined,
602
+ };
603
+ return this.request({
604
+ method: 'GET',
605
+ path: '/v1/user/bookmarks/novel',
606
+ params: parameters,
607
+ });
608
+ }
609
+ // ---------- その他 ---------- //
610
+ /**
611
+ * 接続を閉じる。
612
+ */
613
+ async close() {
614
+ if (this.responseDatabase) {
615
+ await this.responseDatabase.close();
616
+ }
617
+ }
618
+ // ---------- ユーティリティ ---------- //
619
+ /**
620
+ * クエリストリングをパースする。
621
+ *
622
+ * @param url URL
623
+ * @returns パースしたクエリストリングオブジェクト
624
+ */
625
+ static parseQueryString(url) {
626
+ let query = url;
627
+ if (url.includes('?')) {
628
+ query = url.split('?')[1];
629
+ }
630
+ return qs_1.default.parse(query);
631
+ }
632
+ /**
633
+ * レスポンスがエラーかどうかを判定する。
634
+ *
635
+ * @param response Axios レスポンス
636
+ * @returns エラーかどうか
637
+ */
638
+ static isError(response) {
639
+ return (
640
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
641
+ response.error !== undefined &&
642
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
643
+ response.error.user_message !== undefined &&
644
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
645
+ response.error.message !== undefined &&
646
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
647
+ response.error.reason !== undefined);
648
+ }
649
+ /**
650
+ * MD5ハッシュを生成する。
651
+ *
652
+ * @param str 文字列
653
+ * @returns ハッシュ
654
+ */
655
+ static hash(string) {
656
+ const hash = node_crypto_1.default.createHash('md5');
657
+ return hash.update(string + this.hashSecret).digest('hex');
658
+ }
659
+ /**
660
+ * リクエストを送信する。
661
+ *
662
+ * ジェネリクスの順番は、T: リクエスト、U: レスポンス。
663
+ *
664
+ * @param options オプション
665
+ * @returns レスポンス
666
+ */
667
+ async request(options) {
668
+ if (options.method === 'GET') {
669
+ return await this.saveResponse(options, await this.http.get(options.path, {
670
+ params: options.params,
671
+ }));
672
+ }
673
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
674
+ if (options.method === 'POST') {
675
+ return await this.saveResponse(options, await this.http.post(options.path, qs_1.default.stringify(options.data), {
676
+ headers: {
677
+ 'Content-Type': 'application/x-www-form-urlencoded',
678
+ },
679
+ }));
680
+ }
681
+ throw new Error('Invalid method');
682
+ }
683
+ async saveResponse(request, response) {
684
+ if (this.responseDatabase === null) {
685
+ return response;
686
+ }
687
+ const method = request.method;
688
+ const path = request.path;
689
+ const rawUrl = [
690
+ this.hosts,
691
+ path,
692
+ method === 'GET'
693
+ ? qs_1.default.stringify(request.params, { addQueryPrefix: true })
694
+ : '',
695
+ ].join('');
696
+ const url = response.responseUrl ?? rawUrl;
697
+ const responseType = this.isJSON(response.data) ? 'JSON' : 'TEXT';
698
+ const responseBody = responseType === 'JSON'
699
+ ? JSON.stringify(response.data)
700
+ : response.data;
701
+ await this.responseDatabase.addResponse({
702
+ method,
703
+ endpoint: path,
704
+ url,
705
+ requestHeaders: JSON.stringify(response.requestHeaders),
706
+ requestBody: response.requestBody,
707
+ responseType,
708
+ statusCode: response.status,
709
+ responseHeaders: JSON.stringify(response.headers),
710
+ responseBody,
711
+ });
712
+ return response;
713
+ }
714
+ isJSON(value) {
715
+ if (typeof value === 'object') {
716
+ return true;
717
+ }
718
+ try {
719
+ JSON.parse(value);
720
+ return true;
721
+ }
722
+ catch {
723
+ return false;
724
+ }
725
+ }
726
+ /**
727
+ * 必須のオプションが含まれているかどうかをチェックする。
728
+ *
729
+ * @param options オプション
730
+ * @param required 必須のオプションキー
731
+ * @throws 必須のオプションが含まれていない場合
732
+ */
733
+ checkRequiredOptions(options, required) {
734
+ for (const key of required) {
735
+ if (options[key] === undefined) {
736
+ throw new Error(`Missing required option: ${key}`);
737
+ }
738
+ }
739
+ }
740
+ /**
741
+ * キャメルケースのオブジェクトキーをスネークケースなオブジェクトキーに変換する。
742
+ *
743
+ * @param object オブジェクト
744
+ * @returns 変換後のオブジェクト
745
+ */
746
+ convertCamelToSnake(object) {
747
+ const result = {};
748
+ for (const key of Object.keys(object)) {
749
+ const snakeKey = key.replaceAll(/([A-Z])/g, (m) => `_${m[0].toLowerCase()}`);
750
+ result[snakeKey] = object[key];
751
+ }
752
+ return result;
753
+ }
754
+ }
755
+ Pixiv.clientId = 'MOBrBDS8blbauoSck0ZfDbtuzpyT';
756
+ Pixiv.clientSecret = 'lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj';
757
+ Pixiv.hashSecret = '28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c';
758
+ exports.default = Pixiv;
759
+ //# sourceMappingURL=pixiv.js.map