@purpleproser/soundboard-downloader-cli 1.6.3 → 1.7.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/.github/workflows/publish.yml +1 -1
- package/.github/workflows/test.yml +1 -1
- package/CHANGELOG.md +7 -0
- package/README.md +23 -3
- package/dist/api/my-instants.api.js +4 -3
- package/dist/api/my-instants.api.js.map +1 -1
- package/dist/utils/rate-limiter.js +50 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/package.json +2 -2
- package/vulnerability-score.json +53 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
This changelog is automatically generated by [release-please](https://github.com/google-github-actions/release-please-action).
|
|
4
4
|
|
|
5
|
+
## [1.7.0](https://github.com/blacksagres/soundboard-downloader-cli/compare/v1.6.3...v1.7.0) (2026-03-10)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* improve MyInstants compliance with rate limiting and legal updates\n\n- Add rate limiting to API requests (1s delay between calls)\n- Update package description to specify personal, non-commercial use\n- Add comprehensive compliance section to README\n- Create rate-limiter utility for reusable compliance\n\nThis improves compliance with MyInstants Terms of Use by:\n1. Adding delays between requests to avoid scraping appearance\n2. Explicitly stating non-commercial use limitations\n3. Educating users about responsible usage\n\nSigned-off-by: Mistral Vibe <vibe@mistral.ai>\n\nGenerated by Mistral Vibe.\nCo-Authored-By: Mistral Vibe <vibe@mistral.ai> ([d756529](https://github.com/blacksagres/soundboard-downloader-cli/commit/d75652972ff8fb7e56a2607bc11647edba506699))
|
|
11
|
+
|
|
5
12
|
## [1.6.3](https://github.com/blacksagres/soundboard-downloader-cli/compare/v1.6.2...v1.6.3) (2026-03-10)
|
|
6
13
|
|
|
7
14
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# @purpleproser/soundboard-downloader-cli
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@purpleproser/soundboard-downloader-cli)
|
|
4
|
+
[](https://www.npmjs.com/package/@purpleproser/soundboard-downloader-cli)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://badge.socket.dev/npm/package/@purpleproser/soundboard-downloader-cli/1.6.2)
|
|
4
7
|
|
|
5
8
|
A Node.js command-line tool to download soundboard sounds from [MyInstants](https://myinstants.com).
|
|
6
9
|
|
|
@@ -15,10 +18,10 @@ A Node.js command-line tool to download soundboard sounds from [MyInstants](http
|
|
|
15
18
|
|
|
16
19
|
## Tech Stack
|
|
17
20
|
|
|
18
|
-
- **
|
|
21
|
+
- **prompts** - Lightweight interactive CLI prompts
|
|
19
22
|
- **jsdom** - HTML parsing and DOM manipulation
|
|
20
23
|
- **ora** - Elegant terminal spinners
|
|
21
|
-
- **
|
|
24
|
+
- **opener** - Opens URLs in the user's browser to allow previewing the sound
|
|
22
25
|
|
|
23
26
|
## Prerequisites
|
|
24
27
|
|
|
@@ -157,6 +160,23 @@ soundboard-downloader
|
|
|
157
160
|
|
|
158
161
|
Contributions are welcome! Please open an issue or submit a pull request.
|
|
159
162
|
|
|
163
|
+
## Compliance with MyInstants Terms of Use
|
|
164
|
+
|
|
165
|
+
This tool is designed to comply with [MyInstants Terms of Use](https://www.myinstants.com/en/terms_of_use.html):
|
|
166
|
+
|
|
167
|
+
✅ **Personal Use Only**: This tool is for personal, non-commercial use only
|
|
168
|
+
✅ **Official Functionality**: Uses MyInstants' official download buttons and links
|
|
169
|
+
✅ **No Circumvention**: Does not bypass any access controls or copyright protections
|
|
170
|
+
✅ **Rate Limiting**: Includes reasonable delays between requests to avoid server overload
|
|
171
|
+
|
|
172
|
+
⚠️ **User Responsibilities**:
|
|
173
|
+
- Do not use this tool for commercial purposes
|
|
174
|
+
- Do not make excessive automated requests
|
|
175
|
+
- Respect MyInstants' copyright and terms of service
|
|
176
|
+
- MyInstants may revoke access at any time
|
|
177
|
+
|
|
178
|
+
**Important**: MyInstants grants access under a revocable license. This tool may stop working if MyInstants changes their website or terms.
|
|
179
|
+
|
|
160
180
|
## Legal Disclaimer
|
|
161
181
|
|
|
162
182
|
**Important Notice About Copyright and Usage:**
|
|
@@ -44,6 +44,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
44
44
|
exports.getNodeDownloadPage = exports.getAllSoundNodes = exports.hasNextPage = exports.getSoundNodesPage = void 0;
|
|
45
45
|
const api_config_1 = require("./api-config");
|
|
46
46
|
const MockApi = __importStar(require("./my-instants.api.mock"));
|
|
47
|
+
const rate_limiter_1 = require("../utils/rate-limiter");
|
|
47
48
|
/**
|
|
48
49
|
* Fetch a single page of sound nodes from myinstants.com
|
|
49
50
|
* @param searchString The search term
|
|
@@ -58,7 +59,7 @@ const getSoundNodesPage = async ({ searchString, page = 1 }) => {
|
|
|
58
59
|
}
|
|
59
60
|
const escapedSearchParam = encodeURIComponent(searchString);
|
|
60
61
|
const url = `https://www.myinstants.com/en/search/?name=${escapedSearchParam}&page=${page}`;
|
|
61
|
-
const response = await
|
|
62
|
+
const response = await (0, rate_limiter_1.rateLimitedFetch)(url);
|
|
62
63
|
if (!response.ok) {
|
|
63
64
|
throw new Error(`Failed to fetch page ${page} for search: ${searchString}`);
|
|
64
65
|
}
|
|
@@ -79,7 +80,7 @@ const hasNextPage = async ({ searchString, page = 1 }) => {
|
|
|
79
80
|
const escapedSearchParam = encodeURIComponent(searchString);
|
|
80
81
|
const url = `https://www.myinstants.com/en/search/?name=${escapedSearchParam}&page=${page + 1}`;
|
|
81
82
|
try {
|
|
82
|
-
const response = await
|
|
83
|
+
const response = await (0, rate_limiter_1.rateLimitedFetch)(url, { method: 'HEAD' });
|
|
83
84
|
return response.ok;
|
|
84
85
|
}
|
|
85
86
|
catch (error) {
|
|
@@ -129,7 +130,7 @@ const getNodeDownloadPage = async (soundNodeDetailsURL) => {
|
|
|
129
130
|
return MockApi.getNodeDownloadPage(soundNodeDetailsURL);
|
|
130
131
|
}
|
|
131
132
|
const root = `https://www.myinstants.com${soundNodeDetailsURL}`;
|
|
132
|
-
const response = await
|
|
133
|
+
const response = await (0, rate_limiter_1.rateLimitedFetch)(root);
|
|
133
134
|
if (!response.ok) {
|
|
134
135
|
throw new Error(`Failed to fetch sound detail page: ${soundNodeDetailsURL}`);
|
|
135
136
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"my-instants.api.js","sourceRoot":"","sources":["../../src/api/my-instants.api.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGH,6CAAsD;AACtD,gEAAkD;
|
|
1
|
+
{"version":3,"file":"my-instants.api.js","sourceRoot":"","sources":["../../src/api/my-instants.api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rate limiting utility to ensure compliance with MyInstants Terms of Use
|
|
4
|
+
* Adds reasonable delays between requests to avoid being seen as scraping
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.getRateLimitDelay = exports.resetRateLimiter = exports.rateLimitedFetch = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Minimum delay between requests (in milliseconds)
|
|
10
|
+
* This helps avoid being flagged as automated scraping
|
|
11
|
+
*/
|
|
12
|
+
const MIN_REQUEST_DELAY = 1000; // 1 second
|
|
13
|
+
/**
|
|
14
|
+
* Track the last request time for rate limiting
|
|
15
|
+
*/
|
|
16
|
+
let lastRequestTime = 0;
|
|
17
|
+
/**
|
|
18
|
+
* Rate-limited fetch wrapper
|
|
19
|
+
* Ensures minimum delay between consecutive requests to MyInstants
|
|
20
|
+
*/
|
|
21
|
+
const rateLimitedFetch = async (input, init) => {
|
|
22
|
+
// Calculate time since last request
|
|
23
|
+
const now = Date.now();
|
|
24
|
+
const timeSinceLastRequest = now - lastRequestTime;
|
|
25
|
+
// If we're making requests too quickly, add a delay
|
|
26
|
+
if (timeSinceLastRequest < MIN_REQUEST_DELAY) {
|
|
27
|
+
const delayNeeded = MIN_REQUEST_DELAY - timeSinceLastRequest;
|
|
28
|
+
await new Promise(resolve => setTimeout(resolve, delayNeeded));
|
|
29
|
+
}
|
|
30
|
+
// Update last request time
|
|
31
|
+
lastRequestTime = Date.now();
|
|
32
|
+
// Make the actual fetch request
|
|
33
|
+
return fetch(input, init);
|
|
34
|
+
};
|
|
35
|
+
exports.rateLimitedFetch = rateLimitedFetch;
|
|
36
|
+
/**
|
|
37
|
+
* Reset the rate limiter (useful for testing)
|
|
38
|
+
*/
|
|
39
|
+
const resetRateLimiter = () => {
|
|
40
|
+
lastRequestTime = 0;
|
|
41
|
+
};
|
|
42
|
+
exports.resetRateLimiter = resetRateLimiter;
|
|
43
|
+
/**
|
|
44
|
+
* Get the current rate limit delay
|
|
45
|
+
*/
|
|
46
|
+
const getRateLimitDelay = () => {
|
|
47
|
+
return MIN_REQUEST_DELAY;
|
|
48
|
+
};
|
|
49
|
+
exports.getRateLimitDelay = getRateLimitDelay;
|
|
50
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/utils/rate-limiter.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH;;;GAGG;AACH,MAAM,iBAAiB,GAAG,IAAI,CAAC,CAAC,WAAW;AAE3C;;GAEG;AACH,IAAI,eAAe,GAAG,CAAC,CAAC;AAExB;;;GAGG;AACI,MAAM,gBAAgB,GAAG,KAAK,EAAE,KAAwB,EAAE,IAAkB,EAAqB,EAAE;IACxG,oCAAoC;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,oBAAoB,GAAG,GAAG,GAAG,eAAe,CAAC;IAEnD,oDAAoD;IACpD,IAAI,oBAAoB,GAAG,iBAAiB,EAAE,CAAC;QAC7C,MAAM,WAAW,GAAG,iBAAiB,GAAG,oBAAoB,CAAC;QAC7D,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IACjE,CAAC;IAED,2BAA2B;IAC3B,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAE7B,gCAAgC;IAChC,OAAO,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AAC5B,CAAC,CAAC;AAhBW,QAAA,gBAAgB,oBAgB3B;AAEF;;GAEG;AACI,MAAM,gBAAgB,GAAG,GAAS,EAAE;IACzC,eAAe,GAAG,CAAC,CAAC;AACtB,CAAC,CAAC;AAFW,QAAA,gBAAgB,oBAE3B;AAEF;;GAEG;AACI,MAAM,iBAAiB,GAAG,GAAW,EAAE;IAC5C,OAAO,iBAAiB,CAAC;AAC3B,CAAC,CAAC;AAFW,QAAA,iBAAiB,qBAE5B"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@purpleproser/soundboard-downloader-cli",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Easily download sounds from myinstants.com using this interactive CLI tool",
|
|
3
|
+
"version": "1.7.0",
|
|
4
|
+
"description": "Easily download sounds from myinstants.com using this interactive CLI tool (for personal, non-commercial use only)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Score report for "pkg:npm/@purpleproser/soundboard-downloader-cli" (use --json for raw and --markdown for formatted reports):
|
|
2
|
+
{
|
|
3
|
+
purl: 'pkg:npm/@purpleproser/soundboard-downloader-cli',
|
|
4
|
+
self: {
|
|
5
|
+
purl: 'npm/@purpleproser/soundboard-downloader-cli@1.6.3',
|
|
6
|
+
score: {
|
|
7
|
+
license: 100,
|
|
8
|
+
maintenance: 93,
|
|
9
|
+
overall: 73,
|
|
10
|
+
quality: 100,
|
|
11
|
+
supplyChain: 73,
|
|
12
|
+
vulnerability: 100
|
|
13
|
+
},
|
|
14
|
+
capabilities: [ 'fs', 'net', 'url' ],
|
|
15
|
+
alerts: [ [Object], [Object], [Object] ]
|
|
16
|
+
},
|
|
17
|
+
transitively: {
|
|
18
|
+
dependencyCount: 67,
|
|
19
|
+
func: 'min',
|
|
20
|
+
score: {
|
|
21
|
+
license: 100,
|
|
22
|
+
maintenance: 75,
|
|
23
|
+
overall: 65,
|
|
24
|
+
quality: 65,
|
|
25
|
+
supplyChain: 71,
|
|
26
|
+
vulnerability: 100
|
|
27
|
+
},
|
|
28
|
+
lowest: {
|
|
29
|
+
license: 'npm/prompts@2.4.2',
|
|
30
|
+
maintenance: 'npm/symbol-tree@3.2.4',
|
|
31
|
+
overall: 'npm/restore-cursor@5.1.0',
|
|
32
|
+
quality: 'npm/restore-cursor@5.1.0',
|
|
33
|
+
supplyChain: 'npm/undici@7.22.0',
|
|
34
|
+
vulnerability: 'npm/prompts@2.4.2'
|
|
35
|
+
},
|
|
36
|
+
capabilities: [
|
|
37
|
+
'env', 'eval',
|
|
38
|
+
'fs', 'net',
|
|
39
|
+
'shell', 'unsafe',
|
|
40
|
+
'url'
|
|
41
|
+
],
|
|
42
|
+
alerts: [
|
|
43
|
+
[Object], [Object],
|
|
44
|
+
[Object], [Object],
|
|
45
|
+
[Object], [Object],
|
|
46
|
+
[Object], [Object],
|
|
47
|
+
[Object], [Object],
|
|
48
|
+
[Object], [Object],
|
|
49
|
+
[Object], [Object]
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|