@vacantthinker/firefox-addon-framework-easy 2026.526.2259 → 2026.527.2043
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/README.md +14 -0
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/browserDownload.js +11 -0
- package/src/generate.js +188 -10
- package/src/opTab.js +19 -3
- package/src/serviceOpContent.js +28 -2
package/README.md
CHANGED
|
@@ -23,6 +23,12 @@ export class BaseORM { }
|
|
|
23
23
|
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
### 📄 File: `src/browserDownload.js`
|
|
27
|
+
```javascript
|
|
28
|
+
export async function browserDownloadByDownlink({ }
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
|
|
26
32
|
### 📄 File: `src/browserNotification.js`
|
|
27
33
|
```javascript
|
|
28
34
|
export async function browserNotificationCreate(
|
|
@@ -59,6 +65,10 @@ export function generateHtmlByUserSettings(
|
|
|
59
65
|
radioItemClickCallback,
|
|
60
66
|
) { }
|
|
61
67
|
|
|
68
|
+
export function generateMkvScriptForSystemWindows({ }
|
|
69
|
+
|
|
70
|
+
export function generateMkvScriptForSystemFedora({ }
|
|
71
|
+
|
|
62
72
|
```
|
|
63
73
|
|
|
64
74
|
### 📄 File: `src/opStorage.js`
|
|
@@ -95,6 +105,8 @@ export async function tabOpCreate(urlOrArgs) { }
|
|
|
95
105
|
|
|
96
106
|
export async function tabOpCreateNormal(urlOrArgs) { }
|
|
97
107
|
|
|
108
|
+
export async function tabOpCreateByWindow(url) { }
|
|
109
|
+
|
|
98
110
|
export async function tabOpRemove(tabId) { }
|
|
99
111
|
|
|
100
112
|
export async function tabOpHide(tabId) { }
|
|
@@ -149,6 +161,8 @@ export async function serviceCopyContentToClipboard(data) { }
|
|
|
149
161
|
|
|
150
162
|
export function serviceSaveContentToLocal(content, filename, ext = 'txt') { }
|
|
151
163
|
|
|
164
|
+
export async function serviceGenerateMkvToolNixScript({ }
|
|
165
|
+
|
|
152
166
|
export function serviceRemoveIllegalWord(value) { }
|
|
153
167
|
|
|
154
168
|
```
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* @param param0
|
|
4
|
+
* @param param0.downlink{string}
|
|
5
|
+
* @param param0.filename{string}
|
|
6
|
+
* @returns {Promise<void>}
|
|
7
|
+
*/
|
|
8
|
+
export async function browserDownloadByDownlink({ downlink, filename }) {
|
|
9
|
+
let url = downlink;
|
|
10
|
+
await browser.downloads.download({url, filename});
|
|
11
|
+
}
|
package/src/generate.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {stoOpGet, stoOpSet} from './opStorage.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Generates HTML elements based on a user settings schema object.
|
|
@@ -51,17 +51,20 @@ export function generateHtmlByUserSettings(
|
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
eleInput.addEventListener('change', async () => {
|
|
54
|
-
const optionsCurrent = await stoOpGet(storageKey) ||
|
|
54
|
+
const optionsCurrent = await stoOpGet(storageKey) ||
|
|
55
|
+
storageValue.selected || [];
|
|
55
56
|
const set = new Set(Array.from(optionsCurrent));
|
|
56
57
|
|
|
57
58
|
if (eleInput.checked) {
|
|
58
59
|
set.add(option);
|
|
59
|
-
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
60
62
|
set.delete(option);
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
const valueNew = Array.from(set);
|
|
64
|
-
console.info(
|
|
66
|
+
console.info(
|
|
67
|
+
`k=${storageKey} option=${option} eleInput.checked=${eleInput.checked} valueNew=${valueNew}`);
|
|
65
68
|
await stoOpSet(storageKey, valueNew);
|
|
66
69
|
|
|
67
70
|
// Dynamic visibility update
|
|
@@ -70,7 +73,9 @@ export function generateHtmlByUserSettings(
|
|
|
70
73
|
}
|
|
71
74
|
else if (type === 'radio') {
|
|
72
75
|
stoOpGet(storageKey).then((v) => {
|
|
73
|
-
const currentSelected = (v !== undefined && v !== null) ?
|
|
76
|
+
const currentSelected = (v !== undefined && v !== null) ?
|
|
77
|
+
v :
|
|
78
|
+
storageValue.selected;
|
|
74
79
|
if (option === currentSelected) {
|
|
75
80
|
eleInput.checked = true;
|
|
76
81
|
}
|
|
@@ -79,7 +84,7 @@ export function generateHtmlByUserSettings(
|
|
|
79
84
|
triggerVisibility(storageKey, currentSelected);
|
|
80
85
|
});
|
|
81
86
|
|
|
82
|
-
eleLabel.onclick = function
|
|
87
|
+
eleLabel.onclick = function() {
|
|
83
88
|
stoOpSet(storageKey, option).then(() => {
|
|
84
89
|
console.info(`k=${storageKey} option=${option}`);
|
|
85
90
|
if (typeof radioItemClickCallback === 'function') {
|
|
@@ -104,7 +109,9 @@ export function generateHtmlByUserSettings(
|
|
|
104
109
|
|
|
105
110
|
stoOpGet(storageKey).then((v) => {
|
|
106
111
|
// Fallback to default schema configuration if no value is stored yet
|
|
107
|
-
let currentStatus = (v !== undefined && v !== null) ?
|
|
112
|
+
let currentStatus = (v !== undefined && v !== null) ?
|
|
113
|
+
(v === true || v === 'true') :
|
|
114
|
+
storageValue.selected;
|
|
108
115
|
eleButton.textContent = String(currentStatus);
|
|
109
116
|
|
|
110
117
|
// Initial visibility evaluation
|
|
@@ -131,7 +138,9 @@ export function generateHtmlByUserSettings(
|
|
|
131
138
|
eleInput.name = storageKey;
|
|
132
139
|
|
|
133
140
|
stoOpGet(storageKey).then((v) => {
|
|
134
|
-
const currentVal = (v !== undefined && v !== null) ?
|
|
141
|
+
const currentVal = (v !== undefined && v !== null) ?
|
|
142
|
+
v :
|
|
143
|
+
storageValue.selected;
|
|
135
144
|
eleInput.value = currentVal;
|
|
136
145
|
|
|
137
146
|
// Initial visibility evaluation
|
|
@@ -162,7 +171,7 @@ export function generateHtmlByUserSettings(
|
|
|
162
171
|
function triggerVisibility(sourceKey, currentValue) {
|
|
163
172
|
const config = userSettings[sourceKey];
|
|
164
173
|
if (config && config.visibilityControl) {
|
|
165
|
-
const {
|
|
174
|
+
const {targetField, expectedValue} = config.visibilityControl;
|
|
166
175
|
const targetElement = elementsMap[targetField];
|
|
167
176
|
|
|
168
177
|
if (targetElement) {
|
|
@@ -174,4 +183,173 @@ export function generateHtmlByUserSettings(
|
|
|
174
183
|
}
|
|
175
184
|
|
|
176
185
|
return fieldsets;
|
|
177
|
-
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function generateMkvScriptForSystemWindows({vid, title}) {
|
|
189
|
+
let args = {vid, title};
|
|
190
|
+
|
|
191
|
+
return `if (true) {
|
|
192
|
+
const path = require('path');
|
|
193
|
+
const fs = require('fs');
|
|
194
|
+
const {execSync, exec} = require('node:child_process');
|
|
195
|
+
let pathDownload = path.join(__dirname);
|
|
196
|
+
let dot = '.';
|
|
197
|
+
let extMKV = 'mkv';
|
|
198
|
+
|
|
199
|
+
let {vid, title} = ${JSON.stringify(args)};
|
|
200
|
+
let playVideoAfterMerged = true;
|
|
201
|
+
let pathToMkvmerge = 'C:\\\\Program Files\\\\MKVToolNix\\\\mkvmerge.exe';
|
|
202
|
+
|
|
203
|
+
let pathMKVOutput = path.join(pathDownload, title.concat(dot, extMKV));
|
|
204
|
+
let pathOutput = path.join(pathDownload, vid.concat(dot, extMKV));
|
|
205
|
+
let pathInputAudio = path.join(pathDownload, vid.concat(dot, "mp3"));
|
|
206
|
+
let pathInputVideo = path.join(pathDownload, vid.concat(dot, "mp4"));
|
|
207
|
+
let pathJsFile = path.join(__filename);
|
|
208
|
+
|
|
209
|
+
if (
|
|
210
|
+
fs.existsSync(pathToMkvmerge)
|
|
211
|
+
) {
|
|
212
|
+
|
|
213
|
+
console.log('');
|
|
214
|
+
console.log(['file check ok!', title].join(' '));
|
|
215
|
+
|
|
216
|
+
let cmd_merge = [
|
|
217
|
+
[pathToMkvmerge].map(v => '"' + v + '"').join(''),
|
|
218
|
+
|
|
219
|
+
'-o',
|
|
220
|
+
[pathOutput]
|
|
221
|
+
.map(value => '"' + value + '"')
|
|
222
|
+
.join(' '),
|
|
223
|
+
|
|
224
|
+
'--no-video',
|
|
225
|
+
[pathInputAudio]
|
|
226
|
+
.map(value => '"' + value + '"')
|
|
227
|
+
.join(' '),
|
|
228
|
+
|
|
229
|
+
'--no-audio',
|
|
230
|
+
[pathInputVideo]
|
|
231
|
+
.map(value => '"' + value + '"')
|
|
232
|
+
.join(' '),
|
|
233
|
+
].join(' ');
|
|
234
|
+
|
|
235
|
+
console.log('execute script merge ...')
|
|
236
|
+
console.log(cmd_merge);
|
|
237
|
+
|
|
238
|
+
let exec_merge = exec(cmd_merge);
|
|
239
|
+
exec_merge.stdout.on('data', (data) => {
|
|
240
|
+
console.log(data);
|
|
241
|
+
});
|
|
242
|
+
exec_merge.stderr.on('data', (data) => {
|
|
243
|
+
console.log('error', data);
|
|
244
|
+
});
|
|
245
|
+
exec_merge.stdout.on('close', (data) => {
|
|
246
|
+
console.log(['merge finish!', title].join(' '));
|
|
247
|
+
|
|
248
|
+
if (true) {
|
|
249
|
+
console.log('remove inputFile');
|
|
250
|
+
fs.unlinkSync(pathInputVideo);
|
|
251
|
+
fs.unlinkSync(pathInputAudio);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
console.log('rename output mkv video...');
|
|
255
|
+
fs.renameSync(pathOutput, pathMKVOutput);
|
|
256
|
+
|
|
257
|
+
if (playVideoAfterMerged) {
|
|
258
|
+
console.log('play mkv video...');
|
|
259
|
+
execSync('"' + pathMKVOutput + '"');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
if (true) {
|
|
263
|
+
console.log('remove script file');
|
|
264
|
+
fs.unlinkSync(pathJsFile);
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
} else {
|
|
269
|
+
console.log('pathToMkvmerge error')
|
|
270
|
+
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
`;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
export function generateMkvScriptForSystemFedora({vid, title}) {
|
|
277
|
+
let args = {vid, title};
|
|
278
|
+
|
|
279
|
+
// We wrap the Node.js script inside a Linux Shell script block
|
|
280
|
+
return `#!/usr/bin/env bash
|
|
281
|
+
# This header tells Fedora to treat this file as a runnable bash script
|
|
282
|
+
|
|
283
|
+
# Open a terminal window if not already running inside one
|
|
284
|
+
if [ -z "$VTE_VERSION" ] && [ -z "$ALACRITTY_WINDOW_ID" ] && [ -z "$KITTY_WINDOW_ID" ] && [ "$1" != "--child" ]; then
|
|
285
|
+
# Re-run this same script inside a visible terminal window so the user sees progress
|
|
286
|
+
gnome-terminal -- "$0" --child
|
|
287
|
+
exit 0
|
|
288
|
+
fi
|
|
289
|
+
|
|
290
|
+
# Run the embedded Node.js code below
|
|
291
|
+
node << 'EOF'
|
|
292
|
+
const path = require('path');
|
|
293
|
+
const fs = require('fs');
|
|
294
|
+
const { execSync } = require('node:child_process');
|
|
295
|
+
|
|
296
|
+
let pathDownload = path.join(__dirname);
|
|
297
|
+
let dot = '.';
|
|
298
|
+
let extMKV = 'mkv';
|
|
299
|
+
|
|
300
|
+
let {vid, title} = ${JSON.stringify(args)};
|
|
301
|
+
let playVideoAfterMerged = true;
|
|
302
|
+
let pathToMkvmerge = '/usr/bin/mkvmerge';
|
|
303
|
+
|
|
304
|
+
let pathMKVOutput = path.join(pathDownload, title.concat(dot, extMKV));
|
|
305
|
+
let pathOutput = path.join(pathDownload, vid.concat(dot, extMKV));
|
|
306
|
+
let pathInputAudio = path.join(pathDownload, vid.concat(dot, "mp3"));
|
|
307
|
+
let pathInputVideo = path.join(pathDownload, vid.concat(dot, "mp4"));
|
|
308
|
+
|
|
309
|
+
if (fs.existsSync(pathToMkvmerge)) {
|
|
310
|
+
console.log('\\nfile check ok! ' + title);
|
|
311
|
+
|
|
312
|
+
let cmd_merge = [
|
|
313
|
+
'"' + pathToMkvmerge + '"',
|
|
314
|
+
'-o', '"' + pathOutput + '"',
|
|
315
|
+
'--no-video', '"' + pathInputAudio + '"',
|
|
316
|
+
'--no-audio', '"' + pathInputVideo + '"',
|
|
317
|
+
].join(' ');
|
|
318
|
+
|
|
319
|
+
console.log('execute script merge ...\\n' + cmd_merge);
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
// Run merge synchronously so stdout pipes directly to the terminal
|
|
323
|
+
execSync(cmd_merge, { stdio: 'inherit' });
|
|
324
|
+
console.log('merge finish! ' + title);
|
|
325
|
+
|
|
326
|
+
console.log('remove inputFile');
|
|
327
|
+
if (fs.existsSync(pathInputVideo)) fs.unlinkSync(pathInputVideo);
|
|
328
|
+
if (fs.existsSync(pathInputAudio)) fs.unlinkSync(pathInputAudio);
|
|
329
|
+
|
|
330
|
+
console.log('rename output mkv video...');
|
|
331
|
+
fs.renameSync(pathOutput, pathMKVOutput);
|
|
332
|
+
|
|
333
|
+
if (playVideoAfterMerged) {
|
|
334
|
+
console.log('play mkv video...');
|
|
335
|
+
// xdg-open usually hands off to the player and exits immediately
|
|
336
|
+
execSync('xdg-open "' + pathMKVOutput + '"');
|
|
337
|
+
}
|
|
338
|
+
} catch (err) {
|
|
339
|
+
console.error('An error occurred during processing:', err.message);
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
console.log('pathToMkvmerge error. run: sudo dnf install mkvtoolnix')
|
|
343
|
+
}
|
|
344
|
+
EOF
|
|
345
|
+
|
|
346
|
+
# Prevent the terminal window from closing instantly so the user can read messages
|
|
347
|
+
echo ""
|
|
348
|
+
echo "Press Enter to exit and automatically delete this script..."
|
|
349
|
+
read unused_input
|
|
350
|
+
|
|
351
|
+
# Deletes the shell script file itself after completion
|
|
352
|
+
rm -- "$0"
|
|
353
|
+
`;
|
|
354
|
+
}
|
|
355
|
+
|
package/src/opTab.js
CHANGED
|
@@ -51,7 +51,6 @@ export async function tabOpReload(tabId) {
|
|
|
51
51
|
* @returns {Promise<(browser.tabs.Tab & {tabId: number})>}
|
|
52
52
|
*/
|
|
53
53
|
export async function tabOpCreate(urlOrArgs) {
|
|
54
|
-
try {
|
|
55
54
|
/**
|
|
56
55
|
* @type {browser.tabs._CreateCreateProperties}
|
|
57
56
|
*/
|
|
@@ -66,8 +65,6 @@ export async function tabOpCreate(urlOrArgs) {
|
|
|
66
65
|
Object.assign(urlOrArgs, source);
|
|
67
66
|
let tab = await browser.tabs.create(urlOrArgs);
|
|
68
67
|
return tabOpEnhance(tab)
|
|
69
|
-
} catch (e) {
|
|
70
|
-
}
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
/**
|
|
@@ -88,6 +85,25 @@ export async function tabOpCreateNormal(urlOrArgs) {
|
|
|
88
85
|
return tabOpEnhance(tab)
|
|
89
86
|
}
|
|
90
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Creates a normal tab using either a URL string or a properties object.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} url
|
|
92
|
+
* @returns {Promise<(browser.tabs.Tab & {tabId: number})>}
|
|
93
|
+
*/
|
|
94
|
+
export async function tabOpCreateByWindow(url) {
|
|
95
|
+
// If it's a string, wrap it in an object
|
|
96
|
+
if (typeof url === 'string') {
|
|
97
|
+
let window = await browser.windows.create({
|
|
98
|
+
url
|
|
99
|
+
});
|
|
100
|
+
let tab = window.tabs.shift();
|
|
101
|
+
return tabOpEnhance(tab)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
91
107
|
/**
|
|
92
108
|
*
|
|
93
109
|
* @param tabId{number}
|
package/src/serviceOpContent.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {browserRuntimePlatformInfo} from './browserRuntime.js';
|
|
2
|
+
import {
|
|
3
|
+
generateMkvScriptForSystemFedora,
|
|
4
|
+
generateMkvScriptForSystemWindows,
|
|
5
|
+
} from './generate.js';
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
8
|
*
|
|
3
9
|
* @param data
|
|
@@ -22,8 +28,8 @@ export function serviceSaveContentToLocal(content, filename, ext = 'txt') {
|
|
|
22
28
|
|
|
23
29
|
const extObj = {
|
|
24
30
|
txt: 'text/plain',
|
|
25
|
-
json: 'application/json'
|
|
26
|
-
}
|
|
31
|
+
json: 'application/json',
|
|
32
|
+
};
|
|
27
33
|
const type = extObj[ext];
|
|
28
34
|
|
|
29
35
|
const file = new Blob([content], {type});
|
|
@@ -38,6 +44,26 @@ export function serviceSaveContentToLocal(content, filename, ext = 'txt') {
|
|
|
38
44
|
// eleBtn.previousElementSibling
|
|
39
45
|
}
|
|
40
46
|
|
|
47
|
+
/**
|
|
48
|
+
* need install nodejs mkvtoolnix
|
|
49
|
+
* @param message{{
|
|
50
|
+
* vid, title,
|
|
51
|
+
* }}
|
|
52
|
+
* @returns {Promise<void>}
|
|
53
|
+
*/
|
|
54
|
+
export async function serviceGenerateMkvToolNixScript({vid, title}) {
|
|
55
|
+
let message = {vid, title};
|
|
56
|
+
const platformInfo = await browserRuntimePlatformInfo();
|
|
57
|
+
if (platformInfo.os === 'win') {
|
|
58
|
+
let content = generateMkvScriptForSystemWindows(message);
|
|
59
|
+
serviceSaveContentToLocal(content, title, 'js');
|
|
60
|
+
}
|
|
61
|
+
else if (platformInfo.os === 'linux') {
|
|
62
|
+
let content = generateMkvScriptForSystemFedora(message);
|
|
63
|
+
serviceSaveContentToLocal(content, title, 'sh');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
/**
|
|
42
68
|
*
|
|
43
69
|
* @param {string} value
|