patreon-dl 3.5.0 → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Patrick Kan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -74,7 +74,7 @@ $ patreon-dl --configure-youtube
74
74
 
75
75
  You can specify external programs to download embedded videos or from embedded links. For YouTube videos, this will replace the built-in downloader.
76
76
 
77
- See the [example config](./example-embed.conf) on how to configure an external downloader to fetch YouTube and Vimeo videos through [yt-dlp](https://github.com/yt-dlp/yt-dlp). For Vimeo videos, a [helper script](./bin/patreon-dl-vimeo.js) bundled with `patreon-dl` is used.
77
+ See the [example config](./example-embed.conf) on how to configure an external downloader to fetch YouTube, Vimeo and SproutVideo content through [yt-dlp](https://github.com/yt-dlp/yt-dlp). Helper scripts bundled with `patreon-dl` are used in the case of Vimeo and SproutVideo ([patreon-dl-vimeo.js](./bin/patreon-dl-vimeo.js) and [patreon-dl-sprout.js](./bin/patreon-dl-sprout.js) respectively).
78
78
 
79
79
  ## Installation
80
80
 
@@ -293,6 +293,12 @@ Note the URL shown in the output. Open this URL in a web browser to begin viewin
293
293
 
294
294
  ## Changelog
295
295
 
296
+ v3.6.0
297
+ - Browse: affix nav links (previous / next post) to viewport bottom if post content overflows ([patreon-dl-gui#41](https://github.com/patrickkfkan/patreon-dl-gui/issues/41))
298
+ - Fix error when Deno path contains spaces ([patreon-dl-gui#42](https://github.com/patrickkfkan/patreon-dl-gui/issues/42))
299
+ - Add SproutVideo download script ([patreon-dl-gui#43](https://github.com/patrickkfkan/patreon-dl-gui/issues/43))
300
+ - Fix YouTube download returning "auth required" error
301
+
296
302
  v3.5.0
297
303
  - Add support for downloading from "shop" URLs (e.g. `https://www.patreon.com/<creator>/shop`). This will download all products from a creator's shop.
298
304
  - Add `productsPublished` / `products.published.after` / `products.published.before` option to set publish date criteria of products included in download.
@@ -0,0 +1,173 @@
1
+ import parseArgs from 'yargs-parser';
2
+ import spawn from '@patrickkfkan/cross-spawn';
3
+ import path from 'path';
4
+
5
+ /**
6
+ * EmbedlyDownloader uses yt-dlp to download content embedded through Embedly:
7
+ * - It obtains the video URL from 'embed.html' or 'embed.url' commandline args.
8
+ * The former ("player URL") is always preferable since it is what's actually played within
9
+ * the Patreon post, and furthermore 'embed.url' sometimes returns "Page not found" (see
10
+ * issue: https://github.com/patrickkfkan/patreon-dl/issues/65) or a password-protected page.
11
+ * - The URL is passed to yt-dlp.
12
+ * - yt-dlp downloads the video from URL and saves it to 'dest.dir'. The filename is determined by the specified
13
+ * format '%(title)s.%(ext)s' (see: https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#output-template).
14
+ * - Fallback to embed URL if player URL fails to download.
15
+ */
16
+
17
+ export default class EmbedlyDownloader {
18
+
19
+ /**
20
+ *
21
+ * @param {*} provider Name of the provider.
22
+ * @param {*} srcHostname Hostname of the embedded source URL.
23
+ */
24
+ constructor(provider, srcHostname) {
25
+ this.provider = provider;
26
+ this.srcHostname = srcHostname;
27
+ }
28
+
29
+ getPlayerURL(html) {
30
+ if (!html) {
31
+ return null;
32
+ }
33
+
34
+ const regex = /src="(\/\/cdn.embedly.com\/widgets.+?)"/g;
35
+ const match = regex.exec(html);
36
+ if (match && match[1]) {
37
+ const embedlyURL = match[1];
38
+ console.log('Found Embedly URL from embed HTML:', embedlyURL);
39
+ let embedlySrc;
40
+ try {
41
+ const urlObj = new URL(`https:${embedlyURL}`);
42
+ embedlySrc = urlObj.searchParams.get('src');
43
+ }
44
+ catch (error) {
45
+ console.error('Error parsing Embedly URL:', error);
46
+ }
47
+ try {
48
+ const embedlySrcObj = new URL(embedlySrc);
49
+ if (!this.srcHostname) {
50
+ console.log(`Got Embedly src "${embedlySrc}" - assume it is ${this.provider} player URL since no hostname was specified`);
51
+ }
52
+ else if (embedlySrcObj.hostname === this.srcHostname) {
53
+ console.log(`Got ${this.provider} player URL from Embedly src: ${embedlySrc}`);
54
+ }
55
+ else {
56
+ console.warn(`Embedly src "${embedlySrc}" does not correspond to ${this.provider} player URL`);
57
+ }
58
+ return embedlySrc;
59
+ }
60
+ catch (error) {
61
+ console.error(`Error parsing Embedly src "${embedlySrc}":`, error);
62
+ }
63
+ }
64
+
65
+ return null;
66
+ }
67
+
68
+ getCommandString(cmd, args) {
69
+ const quotedArgs = args.map((arg) => arg.includes(' ') ? `"${arg}"` : arg);
70
+ return [
71
+ cmd,
72
+ ...quotedArgs
73
+ ].join(' ');
74
+ }
75
+
76
+ async download(url, o, videoPassword, ytdlpPath, ytdlpArgs) {
77
+ let proc;
78
+ const ytdlp = ytdlpPath || 'yt-dlp';
79
+ const parsedYtdlpArgs = parseArgs(ytdlpArgs);
80
+ try {
81
+ return await new Promise((resolve, reject) => {
82
+ let settled = false;
83
+ const args = [];
84
+ if (!parsedYtdlpArgs['o'] && !parsedYtdlpArgs['output']) {
85
+ args.push('-o', o);
86
+ }
87
+ if (!parsedYtdlpArgs['referrer']) {
88
+ args.push('--add-header', 'Referer: https://patreon.com/');
89
+ }
90
+ args.push(...ytdlpArgs);
91
+ const printArgs = [...args];
92
+ if (videoPassword && !parsedYtdlpArgs['video-password']) {
93
+ args.push('--video-password', videoPassword);
94
+ printArgs.push('--video-password', '******');
95
+ }
96
+ args.push(url);
97
+ printArgs.push(url);
98
+
99
+ console.log(`Command: ${this.getCommandString(ytdlp, printArgs)}`);
100
+ proc = spawn(ytdlp, args);
101
+
102
+ proc.stdout?.on('data', (data) => {
103
+ console.log(data.toString());
104
+ });
105
+
106
+ proc.stderr?.on('data', (data_1) => {
107
+ console.error(data_1.toString());
108
+ });
109
+
110
+ proc.on('error', (err) => {
111
+ if (settled) {
112
+ return;
113
+ }
114
+ settled = true;
115
+ reject(err);
116
+ });
117
+
118
+ proc.on('exit', (code) => {
119
+ if (settled) {
120
+ return;
121
+ }
122
+ settled = true;
123
+ resolve(code);
124
+ });
125
+ });
126
+ } finally {
127
+ if (proc) {
128
+ proc.removeAllListeners();
129
+ proc.stdout?.removeAllListeners();
130
+ proc.stderr?.removeAllListeners();
131
+ }
132
+ }
133
+ }
134
+
135
+ async start() {
136
+ const args = parseArgs(process.argv.slice(2));
137
+ const {
138
+ 'o': _o,
139
+ 'embed-html': _embedHTML,
140
+ 'embed-url': _embedURL,
141
+ 'video-password': videoPassword,
142
+ 'yt-dlp': _ytdlpPath
143
+ } = args;
144
+ const o = _o?.trim() ? path.resolve(_o.trim()) : null;
145
+ const embedHTML = _embedHTML?.trim();
146
+ const embedURL = _embedURL?.trim();
147
+ const ytdlpPath = _ytdlpPath?.trim() ? path.resolve(_ytdlpPath.trim()) : null;
148
+ const ytdlpArgs = args['_'];
149
+
150
+ if (!o) {
151
+ throw Error('No output file specified');
152
+ }
153
+
154
+ if (!embedHTML && !embedURL) {
155
+ throw Error('No embed HTML or URL provided');
156
+ }
157
+
158
+ const _url = this.getPlayerURL(embedHTML) || embedURL;
159
+
160
+ if (!_url) {
161
+ throw Error(`Failed to obtain video URL`);
162
+ }
163
+
164
+ console.log(`Going to download video from "${_url}"`);
165
+ let code = await this.download(_url, o, videoPassword, ytdlpPath, ytdlpArgs);
166
+ if (code !== 0 && _url !== embedURL && embedURL) {
167
+ console.log(`Download failed - retrying with embed URL "${embedURL}"`);
168
+ return await this.download(embedURL);
169
+ }
170
+
171
+ return code;
172
+ }
173
+ }
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * External downloader for embedded SproutVideo videos. Obtains the appropriate URL to download from and
5
+ * passes it to 'yt-dlp' (https://github.com/yt-dlp/yt-dlp).
6
+ *
7
+ * Usage
8
+ * -----
9
+ * Place the following two lines in your 'patreon-dl' config file:
10
+ *
11
+ * [embed.downloader.sproutvideo]
12
+ * exec = patreon-dl-sprout -o "{dest.dir}/%(title)s.%(ext)s" --embed-html "{embed.html}" --embed-url "{embed.url}"
13
+ *
14
+ * You can append the following additional options to the exec line if necessary:
15
+ * --video-password "<password>": for password-protected videos
16
+ * --yt-dlp "</path/to/yt-dlp>": if yt-dlp is not in the PATH
17
+ *
18
+ * You can pass options directly to yt-dlp. To do so, add '--' to the end of the exec line, followed by the options.
19
+ * For example:
20
+ * exec = patreon-dl-sprout -o "{dest.dir}/%(title)s.%(ext)s" --embed-html "{embed.html}" --embed-url "{embed.url}" -- --cookies-from-browser firefox
21
+ *
22
+ * Upon encountering a post with embedded SproutVideo content, 'patreon-dl' will call this script, which in turn proceeds to download through SproutVideoDownloader
23
+ * (see the parent class EmbedlyDownloader for more info).
24
+ */
25
+
26
+ import EmbedlyDownloader from './EmbedlyDownloader.js';
27
+
28
+ class SproutVideoDownloader extends EmbedlyDownloader {
29
+ constructor() {
30
+ super('SproutVideo', 'videos.sproutvideo.com');
31
+ }
32
+ }
33
+
34
+ (async () => {
35
+ const downloader = new SproutVideoDownloader();
36
+ try {
37
+ process.exit(await downloader.start());
38
+ }
39
+ catch (error) {
40
+ console.error(error instanceof Error ? error.message : String(error));
41
+ process.exit(1);
42
+ }
43
+ })();
@@ -19,173 +19,38 @@
19
19
  * For example:
20
20
  * exec = patreon-dl-vimeo -o "{dest.dir}/%(title)s.%(ext)s" --embed-html "{embed.html}" --embed-url "{embed.url}" -- --cookies-from-browser firefox
21
21
  *
22
- * Upon encountering a post with embedded Vimeo content, 'patreon-dl' will call this script. The following then happens:
23
- * - This script obtains the video URL from 'embed.html' or 'embed.url'. The former ("player URL") is always preferable
24
- * since it is what's actually played within the Patreon post, and furthermore 'embed.url' sometimes returns
25
- * "Page not found" (see issue: https://github.com/patrickkfkan/patreon-dl/issues/65).
26
- * - The URL is passed to yt-dlp.
27
- * - yt-dlp downloads the video from URL and saves it to 'dest.dir'. The filename is determined by the specified
28
- * format '%(title)s.%(ext)s' (see: https://github.com/yt-dlp/yt-dlp?tab=readme-ov-file#output-template).
29
- * - Fallback to embed URL if player URL fails to download.
30
- *
22
+ * Upon encountering a post with embedded Vimeo content, 'patreon-dl' will call this script, which in turn proceeds to download through VimeoDownloader
23
+ * (see the parent class EmbedlyDownloader for more info).
31
24
  */
32
25
 
33
- import parseArgs from 'yargs-parser';
34
- import spawn from '@patrickkfkan/cross-spawn';
35
- import path from 'path';
36
-
37
- function tryGetPlayerURL(html) {
38
- if (!html) {
39
- return null;
40
- }
26
+ import EmbedlyDownloader from './EmbedlyDownloader.js';
41
27
 
42
- const regex = /https:\/\/player\.vimeo\.com\/video\/\d+/g;
43
- const match = regex.exec(html);
44
- if (match && match[0]) {
45
- console.log('Found Vimeo player URL from embed HTML:', match[0]);
46
- return match[0];
28
+ class VimeoDownloader extends EmbedlyDownloader {
29
+ constructor() {
30
+ super('Vimeo', 'player.vimeo.com');
47
31
  }
48
32
 
49
- const regex2 = /src="(\/\/cdn.embedly.com\/widgets.+?)"/g;
50
- const match2 = regex2.exec(html);
51
- if (match2 && match2[1]) {
52
- const embedlyURL = match2[1];
53
- console.log('Found Embedly URL from embed HTML:', embedlyURL);
54
- let embedlySrc;
55
- try {
56
- const urlObj = new URL(`https:${embedlyURL}`);
57
- embedlySrc = urlObj.searchParams.get('src');
58
- }
59
- catch (error) {
60
- console.error('Error parsing Embedly URL:', error);
61
- }
62
- try {
63
- const embedlySrcObj = new URL(embedlySrc);
64
- if (embedlySrcObj.hostname === 'player.vimeo.com') {
65
- console.log(`Got Vimeo player URL from Embedly src: ${embedlySrc}`);
66
- }
67
- else {
68
- console.warn(`Embedly src "${embedlySrc}" does not correspond to Vimeo player URL`);
33
+ // Override
34
+ getPlayerURL(html) {
35
+ if (html) {
36
+ const regex = /https:\/\/player\.vimeo\.com\/video\/\d+/g;
37
+ const match = regex.exec(html);
38
+ if (match && match[0]) {
39
+ console.log('Found Vimeo player URL from embed HTML:', match[0]);
40
+ return match[0];
69
41
  }
70
- return embedlySrc;
71
- }
72
- catch (error) {
73
- console.error(`Error parsing Embedly src "${embedlySrc}":`, error);
74
42
  }
43
+ return super.getPlayerURL(html);
75
44
  }
76
-
77
- return null;
78
45
  }
79
46
 
80
- function getCommandString(cmd, args) {
81
- const quotedArgs = args.map((arg) => arg.includes(' ') ? `"${arg}"` : arg);
82
- return [
83
- cmd,
84
- ...quotedArgs
85
- ].join(' ');
86
- }
87
-
88
- async function download(url, o, videoPassword, ytdlpPath, ytdlpArgs) {
89
- let proc;
90
- const ytdlp = ytdlpPath || 'yt-dlp';
91
- const parsedYtdlpArgs = parseArgs(ytdlpArgs);
47
+ (async () => {
48
+ const downloader = new VimeoDownloader();
92
49
  try {
93
- return await new Promise((resolve, reject) => {
94
- let settled = false;
95
- const args = [];
96
- if (!parsedYtdlpArgs['o'] && !parsedYtdlpArgs['output']) {
97
- args.push('-o', o);
98
- }
99
- if (!parsedYtdlpArgs['referrer']) {
100
- args.push('--referer', 'https://patreon.com/');
101
- }
102
- args.push(...ytdlpArgs);
103
- const printArgs = [...args];
104
- if (videoPassword && !parsedYtdlpArgs['video-password']) {
105
- args.push('--video-password', videoPassword);
106
- printArgs.push('--video-password', '******');
107
- }
108
- args.push(url);
109
- printArgs.push(url);
110
-
111
- console.log(`Command: ${getCommandString(ytdlp, printArgs)}`);
112
- proc = spawn(ytdlp, args);
113
-
114
- proc.stdout?.on('data', (data) => {
115
- console.log(data.toString());
116
- });
117
-
118
- proc.stderr?.on('data', (data_1) => {
119
- console.error(data_1.toString());
120
- });
121
-
122
- proc.on('error', (err) => {
123
- if (settled) {
124
- return;
125
- }
126
- settled = true;
127
- reject(err);
128
- });
129
-
130
- proc.on('exit', (code) => {
131
- if (settled) {
132
- return;
133
- }
134
- settled = true;
135
- resolve(code);
136
- });
137
- });
138
- } finally {
139
- if (proc) {
140
- proc.removeAllListeners();
141
- proc.stdout?.removeAllListeners();
142
- proc.stderr?.removeAllListeners();
143
- }
50
+ process.exit(await downloader.start());
144
51
  }
145
- }
146
-
147
- const args = parseArgs(process.argv.slice(2));
148
- const {
149
- 'o': _o,
150
- 'embed-html': _embedHTML,
151
- 'embed-url': _embedURL,
152
- 'video-password': videoPassword,
153
- 'yt-dlp': _ytdlpPath
154
- } = args;
155
- const o = _o?.trim() ? path.resolve(_o.trim()) : null;
156
- const embedHTML = _embedHTML?.trim();
157
- const embedURL = _embedURL?.trim();
158
- const ytdlpPath = _ytdlpPath?.trim() ? path.resolve(_ytdlpPath.trim()) : null;
159
- const ytdlpArgs = args['_'];
160
-
161
- if (!o) {
162
- console.error('No output file specified');
163
- process.exit(1);
164
- }
165
-
166
- if (!embedHTML && !embedURL) {
167
- console.error('No embed HTML or URL provided');
168
- process.exit(1);
169
- }
170
-
171
- const url = tryGetPlayerURL(embedHTML) || embedURL;
172
-
173
- if (!url) {
174
- console.error(`Failed to obtain video URL`);
175
- process.exit(1);
176
- }
177
-
178
- async function doDownload(_url) {
179
- let code = await download(_url, o, videoPassword, ytdlpPath, ytdlpArgs);
180
- if (code !== 0 && _url !== embedURL && embedURL) {
181
- console.log(`Download failed - retrying with embed URL "${embedURL}"`);
182
- return await doDownload(embedURL);
52
+ catch (error) {
53
+ console.error(error instanceof Error ? error.message : String(error));
54
+ process.exit(1);
183
55
  }
184
- return code;
185
- }
186
-
187
- console.log(`Going to download video from "${url}"`);
188
-
189
- doDownload(url).then((code) => {
190
- process.exit(code);
191
- });
56
+ })();
@@ -11,7 +11,7 @@ export declare function CampaignAPIMixin<TBase extends APIConstructor>(Base: TBa
11
11
  withCounts?: false;
12
12
  }): Campaign | null;
13
13
  getCampaign(params: GetCampaignParams): Campaign | CampaignWithCounts | null;
14
- "__#131@#sanitizeCampaign"(campaign: Campaign): void;
14
+ "__#129@#sanitizeCampaign"(campaign: Campaign): void;
15
15
  name: string;
16
16
  db: import("../db").DBInstance;
17
17
  logger?: import("../..").Logger | null;
@@ -13,7 +13,7 @@ export declare function ContentAPIMixin<TBase extends APIConstructor>(Base: TBas
13
13
  } | null;
14
14
  getCollectionList(params: GetCollectionListParams): import("../types/Content.js").CollectionList;
15
15
  getPostTagList(params: GetPostTagListParams): import("../types/Content.js").PostTagList;
16
- "__#132@#processPostContentInlineMedia"(post: Post): void;
16
+ "__#130@#processPostContentInlineMedia"(post: Post): void;
17
17
  name: string;
18
18
  db: import("../db").DBInstance;
19
19
  logger?: import("../..").Logger | null;
@@ -3,7 +3,7 @@ import { type FilterData, type MediaFilterSearchParams, type PostFilterSearchPar
3
3
  export declare function FilterAPIMixin<TBase extends APIConstructor>(Base: TBase): {
4
4
  new (...args: any[]): {
5
5
  getPostFilterData(campaignId: string): FilterData<PostFilterSearchParams>;
6
- "__#133@#getPostTypeTitle"(postType: string): "Link" | "Audio" | "Image" | "Video" | "Other" | "Text" | "Podcast" | "Poll";
6
+ "__#131@#getPostTypeTitle"(postType: string): "Link" | "Audio" | "Image" | "Video" | "Other" | "Text" | "Podcast" | "Poll";
7
7
  getProductFilterData(campaignId: string): FilterData<ProductFilterSearchParams>;
8
8
  getMediaFilterData(campaignId: string): FilterData<MediaFilterSearchParams>;
9
9
  name: string;
@@ -10,7 +10,7 @@ export declare class APIBase {
10
10
  constructor(db: DBInstance, logger?: Logger | null);
11
11
  static getInstance(db: DBInstance, logger?: Logger | null): {
12
12
  getPostFilterData(campaignId: string): import("../types/Filter.js").FilterData<import("../types/Filter.js").PostFilterSearchParams>;
13
- "__#133@#getPostTypeTitle"(postType: string): "Link" | "Audio" | "Image" | "Video" | "Other" | "Text" | "Podcast" | "Poll";
13
+ "__#131@#getPostTypeTitle"(postType: string): "Link" | "Audio" | "Image" | "Video" | "Other" | "Text" | "Podcast" | "Poll";
14
14
  getProductFilterData(campaignId: string): import("../types/Filter.js").FilterData<import("../types/Filter.js").ProductFilterSearchParams>;
15
15
  getMediaFilterData(campaignId: string): import("../types/Filter.js").FilterData<import("../types/Filter.js").MediaFilterSearchParams>;
16
16
  name: string;
@@ -49,7 +49,7 @@ export declare class APIBase {
49
49
  } | null;
50
50
  getCollectionList(params: import("../types/Content.js").GetCollectionListParams): import("../types/Content.js").CollectionList;
51
51
  getPostTagList(params: import("../types/Content.js").GetPostTagListParams): import("../types/Content.js").PostTagList;
52
- "__#132@#processPostContentInlineMedia"(post: import("../../index.js").Post): void;
52
+ "__#130@#processPostContentInlineMedia"(post: import("../../index.js").Post): void;
53
53
  name: string;
54
54
  db: DBInstance;
55
55
  logger?: Logger | null;
@@ -64,7 +64,7 @@ export declare class APIBase {
64
64
  withCounts?: false;
65
65
  }): import("../../index.js").Campaign | null;
66
66
  getCampaign(params: import("../types/Campaign.js").GetCampaignParams): import("../../index.js").Campaign | import("../types/Campaign.js").CampaignWithCounts | null;
67
- "__#131@#sanitizeCampaign"(campaign: import("../../index.js").Campaign): void;
67
+ "__#129@#sanitizeCampaign"(campaign: import("../../index.js").Campaign): void;
68
68
  name: string;
69
69
  db: DBInstance;
70
70
  logger?: Logger | null;
@@ -77,7 +77,7 @@ export declare class APIBase {
77
77
  declare const API: {
78
78
  new (...args: any[]): {
79
79
  getPostFilterData(campaignId: string): import("../types/Filter.js").FilterData<import("../types/Filter.js").PostFilterSearchParams>;
80
- "__#133@#getPostTypeTitle"(postType: string): "Link" | "Audio" | "Image" | "Video" | "Other" | "Text" | "Podcast" | "Poll";
80
+ "__#131@#getPostTypeTitle"(postType: string): "Link" | "Audio" | "Image" | "Video" | "Other" | "Text" | "Podcast" | "Poll";
81
81
  getProductFilterData(campaignId: string): import("../types/Filter.js").FilterData<import("../types/Filter.js").ProductFilterSearchParams>;
82
82
  getMediaFilterData(campaignId: string): import("../types/Filter.js").FilterData<import("../types/Filter.js").MediaFilterSearchParams>;
83
83
  name: string;
@@ -122,7 +122,7 @@ declare const API: {
122
122
  } | null;
123
123
  getCollectionList(params: import("../types/Content.js").GetCollectionListParams): import("../types/Content.js").CollectionList;
124
124
  getPostTagList(params: import("../types/Content.js").GetPostTagListParams): import("../types/Content.js").PostTagList;
125
- "__#132@#processPostContentInlineMedia"(post: import("../../index.js").Post): void;
125
+ "__#130@#processPostContentInlineMedia"(post: import("../../index.js").Post): void;
126
126
  name: string;
127
127
  db: DBInstance;
128
128
  logger?: Logger | null;
@@ -139,7 +139,7 @@ declare const API: {
139
139
  withCounts?: false;
140
140
  }): import("../../index.js").Campaign | null;
141
141
  getCampaign(params: import("../types/Campaign.js").GetCampaignParams): import("../../index.js").Campaign | import("../types/Campaign.js").CampaignWithCounts | null;
142
- "__#131@#sanitizeCampaign"(campaign: import("../../index.js").Campaign): void;
142
+ "__#129@#sanitizeCampaign"(campaign: import("../../index.js").Campaign): void;
143
143
  name: string;
144
144
  db: DBInstance;
145
145
  logger?: Logger | null;
@@ -7,10 +7,10 @@ export declare function CampaignDBMixin<TBase extends UserDBConstructor>(Base: T
7
7
  new (...args: any[]): {
8
8
  saveCampaign(campaign: Campaign | null, downloadDate: Date, overwriteIfExists?: boolean): void;
9
9
  getCampaign(params: GetCampaignParams): Campaign | null;
10
- "__#115@#saveRewards"(campaign: Campaign): void;
11
- "__#115@#doSaveReward"(campaign: Campaign, reward: Reward): void;
10
+ "__#113@#saveRewards"(campaign: Campaign): void;
11
+ "__#113@#doSaveReward"(campaign: Campaign, reward: Reward): void;
12
12
  getCampaignList(params: GetCampaignListParams): CampaignList;
13
- "__#115@#getCampaignWithCounts"(params: GetCampaignParams): CampaignWithCounts | null;
13
+ "__#113@#getCampaignWithCounts"(params: GetCampaignParams): CampaignWithCounts | null;
14
14
  checkCampaignExists(id: string): boolean;
15
15
  saveUser(user: import("../../index.js").User | null): void;
16
16
  getUserByID(id: string): import("../../index.js").User | null;
@@ -5,13 +5,13 @@ import { type CampaignDBConstructor } from './CampaignDBMixin.js';
5
5
  export declare function ContentDBMixin<TBase extends CampaignDBConstructor>(Base: TBase): {
6
6
  new (...args: any[]): {
7
7
  saveContent(content: Post | Product): void;
8
- "__#116@#saveContentMedia"(content: Post | Product): void;
9
- "__#116@#savepostMedia"(post: Post): void;
10
- "__#116@#saveProductMedia"(product: Product): void;
11
- "__#116@#doSaveContentMedia"(content: Post | Product, media: Downloadable, mediaIndex: number, isPreview: boolean): void;
12
- "__#116@#publishedAtToTime"(publishedAt: string | null): number | null;
13
- "__#116@#savePostTiers"(post: Post): void;
14
- "__#116@#doSaveTier"(post: Post, tier: Tier): void;
8
+ "__#114@#saveContentMedia"(content: Post | Product): void;
9
+ "__#114@#savepostMedia"(post: Post): void;
10
+ "__#114@#saveProductMedia"(product: Product): void;
11
+ "__#114@#doSaveContentMedia"(content: Post | Product, media: Downloadable, mediaIndex: number, isPreview: boolean): void;
12
+ "__#114@#publishedAtToTime"(publishedAt: string | null): number | null;
13
+ "__#114@#savePostTiers"(post: Post): void;
14
+ "__#114@#doSaveTier"(post: Post, tier: Tier): void;
15
15
  savePostComments(post: Post, comments: Comment[]): void;
16
16
  checkPostCommentsExist(post: Post): boolean;
17
17
  getContent(id: string, contentType: "post"): PostWithComments | null;
@@ -26,7 +26,7 @@ export declare function ContentDBMixin<TBase extends CampaignDBConstructor>(Base
26
26
  * @param row Must have `details`, `comment_count` and `comments`
27
27
  * @returns
28
28
  */
29
- "__#116@#parseContentRowJoinedComments"(row: any): any;
29
+ "__#114@#parseContentRowJoinedComments"(row: any): any;
30
30
  getContentCountByDate(contentType: ContentType, groupBy: "year" | "month", filter?: {
31
31
  campaign?: Campaign | string | null;
32
32
  date?: Date | null;
@@ -50,19 +50,19 @@ export declare function ContentDBMixin<TBase extends CampaignDBConstructor>(Base
50
50
  collection: Collection;
51
51
  campaignId: string;
52
52
  } | null;
53
- "__#116@#savePostCollection"(post: Post): void;
53
+ "__#114@#savePostCollection"(post: Post): void;
54
54
  getCollectionList(params: GetCollectionListParams): CollectionList;
55
55
  checkCollectionExists(id: string): boolean;
56
56
  checkPostTagExists(id: string, campaign: Campaign | null): boolean;
57
57
  getPostComments(post: Post | string): Comment[] | null;
58
- "__#116@#savePostTags"(post: Post): void;
58
+ "__#114@#savePostTags"(post: Post): void;
59
59
  getPostTagList(params: GetPostTagListParams): PostTagList;
60
60
  saveCampaign(campaign: Campaign | null, downloadDate: Date, overwriteIfExists?: boolean): void;
61
61
  getCampaign(params: import("../types/Campaign").GetCampaignParams): Campaign | null;
62
- "__#115@#saveRewards"(campaign: Campaign): void;
63
- "__#115@#doSaveReward"(campaign: Campaign, reward: import("../../entities").Reward): void;
62
+ "__#113@#saveRewards"(campaign: Campaign): void;
63
+ "__#113@#doSaveReward"(campaign: Campaign, reward: import("../../entities").Reward): void;
64
64
  getCampaignList(params: import("../types/Campaign").GetCampaignListParams): import("../types/Campaign").CampaignList;
65
- "__#115@#getCampaignWithCounts"(params: import("../types/Campaign").GetCampaignParams): import("../types/Campaign").CampaignWithCounts | null;
65
+ "__#113@#getCampaignWithCounts"(params: import("../types/Campaign").GetCampaignParams): import("../types/Campaign").CampaignWithCounts | null;
66
66
  checkCampaignExists(id: string): boolean;
67
67
  saveUser(user: import("../../entities").User | null): void;
68
68
  getUserByID(id: string): import("../../entities").User | null;