pixivflow 3.4.3 → 3.6.0
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.
- package/dist/config/loader.js +4 -0
- package/dist/config/types.d.ts +7 -0
- package/dist/config/validation.js +4 -0
- package/dist/delivery/DeliveryService.js +2 -0
- package/dist/delivery/HttpMultipartDelivery.js +17 -0
- package/dist/delivery/types.d.ts +13 -0
- package/dist/domain/media/MediaAsset.d.ts +18 -2
- package/dist/domain/media/MediaAsset.js +30 -3
- package/dist/download/NovelDownloader.js +1 -0
- package/dist/package.json +1 -1
- package/dist/version.js +1 -1
- package/dist/webui/package.json +1 -1
- package/package.json +1 -1
package/dist/config/loader.js
CHANGED
|
@@ -18,6 +18,7 @@ const path_resolution_1 = require("./path-resolution");
|
|
|
18
18
|
const environment_1 = require("./environment");
|
|
19
19
|
const placeholders_1 = require("./placeholders");
|
|
20
20
|
const validation_1 = require("./validation");
|
|
21
|
+
const MediaAsset_1 = require("../domain/media/MediaAsset");
|
|
21
22
|
/**
|
|
22
23
|
* Get the resolved configuration file path
|
|
23
24
|
* If no path is specified, automatically selects the first available config file
|
|
@@ -128,6 +129,9 @@ function loadConfig(configPath, skipValidation = false, resolveDynamicPlaceholde
|
|
|
128
129
|
// Apply defaults - use config file directory as base path to ensure paths are resolved correctly
|
|
129
130
|
// This prevents the app from accidentally using paths from the development machine
|
|
130
131
|
const config = (0, path_resolution_1.applyDefaults)(parsed, configDir);
|
|
132
|
+
// Media-asset id namespace (download.assetNamespace, default 'pixiv').
|
|
133
|
+
// Applied once per config load so every mediaAssetId() call site picks it up.
|
|
134
|
+
(0, MediaAsset_1.configureMediaAssetNamespace)(config.download?.assetNamespace);
|
|
131
135
|
// Set log level
|
|
132
136
|
if (config.logLevel) {
|
|
133
137
|
logger_1.logger.setLevel(config.logLevel);
|
package/dist/config/types.d.ts
CHANGED
|
@@ -959,6 +959,13 @@ export interface StandaloneConfig {
|
|
|
959
959
|
unknown?: 'skip' | 'keep';
|
|
960
960
|
probeFailed?: 'skip' | 'keep';
|
|
961
961
|
};
|
|
962
|
+
/**
|
|
963
|
+
* Media-asset id namespace prefix (`<namespace>:<workId>:<kind>[:<sourceId>]`).
|
|
964
|
+
* Default: 'pixiv' (production unchanged; `pixiv:` ids stay valid forever).
|
|
965
|
+
* A second content source may adopt its own namespace — consumers treat the
|
|
966
|
+
* prefix as opaque. Lowercase letters/digits/dashes, max 32 chars.
|
|
967
|
+
*/
|
|
968
|
+
assetNamespace?: string;
|
|
962
969
|
};
|
|
963
970
|
}
|
|
964
971
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -715,6 +715,10 @@ function validateConfig(config, location, databasePath) {
|
|
|
715
715
|
config.download.novelCover.unknown !== 'keep') {
|
|
716
716
|
errors.push(`download.novelCover.unknown: Must be "skip" or "keep" (got ${String(config.download.novelCover.unknown)})`);
|
|
717
717
|
}
|
|
718
|
+
if (config.download.assetNamespace !== undefined &&
|
|
719
|
+
!/^[a-z0-9][a-z0-9-]{0,31}$/.test(String(config.download.assetNamespace))) {
|
|
720
|
+
errors.push(`download.assetNamespace: Must be lowercase letters/digits/dashes, 1-32 chars (got ${String(config.download.assetNamespace)})`);
|
|
721
|
+
}
|
|
718
722
|
if (config.download.novelCover?.probeFailed !== undefined &&
|
|
719
723
|
config.download.novelCover.probeFailed !== 'skip' &&
|
|
720
724
|
config.download.novelCover.probeFailed !== 'keep') {
|
|
@@ -148,6 +148,7 @@ class DeliveryService {
|
|
|
148
148
|
pixivId: artifact.pixivId,
|
|
149
149
|
type: artifact.type,
|
|
150
150
|
title: artifact.title,
|
|
151
|
+
...(artifact.seriesTitle !== undefined ? { seriesTitle: artifact.seriesTitle } : {}),
|
|
151
152
|
...(artifact.spoiler !== undefined ? { spoiler: artifact.spoiler } : {}),
|
|
152
153
|
},
|
|
153
154
|
files,
|
|
@@ -288,6 +289,7 @@ class DeliveryService {
|
|
|
288
289
|
return {
|
|
289
290
|
deliveryTarget,
|
|
290
291
|
title: artifact.title,
|
|
292
|
+
...(artifact.seriesTitle !== undefined ? { seriesTitle: artifact.seriesTitle } : {}),
|
|
291
293
|
pixivId: artifact.pixivId,
|
|
292
294
|
type: artifact.type,
|
|
293
295
|
targetId: target.id,
|
|
@@ -425,9 +425,26 @@ function buildTemplateVariables(request) {
|
|
|
425
425
|
return 'R18G';
|
|
426
426
|
return `XRestrict${xRestrict}`;
|
|
427
427
|
})();
|
|
428
|
+
// A series novel's `seriesTitle` names its parent series while `title` is the
|
|
429
|
+
// chapter title. Compose the card headline `《系列名》 章节名` when the work
|
|
430
|
+
// belongs to a series; fall back to the bare title otherwise (illustrations
|
|
431
|
+
// and non-series novels never carry a seriesTitle).
|
|
432
|
+
const displayTitle = c.type === 'novel' && c.seriesTitle
|
|
433
|
+
? `《${c.seriesTitle}》 ${c.title}`
|
|
434
|
+
: c.title;
|
|
428
435
|
return {
|
|
429
436
|
title: c.title,
|
|
437
|
+
// Composed card headline: `《系列名》 章节名` for series novels, else the
|
|
438
|
+
// bare work title. Templates should render {{displayTitle}}.
|
|
439
|
+
displayTitle,
|
|
440
|
+
// Parent series name for a series novel chapter; empty otherwise. Tempo
|
|
441
|
+
// templates can render `《{{seriesTitle}}》 {{title}}` themselves.
|
|
442
|
+
seriesTitle: c.seriesTitle ?? '',
|
|
430
443
|
pixivId: c.pixivId,
|
|
444
|
+
// Source-neutral alias of pixivId for delivery templates: TelePost's
|
|
445
|
+
// canonical submission field is work_id (pixiv_id deprecated), so
|
|
446
|
+
// templates should render {{workId}} going forward. Same value.
|
|
447
|
+
workId: c.pixivId,
|
|
431
448
|
type: c.type,
|
|
432
449
|
targetId: c.targetId ?? '',
|
|
433
450
|
tag: c.tag ?? '',
|
package/dist/delivery/types.d.ts
CHANGED
|
@@ -14,6 +14,12 @@ export interface DownloadedArtifact {
|
|
|
14
14
|
pixivId: string;
|
|
15
15
|
type: DeliveryItemType;
|
|
16
16
|
title: string;
|
|
17
|
+
/**
|
|
18
|
+
* Parent series name for a series-novel chapter, when the API response
|
|
19
|
+
* carried one. This is distinct from `title`, which holds the chapter title
|
|
20
|
+
* only. Empty when the work is not part of a Pixiv series.
|
|
21
|
+
*/
|
|
22
|
+
seriesTitle?: string;
|
|
17
23
|
/**
|
|
18
24
|
* Pixiv author (artist) display name, when the API response carried one.
|
|
19
25
|
*
|
|
@@ -57,6 +63,13 @@ export interface DownloadedArtifact {
|
|
|
57
63
|
export declare function deliveryFilePaths(artifact: DownloadedArtifact): string[];
|
|
58
64
|
export interface DeliveryContext {
|
|
59
65
|
title: string;
|
|
66
|
+
/**
|
|
67
|
+
* Series (series novel) name, when the work belongs to a Pixiv series.
|
|
68
|
+
* A series novel's `title` holds the chapter title only; this carries the
|
|
69
|
+
* parent series name so templates can render e.g. `《系列名》 章节名`.
|
|
70
|
+
* Empty when the work is not part of a series.
|
|
71
|
+
*/
|
|
72
|
+
seriesTitle?: string;
|
|
60
73
|
pixivId: string;
|
|
61
74
|
type: DeliveryItemType;
|
|
62
75
|
/**
|
|
@@ -8,6 +8,21 @@
|
|
|
8
8
|
* purpose (referenced media may still be unmaterialized).
|
|
9
9
|
*/
|
|
10
10
|
export type PixivMediaKind = 'uploadedimage' | 'pixivimage' | 'illust' | 'novelcover';
|
|
11
|
+
/**
|
|
12
|
+
* Default (and production) namespace prefix of media-asset ids: `pixiv`.
|
|
13
|
+
* `pixiv:` asset ids stay valid forever — consumers (TelePost) treat the
|
|
14
|
+
* prefix as opaque; a second source may adopt its own namespace via
|
|
15
|
+
* `download.assetNamespace` in the config without any consumer change.
|
|
16
|
+
*/
|
|
17
|
+
export declare const DEFAULT_MEDIA_ASSET_NAMESPACE = "pixiv";
|
|
18
|
+
/**
|
|
19
|
+
* Configure the media-asset id namespace for this process. Called once from
|
|
20
|
+
* the config loader (`download.assetNamespace`); invalid/empty values fall
|
|
21
|
+
* back to the default so existing `pixiv:` ids never break.
|
|
22
|
+
*/
|
|
23
|
+
export declare function configureMediaAssetNamespace(namespace?: string): void;
|
|
24
|
+
/** Current media-asset id namespace (default `pixiv`). */
|
|
25
|
+
export declare function getMediaAssetNamespace(): string;
|
|
11
26
|
export interface MediaSourceRef {
|
|
12
27
|
/** Pixiv work id, e.g. the novel/illust id. */
|
|
13
28
|
workId: string;
|
|
@@ -33,10 +48,11 @@ export interface MediaAsset {
|
|
|
33
48
|
sourceRef?: MediaSourceRef;
|
|
34
49
|
}
|
|
35
50
|
/**
|
|
36
|
-
* Deterministic stable identity:
|
|
51
|
+
* Deterministic stable identity: `<namespace>:<workId>:<pixivKind>[:<sourceId>]`
|
|
52
|
+
* where `namespace` defaults to the configured asset namespace (`pixiv`).
|
|
37
53
|
* Never carries Telegram/thelegraph/catbox ids.
|
|
38
54
|
*/
|
|
39
|
-
export declare function mediaAssetId(workId: string, pixivKind: PixivMediaKind, sourceId?: string): string;
|
|
55
|
+
export declare function mediaAssetId(workId: string, pixivKind: PixivMediaKind, sourceId?: string, namespace?: string): string;
|
|
40
56
|
export interface MediaAssetInput {
|
|
41
57
|
workId: string;
|
|
42
58
|
kind: PixivMediaKind;
|
|
@@ -1,16 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_MEDIA_ASSET_NAMESPACE = void 0;
|
|
4
|
+
exports.configureMediaAssetNamespace = configureMediaAssetNamespace;
|
|
5
|
+
exports.getMediaAssetNamespace = getMediaAssetNamespace;
|
|
3
6
|
exports.mediaAssetId = mediaAssetId;
|
|
4
7
|
exports.buildMediaAsset = buildMediaAsset;
|
|
5
8
|
/**
|
|
6
|
-
*
|
|
9
|
+
* Default (and production) namespace prefix of media-asset ids: `pixiv`.
|
|
10
|
+
* `pixiv:` asset ids stay valid forever — consumers (TelePost) treat the
|
|
11
|
+
* prefix as opaque; a second source may adopt its own namespace via
|
|
12
|
+
* `download.assetNamespace` in the config without any consumer change.
|
|
13
|
+
*/
|
|
14
|
+
exports.DEFAULT_MEDIA_ASSET_NAMESPACE = 'pixiv';
|
|
15
|
+
const NAMESPACE_PATTERN = /^[a-z0-9][a-z0-9-]{0,31}$/;
|
|
16
|
+
let assetNamespace = exports.DEFAULT_MEDIA_ASSET_NAMESPACE;
|
|
17
|
+
/**
|
|
18
|
+
* Configure the media-asset id namespace for this process. Called once from
|
|
19
|
+
* the config loader (`download.assetNamespace`); invalid/empty values fall
|
|
20
|
+
* back to the default so existing `pixiv:` ids never break.
|
|
21
|
+
*/
|
|
22
|
+
function configureMediaAssetNamespace(namespace) {
|
|
23
|
+
const clean = String(namespace ?? '').trim().toLowerCase();
|
|
24
|
+
assetNamespace = NAMESPACE_PATTERN.test(clean) ? clean : exports.DEFAULT_MEDIA_ASSET_NAMESPACE;
|
|
25
|
+
}
|
|
26
|
+
/** Current media-asset id namespace (default `pixiv`). */
|
|
27
|
+
function getMediaAssetNamespace() {
|
|
28
|
+
return assetNamespace;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Deterministic stable identity: `<namespace>:<workId>:<pixivKind>[:<sourceId>]`
|
|
32
|
+
* where `namespace` defaults to the configured asset namespace (`pixiv`).
|
|
7
33
|
* Never carries Telegram/thelegraph/catbox ids.
|
|
8
34
|
*/
|
|
9
|
-
function mediaAssetId(workId, pixivKind, sourceId) {
|
|
35
|
+
function mediaAssetId(workId, pixivKind, sourceId, namespace) {
|
|
10
36
|
const cleanWork = String(workId).trim();
|
|
11
37
|
const cleanKind = String(pixivKind).trim();
|
|
12
38
|
const cleanSource = sourceId ? String(sourceId).trim() : '';
|
|
13
|
-
|
|
39
|
+
const ns = String(namespace ?? assetNamespace).trim() || exports.DEFAULT_MEDIA_ASSET_NAMESPACE;
|
|
40
|
+
return `${ns}:${cleanWork}:${cleanKind}${cleanSource ? `:${cleanSource}` : ''}`;
|
|
14
41
|
}
|
|
15
42
|
/** Build a canonical MediaAsset from a resolved Pixiv media reference. */
|
|
16
43
|
function buildMediaAsset(input) {
|
|
@@ -386,6 +386,7 @@ class NovelDownloader {
|
|
|
386
386
|
pixivId: workId,
|
|
387
387
|
type: 'novel',
|
|
388
388
|
title: detail.title,
|
|
389
|
+
seriesTitle: detail.series?.title || undefined,
|
|
389
390
|
// Attribution travels with the artifact: a publishing provider (e.g. the
|
|
390
391
|
// TelePost submission target) renders it into its own fields, so the
|
|
391
392
|
// downloader must not be the only place that knows the author.
|
package/dist/package.json
CHANGED
package/dist/version.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BUILD = void 0;
|
|
4
4
|
// GENERATED by scripts/write-version.js — do not edit manually.
|
|
5
|
-
exports.BUILD = { version: '3.
|
|
5
|
+
exports.BUILD = { version: '3.6.0', commit: '650a0bacba90' };
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
package/dist/webui/package.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixivflow",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.6.0",
|
|
4
4
|
"description": "🎨 Pixiv 下载、筛选与自动收集工具 - 批量下载插画和小说、按标签/热度/日期筛选、定时任务与可靠 HTTP 交付 | Pixiv downloader and automation toolkit with filtering, scheduling and reliable HTTP delivery",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|