media:salamand user menu | explorer ext 1/2
@ -48,6 +48,10 @@ export const defaultOptions = (yargs) => {
|
||||
describe: 'Preserve alpha channel from input image',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
}).option('jpg', {
|
||||
describe: 'Convert PNG output to JPG format and delete PNG',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
});
|
||||
};
|
||||
export const command = 'background:remove:bria';
|
||||
@ -69,6 +73,7 @@ export async function handler(argv) {
|
||||
options.sync = argv.sync;
|
||||
options.contentModeration = argv.contentModeration;
|
||||
options.preserveAlpha = argv.preserveAlpha;
|
||||
options.jpg = argv.jpg;
|
||||
logger.info("Removing background with Bria AI options:", {
|
||||
sync: options.sync,
|
||||
contentModeration: options.contentModeration,
|
||||
@ -78,4 +83,4 @@ export async function handler(argv) {
|
||||
await briaBackgroundRemove(options);
|
||||
}
|
||||
cli.command(command, desc, builder, handler);
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja2dyb3VuZC1yZW1vdmUtYnJpYS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb21tYW5kcy9iYWNrZ3JvdW5kLXJlbW92ZS1icmlhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUNsRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFDL0IsT0FBTyxFQUNILFFBQVEsRUFDUixRQUFRLEVBQ1gsTUFBTSxZQUFZLENBQUE7QUFFbkIsT0FBTyxFQUNILG9CQUFvQixFQUV2QixNQUFNLCtDQUErQyxDQUFBO0FBRXRELE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUFDLEtBQWUsRUFBRSxFQUFFO0lBQzlDLE9BQU8sS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDdkIsUUFBUSxFQUFFLGtCQUFrQjtRQUM1QixZQUFZLEVBQUUsSUFBSTtLQUNyQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLFFBQVEsRUFBRSxrQkFBa0I7S0FDL0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUU7UUFDZixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSxnQ0FBZ0M7UUFDMUMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx5Q0FBeUM7UUFDbkQsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx3QkFBd0I7UUFDbEMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDakIs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
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja2dyb3VuZC1yZW1vdmUtYnJpYS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb21tYW5kcy9iYWNrZ3JvdW5kLXJlbW92ZS1icmlhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUNsRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sYUFBYSxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFDL0IsT0FBTyxFQUNILFFBQVEsRUFDUixRQUFRLEVBQ1gsTUFBTSxZQUFZLENBQUE7QUFFbkIsT0FBTyxFQUNILG9CQUFvQixFQUV2QixNQUFNLCtDQUErQyxDQUFBO0FBRXRELE1BQU0sQ0FBQyxNQUFNLGNBQWMsR0FBRyxDQUFDLEtBQWUsRUFBRSxFQUFFO0lBQzlDLE9BQU8sS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDdkIsUUFBUSxFQUFFLGtCQUFrQjtRQUM1QixZQUFZLEVBQUUsSUFBSTtLQUNyQixDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRTtRQUNiLFFBQVEsRUFBRSxrQkFBa0I7S0FDL0IsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLEVBQUU7UUFDZixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSxnQ0FBZ0M7UUFDMUMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx5Q0FBeUM7UUFDbkQsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUU7UUFDYixPQUFPLEVBQUUsS0FBSztRQUNkLFFBQVEsRUFBRSx3QkFBd0I7UUFDbEMsSUFBSSxFQUFFLFNBQVM7S0FDbEIsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxTQUFTLEVBQUU7UUFDakIs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
|
||||
6
packages/media/dist-in/commands/register-commands.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import * as CLI from 'yargs';
|
||||
export declare const defaultOptions: (yargs: CLI.Argv) => any;
|
||||
export declare const command = "register-commands";
|
||||
export declare const desc = "Register all pm-media commands in Salamander menu";
|
||||
export declare const builder: (yargs: CLI.Argv) => any;
|
||||
export declare function handler(argv: CLI.Arguments): Promise<void>;
|
||||
218
packages/media/dist-in/commands/register-commands.js
Normal file
6
packages/media/dist-in/commands/register-explorer.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import * as CLI from 'yargs';
|
||||
export declare const defaultOptions: (yargs: CLI.Argv) => any;
|
||||
export declare const command = "register-explorer";
|
||||
export declare const desc = "Register pm-media commands in Windows Explorer context menu for image files";
|
||||
export declare const builder: (yargs: CLI.Argv) => any;
|
||||
export declare function handler(argv: CLI.Arguments): Promise<void>;
|
||||
204
packages/media/dist-in/commands/register-explorer.js
Normal file
6
packages/media/dist-in/commands/salamander.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
import * as CLI from 'yargs';
|
||||
export declare const defaultOptions: (yargs: CLI.Argv) => any;
|
||||
export declare const command = "salamander";
|
||||
export declare const desc = "Generate Salamander file manager menu entries from JSON configuration";
|
||||
export declare const builder: (yargs: CLI.Argv) => any;
|
||||
export declare function handler(argv: CLI.Arguments): Promise<void>;
|
||||
247
packages/media/dist-in/commands/salamander.js
Normal file
@ -4,6 +4,7 @@ export interface BriaBackgroundRemoveOptions extends IOptions {
|
||||
sync?: boolean;
|
||||
contentModeration?: boolean;
|
||||
preserveAlpha?: boolean;
|
||||
jpg?: boolean;
|
||||
}
|
||||
export declare function removeBriaBackground(inputPath: string, outputPath: string, options: BriaBackgroundRemoveOptions): Promise<void>;
|
||||
export declare const briaBackgroundRemove: (options: BriaBackgroundRemoveOptions) => Promise<{
|
||||
|
||||
130
packages/media/dist-in/lib/salamander/index.d.ts
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
export interface SalamanderMenuItem {
|
||||
name: string;
|
||||
command?: string;
|
||||
arguments?: string;
|
||||
initialDirectory?: string;
|
||||
executeThoughShell?: boolean;
|
||||
closeShellWindow?: boolean;
|
||||
openShellWindow?: boolean;
|
||||
icon?: string;
|
||||
showInToolbar?: boolean;
|
||||
type?: 'command' | 'submenu' | 'submenu-end';
|
||||
children?: SalamanderMenuItem[];
|
||||
}
|
||||
export interface SalamanderMenuConfig {
|
||||
baseKey: string;
|
||||
startIndex: number;
|
||||
items: SalamanderMenuItem[];
|
||||
}
|
||||
export interface RegistryEntry {
|
||||
key: string;
|
||||
values: Record<string, string | number>;
|
||||
}
|
||||
export declare class SalamanderMenuGenerator {
|
||||
private config;
|
||||
private baseKey;
|
||||
constructor(config: SalamanderMenuConfig);
|
||||
/**
|
||||
* Parse existing registry file to find the highest menu index
|
||||
*/
|
||||
static parseExistingRegistry(registryPath: string): number;
|
||||
/**
|
||||
* Generate registry entries from menu configuration
|
||||
*/
|
||||
generateRegistryEntries(): RegistryEntry[];
|
||||
/**
|
||||
* Convert menu item to registry values
|
||||
*/
|
||||
private itemToRegistryValues;
|
||||
/**
|
||||
* Escape arguments for registry format
|
||||
*/
|
||||
private escapeArguments;
|
||||
/**
|
||||
* Generate registry file content
|
||||
*/
|
||||
generateRegistryFile(): string;
|
||||
/**
|
||||
* Save registry file to disk
|
||||
*/
|
||||
saveRegistryFile(outputPath: string): void;
|
||||
/**
|
||||
* Load menu configuration from JSON file
|
||||
*/
|
||||
static loadFromJson(jsonPath: string): SalamanderMenuConfig;
|
||||
/**
|
||||
* Find insertion point in existing registry
|
||||
*/
|
||||
static findInsertionPoint(registryPath: string, groupName?: string): number;
|
||||
}
|
||||
/**
|
||||
* Windows Registry helper for direct registry operations using regedit package
|
||||
*/
|
||||
export declare class WindowsRegistry {
|
||||
private static isWindows;
|
||||
private static ensureRegedit;
|
||||
/**
|
||||
* Read all User Menu entries from registry
|
||||
*/
|
||||
static readUserMenuEntries(baseKey?: string): Promise<Record<string, any>>;
|
||||
/**
|
||||
* Read specific menu entry by index
|
||||
*/
|
||||
static readMenuEntry(index: number, baseKey?: string): Promise<Record<string, any>>;
|
||||
/**
|
||||
* Get all existing menu indices
|
||||
*/
|
||||
static getExistingMenuIndices(baseKey?: string): Promise<number[]>;
|
||||
/**
|
||||
* Get next available menu index
|
||||
*/
|
||||
static getNextMenuIndex(baseKey?: string): Promise<number>;
|
||||
/**
|
||||
* Find insertion point for a specific group
|
||||
*/
|
||||
static findGroupInsertionPoint(groupName: string, baseKey?: string): Promise<number>;
|
||||
/**
|
||||
* Write registry values for a menu entry
|
||||
*/
|
||||
static writeMenuEntry(index: number, values: Record<string, any>, baseKey?: string): Promise<void>;
|
||||
/**
|
||||
* Delete a menu entry
|
||||
*/
|
||||
static deleteMenuEntry(index: number, baseKey?: string): Promise<void>;
|
||||
/**
|
||||
* List all menu entries with their details
|
||||
*/
|
||||
static listAllMenuEntries(baseKey?: string): Promise<Array<{
|
||||
index: number;
|
||||
name: string;
|
||||
type: string;
|
||||
command?: string;
|
||||
}>>;
|
||||
}
|
||||
/**
|
||||
* Extended Salamander Menu Generator with direct registry support
|
||||
*/
|
||||
export declare class SalamanderMenuGeneratorRegistry extends SalamanderMenuGenerator {
|
||||
/**
|
||||
* Apply menu configuration directly to Windows registry
|
||||
*/
|
||||
applyToRegistry(): Promise<void>;
|
||||
/**
|
||||
* Remove menu entries from Windows registry
|
||||
*/
|
||||
removeFromRegistry(): Promise<void>;
|
||||
/**
|
||||
* Auto-detect insertion point from Windows registry
|
||||
*/
|
||||
static autoDetectInsertionPoint(groupName?: string): Promise<number>;
|
||||
/**
|
||||
* List current menu entries from registry
|
||||
*/
|
||||
static listCurrentMenuEntries(): Promise<Array<{
|
||||
index: number;
|
||||
name: string;
|
||||
type: string;
|
||||
command?: string;
|
||||
}>>;
|
||||
}
|
||||
export default SalamanderMenuGenerator;
|
||||
403
packages/media/dist-in/lib/salamander/index.js
Normal file
3
packages/media/dist-in/main.d.ts
vendored
@ -5,3 +5,6 @@ import './commands/watermark.js';
|
||||
import './commands/background-remove.js';
|
||||
import './commands/background-remove-bria.js';
|
||||
import './commands/crop-foreground.js';
|
||||
import './commands/salamander.js';
|
||||
import './commands/register-commands.js';
|
||||
import './commands/register-explorer.js';
|
||||
|
||||
@ -8,6 +8,9 @@ import './commands/watermark.js';
|
||||
import './commands/background-remove.js';
|
||||
import './commands/background-remove-bria.js';
|
||||
import './commands/crop-foreground.js';
|
||||
import './commands/salamander.js';
|
||||
import './commands/register-commands.js';
|
||||
import './commands/register-explorer.js';
|
||||
const argv = cli.argv;
|
||||
if (argv.h || argv.help) {
|
||||
cli.showHelp();
|
||||
@ -16,4 +19,4 @@ if (argv.h || argv.help) {
|
||||
else if (argv.v || argv.version) {
|
||||
process.exit();
|
||||
}
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9tYWluLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQUMsUUFBUSxFQUFFLENBQUE7QUFFaEQsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUU5QixPQUFPLHNCQUFzQixDQUFBO0FBQzdCLE9BQU8sdUJBQXVCLENBQUE7QUFDOUIsT0FBTyx5QkFBeUIsQ0FBQTtBQUNoQyxPQUFPLGlDQUFpQyxDQUFBO0FBQ3hDLE9BQU8sc0NBQXNDLENBQUE7QUFDN0MsT0FBTywrQkFBK0IsQ0FBQTtBQUV0QyxNQUFNLElBQUksR0FBUSxHQUFHLENBQUMsSUFBSSxDQUFDO0FBRTNCLElBQUksSUFBSSxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDdEIsR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ2YsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ25CLENBQUM7S0FBTSxJQUFJLElBQUksQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2hDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUNuQixDQUFDIn0=
|
||||
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9tYWluLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQUMsUUFBUSxFQUFFLENBQUE7QUFFaEQsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUU5QixPQUFPLHNCQUFzQixDQUFBO0FBQzdCLE9BQU8sdUJBQXVCLENBQUE7QUFDOUIsT0FBTyx5QkFBeUIsQ0FBQTtBQUNoQyxPQUFPLGlDQUFpQyxDQUFBO0FBQ3hDLE9BQU8sc0NBQXNDLENBQUE7QUFDN0MsT0FBTywrQkFBK0IsQ0FBQTtBQUN0QyxPQUFPLDBCQUEwQixDQUFBO0FBQ2pDLE9BQU8saUNBQWlDLENBQUE7QUFDeEMsT0FBTyxpQ0FBaUMsQ0FBQTtBQUV4QyxNQUFNLElBQUksR0FBUSxHQUFHLENBQUMsSUFBSSxDQUFDO0FBRTNCLElBQUksSUFBSSxDQUFDLENBQUMsSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDdEIsR0FBRyxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQ2YsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ25CLENBQUM7S0FBTSxJQUFJLElBQUksQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2hDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUNuQixDQUFDIn0=
|
||||
52
packages/media/integration/salamander-menu-sample.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"baseKey": "HKEY_CURRENT_USER\\Software\\Altap\\Altap Salamander 4.0\\User Menu",
|
||||
"startIndex": 5,
|
||||
"items": [
|
||||
{
|
||||
"name": "Background Remove (Bria)",
|
||||
"command": "pm-media",
|
||||
"arguments": "background:remove:bria --alt=true --logLevel=info --src=\\\"$(FullName)/**/*.+(&{IMAGES})\\\" --dst=\\\"&{SRC_DIR}/&{SRC_NAME}_nobg.png\\\" --jpg",
|
||||
"initialDirectory": "$(FullPath)",
|
||||
"executeThoughShell": true,
|
||||
"closeShellWindow": false,
|
||||
"openShellWindow": true,
|
||||
"icon": "",
|
||||
"showInToolbar": true
|
||||
},
|
||||
{
|
||||
"name": "Convert Tools",
|
||||
"children": [
|
||||
{
|
||||
"name": "To WebP",
|
||||
"command": "pm-media",
|
||||
"arguments": "convert --alt=true --logLevel=info --src=\\\"$(FullName)/**/*.+(&{IMAGES})\\\" --dst=\\\"&{SRC_DIR}/&{SRC_NAME}.webp\\\"",
|
||||
"initialDirectory": "$(FullPath)",
|
||||
"executeThoughShell": true,
|
||||
"closeShellWindow": true,
|
||||
"openShellWindow": true,
|
||||
"showInToolbar": true
|
||||
},
|
||||
{
|
||||
"name": "To PNG",
|
||||
"command": "pm-media",
|
||||
"arguments": "convert --alt=true --logLevel=info --src=\\\"$(FullName)/**/*.+(&{IMAGES})\\\" --dst=\\\"&{SRC_DIR}/&{SRC_NAME}.png\\\"",
|
||||
"initialDirectory": "$(FullPath)",
|
||||
"executeThoughShell": true,
|
||||
"closeShellWindow": true,
|
||||
"openShellWindow": true,
|
||||
"showInToolbar": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "PDF to Images",
|
||||
"command": "pm-media",
|
||||
"arguments": "pdf2jpg --alt=true --logLevel=info --src=\\\"$(FullName)\\\" --dst=\\\"&{SRC_DIR}/&{SRC_NAME}\\\"",
|
||||
"initialDirectory": "$(FullPath)",
|
||||
"executeThoughShell": true,
|
||||
"closeShellWindow": false,
|
||||
"openShellWindow": true,
|
||||
"showInToolbar": true
|
||||
}
|
||||
]
|
||||
}
|
||||
118
packages/media/package-lock.json
generated
@ -25,6 +25,7 @@
|
||||
"mupdf": "^1.3.3",
|
||||
"novita-sdk": "^1.0.37",
|
||||
"p-map": "^7.0.3",
|
||||
"regedit": "^5.1.4",
|
||||
"replicate": "^1.0.1",
|
||||
"sharp": "^0.34.2",
|
||||
"tslog": "^4.9.3",
|
||||
@ -2207,7 +2208,6 @@
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@ -3205,6 +3205,12 @@
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/if-async": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/if-async/-/if-async-3.7.4.tgz",
|
||||
"integrity": "sha512-BFEH2mZyeF6KZKaKLVPZ0wMjIiWOdjvZ7zbx8ENec0qfZhJwKFbX/4jKM5LTKyJEc/GOqUKiiJ2IFKT9yWrZqA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@ -3615,7 +3621,6 @@
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mupdf": {
|
||||
@ -4011,6 +4016,18 @@
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/regedit": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/regedit/-/regedit-5.1.4.tgz",
|
||||
"integrity": "sha512-3VQ8BY2unUdl4nSx19QAn+pUlkqJhRbIsQc0zciWIVmELLXQLIHvdytUfcI56XYJZj3r9rkb7NTKxkQaXSYnow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.0",
|
||||
"if-async": "^3.7.4",
|
||||
"stream-slicer": "0.0.6",
|
||||
"through2": "^0.6.3"
|
||||
}
|
||||
},
|
||||
"node_modules/replicate": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/replicate/-/replicate-1.0.1.tgz",
|
||||
@ -4333,6 +4350,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stream-slicer": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/stream-slicer/-/stream-slicer-0.0.6.tgz",
|
||||
"integrity": "sha512-QsY0LbweYE5L+e+iBQgtkM5WUIf7+kCMA/m2VULv8rEEDDnlDPsPvOHH4nli6uaZOKQEt64u65h0l/eeZo7lCw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
@ -4542,6 +4565,40 @@
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/through2": {
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
|
||||
"integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readable-stream": ">=1.0.33-1 <1.1.0-0",
|
||||
"xtend": ">=4.0.0 <4.1.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/through2/node_modules/isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/through2/node_modules/readable-stream": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
|
||||
"integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"string_decoder": "~0.10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/through2/node_modules/string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/timed-out": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
|
||||
@ -6298,7 +6355,6 @@
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
||||
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "^2.1.3"
|
||||
}
|
||||
@ -6969,6 +7025,11 @@
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
|
||||
},
|
||||
"if-async": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/if-async/-/if-async-3.7.4.tgz",
|
||||
"integrity": "sha512-BFEH2mZyeF6KZKaKLVPZ0wMjIiWOdjvZ7zbx8ENec0qfZhJwKFbX/4jKM5LTKyJEc/GOqUKiiJ2IFKT9yWrZqA=="
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
@ -7248,8 +7309,7 @@
|
||||
"ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"mupdf": {
|
||||
"version": "1.26.2",
|
||||
@ -7494,6 +7554,17 @@
|
||||
"string_decoder": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"regedit": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/regedit/-/regedit-5.1.4.tgz",
|
||||
"integrity": "sha512-3VQ8BY2unUdl4nSx19QAn+pUlkqJhRbIsQc0zciWIVmELLXQLIHvdytUfcI56XYJZj3r9rkb7NTKxkQaXSYnow==",
|
||||
"requires": {
|
||||
"debug": "^4.1.0",
|
||||
"if-async": "^3.7.4",
|
||||
"stream-slicer": "0.0.6",
|
||||
"through2": "^0.6.3"
|
||||
}
|
||||
},
|
||||
"replicate": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/replicate/-/replicate-1.0.1.tgz",
|
||||
@ -7703,6 +7774,11 @@
|
||||
"integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
|
||||
"dev": true
|
||||
},
|
||||
"stream-slicer": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/stream-slicer/-/stream-slicer-0.0.6.tgz",
|
||||
"integrity": "sha512-QsY0LbweYE5L+e+iBQgtkM5WUIf7+kCMA/m2VULv8rEEDDnlDPsPvOHH4nli6uaZOKQEt64u65h0l/eeZo7lCw=="
|
||||
},
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
@ -7858,6 +7934,38 @@
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
|
||||
},
|
||||
"through2": {
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
|
||||
"integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==",
|
||||
"requires": {
|
||||
"readable-stream": ">=1.0.33-1 <1.1.0-0",
|
||||
"xtend": ">=4.0.0 <4.1.0-0"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
|
||||
"integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.1",
|
||||
"isarray": "0.0.1",
|
||||
"string_decoder": "~0.10.x"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"timed-out": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
"mupdf": "^1.3.3",
|
||||
"novita-sdk": "^1.0.37",
|
||||
"p-map": "^7.0.3",
|
||||
"regedit": "^5.1.4",
|
||||
"replicate": "^1.0.1",
|
||||
"sharp": "^0.34.2",
|
||||
"tslog": "^4.9.3",
|
||||
|
||||
14
packages/media/salamander-menu-watermark.reg
Normal file
@ -0,0 +1,14 @@
|
||||
REGEDIT4
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\1]
|
||||
"Item Name"="Watermark"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="watermark --src=\\" \\ --registry --group Media"
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
@ -57,6 +57,10 @@ export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
describe: 'Preserve alpha channel from input image',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
}).option('jpg', {
|
||||
describe: 'Convert PNG output to JPG format and delete PNG',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
})
|
||||
}
|
||||
|
||||
@ -83,6 +87,7 @@ export async function handler(argv: CLI.Arguments) {
|
||||
options.sync = argv.sync as boolean;
|
||||
options.contentModeration = argv.contentModeration as boolean;
|
||||
options.preserveAlpha = argv.preserveAlpha as boolean;
|
||||
options.jpg = argv.jpg as boolean;
|
||||
|
||||
logger.info("Removing background with Bria AI options:", {
|
||||
sync: options.sync,
|
||||
|
||||
260
packages/media/src/commands/register-commands.ts
Normal file
@ -0,0 +1,260 @@
|
||||
import * as CLI from 'yargs'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import { logger } from '../index.js'
|
||||
import { cli } from '../cli.js'
|
||||
import { defaults } from '../_cli.js'
|
||||
import { SalamanderMenuGeneratorRegistry, WindowsRegistry } from '../lib/salamander/index.js'
|
||||
|
||||
export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
return yargs.option('group', {
|
||||
describe: 'Group name to register commands under',
|
||||
type: 'string',
|
||||
default: 'Media'
|
||||
}).option('dry', {
|
||||
default: false,
|
||||
describe: 'Show what would be registered without actually registering',
|
||||
type: 'boolean'
|
||||
}).option('force', {
|
||||
default: false,
|
||||
describe: 'Force register even if command already exists',
|
||||
type: 'boolean'
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
})
|
||||
}
|
||||
|
||||
export const command = 'register-commands'
|
||||
export const desc = 'Register all pm-media commands in Salamander menu'
|
||||
export const builder = defaultOptions
|
||||
|
||||
interface CommandInfo {
|
||||
name: string
|
||||
command: string
|
||||
args: string
|
||||
description: string
|
||||
}
|
||||
|
||||
// Basic command mappings - users can extend these as needed
|
||||
const COMMAND_MAPPINGS: Record<string, CommandInfo> = {
|
||||
'resize': {
|
||||
name: 'Resize Images',
|
||||
command: 'pm-media',
|
||||
args: 'resize --alt=true --logLevel=info --src=\"$(FullName)/**/*.+(&{IMAGES})\" --dst=\"&{SRC_DIR}/&{SRC_NAME}_resized.&{SRC_EXT}\"',
|
||||
description: 'Resize images'
|
||||
},
|
||||
'watermark': {
|
||||
name: 'Add Watermark',
|
||||
command: 'pm-media',
|
||||
args: 'watermark --alt=true --logLevel=info --src=\"$(FullName)/**/*.+(&{IMAGES})\" --watermark=\"&{POLYMECH-ROOT}/nordin-ex/branding/polymech-saw-ex.svg\" --dst=\"&{SRC_DIR}/&{SRC_NAME}_watermarked.&{SRC_EXT}\"',
|
||||
description: 'Add watermark to images'
|
||||
},
|
||||
'background-remove': {
|
||||
name: 'Remove Background',
|
||||
command: 'pm-media',
|
||||
args: 'background:remove --alt=true --logLevel=info --src=\"$(FullName)/**/*.+(&{IMAGES})\" --dst=\"&{SRC_DIR}/&{SRC_NAME}_bg_removed.&{SRC_EXT}\"',
|
||||
description: 'Remove background from images'
|
||||
},
|
||||
'background-remove-bria': {
|
||||
name: 'Remove Background (Bria AI)',
|
||||
command: 'pm-media',
|
||||
args: 'background:remove:bria --alt=true --logLevel=info --src=\"$(FullName)/**/*.+(&{IMAGES})\" --dst=\"&{SRC_DIR}/&{SRC_NAME}_bria_bg_removed.&{SRC_EXT}\"',
|
||||
description: 'Remove background using Bria AI'
|
||||
},
|
||||
'convert': {
|
||||
name: 'Convert Format',
|
||||
command: 'pm-media',
|
||||
args: 'convert --alt=true --logLevel=info --src=\"$(FullName)/**/*.+(&{IMAGES})\" --dst=\"&{SRC_DIR}/&{SRC_NAME}_converted.jpg\"',
|
||||
description: 'Convert image format'
|
||||
},
|
||||
'pdf2jpg': {
|
||||
name: 'PDF to JPG',
|
||||
command: 'pm-media',
|
||||
args: 'pdf2jpg --alt=true --logLevel=info --src=\"$(FullName)/**/*.pdf\" --dst=\"&{SRC_DIR}/&{SRC_NAME}_page.jpg\"',
|
||||
description: 'Convert PDF pages to JPG images'
|
||||
},
|
||||
'svg2jpg': {
|
||||
name: 'SVG to JPG',
|
||||
command: 'pm-media',
|
||||
args: 'svg2jpg --alt=true --logLevel=info --src=\"$(FullName)/**/*.svg\" --dst=\"&{SRC_DIR}/&{SRC_NAME}.jpg\"',
|
||||
description: 'Convert SVG to JPG'
|
||||
},
|
||||
'crop-foreground': {
|
||||
name: 'Crop Foreground',
|
||||
command: 'pm-media',
|
||||
args: 'crop-foreground --alt=true --logLevel=info --src=\"$(FullName)/**/*.+(&{IMAGES})\" --dst=\"&{SRC_DIR}/&{SRC_NAME}_cropped.&{SRC_EXT}\"',
|
||||
description: 'Crop to foreground content'
|
||||
}
|
||||
}
|
||||
|
||||
async function getAvailableCommands(): Promise<string[]> {
|
||||
const commandsDir = path.join(process.cwd(), 'src', 'commands')
|
||||
const files = fs.readdirSync(commandsDir)
|
||||
|
||||
return files
|
||||
.filter(file => file.endsWith('.ts') &&
|
||||
file !== 'salamander.ts' &&
|
||||
file !== 'register-commands.ts')
|
||||
.map(file => file.replace('.ts', ''))
|
||||
.filter(cmd => COMMAND_MAPPINGS[cmd]) // Only include commands we have mappings for
|
||||
}
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
defaults()
|
||||
logger.settings.minLevel = argv.logLevel as any
|
||||
|
||||
const options = {
|
||||
group: argv.group as string,
|
||||
dry: argv.dry as boolean,
|
||||
force: argv.force as boolean
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info('Scanning available pm-media commands...')
|
||||
const availableCommands = await getAvailableCommands()
|
||||
logger.info(`Found ${availableCommands.length} commands: ${availableCommands.join(', ')}`)
|
||||
|
||||
if (options.dry) {
|
||||
logger.info('\n=== DRY RUN - Commands that would be registered ===')
|
||||
for (const cmdName of availableCommands) {
|
||||
const cmdInfo = COMMAND_MAPPINGS[cmdName]
|
||||
logger.info(`\nCommand: ${cmdName}`)
|
||||
logger.info(` Name: ${cmdInfo.name}`)
|
||||
logger.info(` Args: ${cmdInfo.args}`)
|
||||
logger.info(` Group: ${options.group}`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check which commands already exist
|
||||
logger.info('Checking existing registry entries...')
|
||||
const existingEntries = await WindowsRegistry.listAllMenuEntries()
|
||||
const existingCommandNames = existingEntries
|
||||
.filter(entry => entry.type === 'command') // Only commands, not submenus
|
||||
.map(entry => entry.name)
|
||||
|
||||
logger.info(`Found ${existingCommandNames.length} existing menu entries`)
|
||||
|
||||
let registeredCount = 0
|
||||
let skippedCount = 0
|
||||
|
||||
// Check if the target group exists, create it if it doesn't
|
||||
const groupExists = existingEntries.some(entry =>
|
||||
entry.type === 'submenu' && entry.name === options.group
|
||||
)
|
||||
|
||||
let groupInsertionPoint: number
|
||||
|
||||
if (!groupExists) {
|
||||
logger.info(`Creating group: ${options.group}`)
|
||||
|
||||
// Get the starting index for the new submenu
|
||||
groupInsertionPoint = await WindowsRegistry.getNextMenuIndex()
|
||||
|
||||
// Create the group submenu start
|
||||
const groupStartValues = {
|
||||
'Item Name': options.group,
|
||||
'Command': '',
|
||||
'Arguments': '',
|
||||
'Initial Directory': '',
|
||||
'Execute Through Shell': 'dword:00000000',
|
||||
'Close Shell Window': 'dword:00000000',
|
||||
'Open Shell Window': 'dword:00000000',
|
||||
'Icon': '',
|
||||
'Type': 'dword:00000001',
|
||||
'Show In Toolbar': 'dword:00000001'
|
||||
}
|
||||
|
||||
await WindowsRegistry.writeMenuEntry(groupInsertionPoint, groupStartValues)
|
||||
logger.info(`✓ Created group start: ${options.group} at index ${groupInsertionPoint}`)
|
||||
|
||||
// Increment for the first command slot
|
||||
groupInsertionPoint++
|
||||
} else {
|
||||
logger.info(`Group '${options.group}' already exists`)
|
||||
groupInsertionPoint = await WindowsRegistry.findGroupInsertionPoint(options.group)
|
||||
}
|
||||
|
||||
for (const cmdName of availableCommands) {
|
||||
const cmdInfo = COMMAND_MAPPINGS[cmdName]
|
||||
|
||||
// Check if command already exists
|
||||
const exists = existingCommandNames.some(name =>
|
||||
name.toLowerCase().includes(cmdInfo.name.toLowerCase()) ||
|
||||
cmdInfo.name.toLowerCase().includes(name.toLowerCase())
|
||||
)
|
||||
|
||||
if (exists && !options.force) {
|
||||
logger.info(`Skipping '${cmdInfo.name}' - already exists (use --force to override)`)
|
||||
skippedCount++
|
||||
continue
|
||||
}
|
||||
|
||||
logger.info(`Registering: ${cmdInfo.name}`)
|
||||
|
||||
try {
|
||||
// Build registry values for the command
|
||||
const values = {
|
||||
'Item Name': cmdInfo.name,
|
||||
'Command': cmdInfo.command,
|
||||
'Arguments': cmdInfo.args,
|
||||
'Initial Directory': '$(FullPath)',
|
||||
'Execute Through Shell': 'dword:00000001',
|
||||
'Close Shell Window': 'dword:00000001',
|
||||
'Open Shell Window': 'dword:00000001',
|
||||
'Icon': '',
|
||||
'Type': 'dword:00000000',
|
||||
'Show In Toolbar': 'dword:00000001'
|
||||
}
|
||||
|
||||
await WindowsRegistry.writeMenuEntry(groupInsertionPoint, values)
|
||||
|
||||
registeredCount++
|
||||
logger.info(`✓ Registered: ${cmdInfo.name} at index ${groupInsertionPoint}`)
|
||||
|
||||
// Increment insertion point for next command to insert them sequentially within the group
|
||||
groupInsertionPoint++
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Failed to register '${cmdInfo.name}':`, error)
|
||||
}
|
||||
}
|
||||
|
||||
// If we created a new group and registered commands, add the submenu end
|
||||
if (!groupExists && registeredCount > 0) {
|
||||
const groupEndValues = {
|
||||
'Item Name': '(Submenu End)',
|
||||
'Command': '',
|
||||
'Arguments': '',
|
||||
'Initial Directory': '',
|
||||
'Execute Through Shell': 'dword:00000000',
|
||||
'Close Shell Window': 'dword:00000000',
|
||||
'Open Shell Window': 'dword:00000000',
|
||||
'Icon': '',
|
||||
'Type': 'dword:00000002',
|
||||
'Show In Toolbar': 'dword:00000000'
|
||||
}
|
||||
|
||||
await WindowsRegistry.writeMenuEntry(groupInsertionPoint, groupEndValues)
|
||||
logger.info(`✓ Created group end at index ${groupInsertionPoint}`)
|
||||
}
|
||||
|
||||
logger.info(`\n=== Registration Complete ===`)
|
||||
logger.info(`Registered: ${registeredCount} commands`)
|
||||
logger.info(`Skipped: ${skippedCount} commands`)
|
||||
logger.info(`Total available: ${availableCommands.length} commands`)
|
||||
|
||||
if (registeredCount > 0) {
|
||||
logger.info('\nCommands have been registered in Salamander\'s User Menu.')
|
||||
logger.info('Restart Salamander to see the new menu entries.')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to register commands:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler)
|
||||
240
packages/media/src/commands/register-explorer.ts
Normal file
@ -0,0 +1,240 @@
|
||||
import * as CLI from 'yargs'
|
||||
import { logger } from '../index.js'
|
||||
import { cli } from '../cli.js'
|
||||
import { defaults } from '../_cli.js'
|
||||
import regedit, { RegistryItemPutCollection, REG_SZ_Value } from 'regedit'
|
||||
|
||||
export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
return yargs.option('group', {
|
||||
describe: 'Group name for the context menu items',
|
||||
type: 'string',
|
||||
default: 'PM-Media'
|
||||
}).option('unregister', {
|
||||
default: false,
|
||||
describe: 'Remove the shell extensions from Explorer',
|
||||
type: 'boolean'
|
||||
}).option('dry', {
|
||||
default: false,
|
||||
describe: 'Show what would be registered without actually doing it',
|
||||
type: 'boolean'
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
})
|
||||
}
|
||||
|
||||
export const command = 'register-explorer'
|
||||
export const desc = 'Register pm-media commands in Windows Explorer context menu for image files'
|
||||
export const builder = defaultOptions
|
||||
|
||||
// Image file extensions to register with
|
||||
const IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp', '.svg']
|
||||
|
||||
// Command mappings for Explorer context menu
|
||||
const EXPLORER_COMMANDS = [
|
||||
{
|
||||
name: 'Convert to JPG',
|
||||
command: 'pm-media',
|
||||
args: 'convert --alt=true --logLevel=info --src="%1" --dst="%~dpn1_converted.jpg"'
|
||||
},
|
||||
{
|
||||
name: 'Resize Image',
|
||||
command: 'pm-media',
|
||||
args: 'resize --alt=true --logLevel=info --src="%1" --dst="%~dpn1_resized%~x1"'
|
||||
},
|
||||
{
|
||||
name: 'Add Watermark',
|
||||
command: 'pm-media',
|
||||
args: 'watermark --alt=true --logLevel=info --src="%1" --watermark="&{POLYMECH-ROOT}/nordin-ex/branding/polymech-saw-ex.svg" --dst="%~dpn1_watermarked%~x1"'
|
||||
},
|
||||
{
|
||||
name: 'Remove Background',
|
||||
command: 'pm-media',
|
||||
args: 'background:remove --alt=true --logLevel=info --src="%1" --dst="%~dpn1_bg_removed%~x1"'
|
||||
}
|
||||
]
|
||||
|
||||
interface ExplorerRegistryEntry {
|
||||
keyPath: string
|
||||
valueName: string
|
||||
value: string
|
||||
type: 'REG_SZ' | 'REG_DWORD'
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate registry entries for Windows Explorer shell extensions
|
||||
*/
|
||||
function generateExplorerRegistryEntries(groupName: string): ExplorerRegistryEntry[] {
|
||||
const entries: ExplorerRegistryEntry[] = []
|
||||
|
||||
for (const ext of IMAGE_EXTENSIONS) {
|
||||
const extKey = `HKCU\\Software\\Classes\\${ext}`
|
||||
|
||||
// For each command, create the shell extension entries
|
||||
for (let i = 0; i < EXPLORER_COMMANDS.length; i++) {
|
||||
const cmd = EXPLORER_COMMANDS[i]
|
||||
const cmdKey = `${extKey}\\shell\\${groupName}\\shell\\${cmd.name.replace(/\s+/g, '')}`
|
||||
|
||||
// Command display name
|
||||
entries.push({
|
||||
keyPath: cmdKey,
|
||||
valueName: '',
|
||||
value: cmd.name,
|
||||
type: 'REG_SZ'
|
||||
})
|
||||
|
||||
// Command execution
|
||||
entries.push({
|
||||
keyPath: `${cmdKey}\\command`,
|
||||
valueName: '',
|
||||
value: `${cmd.command} ${cmd.args}`,
|
||||
type: 'REG_SZ'
|
||||
})
|
||||
}
|
||||
|
||||
// Set the group submenu display name
|
||||
entries.push({
|
||||
keyPath: `${extKey}\\shell\\${groupName}`,
|
||||
valueName: '',
|
||||
value: groupName,
|
||||
type: 'REG_SZ'
|
||||
})
|
||||
|
||||
// Set submenu position (optional)
|
||||
entries.push({
|
||||
keyPath: `${extKey}\\shell\\${groupName}`,
|
||||
valueName: 'Position',
|
||||
value: 'Middle',
|
||||
type: 'REG_SZ'
|
||||
})
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
/**
|
||||
* Write registry entries for Explorer shell extensions
|
||||
*/
|
||||
async function writeExplorerRegistryEntries(entries: ExplorerRegistryEntry[]): Promise<void> {
|
||||
const promisified = regedit.promisified
|
||||
|
||||
for (const entry of entries) {
|
||||
try {
|
||||
// Create the key if it doesn't exist
|
||||
await promisified.createKey([entry.keyPath])
|
||||
|
||||
// Write the value using correct regedit format
|
||||
// For default values, use empty string as key name
|
||||
const valueName = entry.valueName || ''
|
||||
const valuesToWrite: RegistryItemPutCollection = {
|
||||
[entry.keyPath]: {
|
||||
[valueName]: {
|
||||
value: entry.value,
|
||||
type: entry.type as 'REG_SZ'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await promisified.putValue(valuesToWrite)
|
||||
logger.debug(`✓ Created registry entry: ${entry.keyPath} = ${entry.value}`)
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Failed to create registry entry ${entry.keyPath}:`, error)
|
||||
logger.error(`Entry details:`, {
|
||||
keyPath: entry.keyPath,
|
||||
valueName: entry.valueName,
|
||||
value: entry.value,
|
||||
type: entry.type
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove registry entries for Explorer shell extensions
|
||||
*/
|
||||
async function removeExplorerRegistryEntries(groupName: string): Promise<void> {
|
||||
const promisified = regedit.promisified
|
||||
|
||||
for (const ext of IMAGE_EXTENSIONS) {
|
||||
const extKey = `HKCU\\Software\\Classes\\${ext}`
|
||||
const groupKey = `${extKey}\\shell\\${groupName}`
|
||||
|
||||
try {
|
||||
// Check if the key exists
|
||||
const result = await promisified.list([groupKey])
|
||||
if (result[groupKey].exists) {
|
||||
await promisified.deleteKey([groupKey])
|
||||
logger.info(`✓ Removed shell extension for ${ext}`)
|
||||
}
|
||||
} catch (error) {
|
||||
logger.debug(`Could not remove key ${groupKey}:`, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
defaults()
|
||||
logger.settings.minLevel = argv.logLevel as any
|
||||
|
||||
const options = {
|
||||
group: argv.group as string,
|
||||
unregister: argv.unregister as boolean,
|
||||
dry: argv.dry as boolean
|
||||
}
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
logger.error('Windows Explorer shell extensions are only supported on Windows')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
try {
|
||||
if (options.unregister) {
|
||||
logger.info(`Removing Explorer shell extensions for group: ${options.group}`)
|
||||
|
||||
if (options.dry) {
|
||||
logger.info('\n=== DRY RUN - Extensions that would be removed ===')
|
||||
for (const ext of IMAGE_EXTENSIONS) {
|
||||
logger.info(` ${ext} files: Remove "${options.group}" context menu`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
await removeExplorerRegistryEntries(options.group)
|
||||
logger.info(`✓ Successfully removed Explorer shell extensions for: ${options.group}`)
|
||||
|
||||
} else {
|
||||
logger.info(`Registering Explorer shell extensions for group: ${options.group}`)
|
||||
logger.info(`Target file types: ${IMAGE_EXTENSIONS.join(', ')}`)
|
||||
|
||||
const entries = generateExplorerRegistryEntries(options.group)
|
||||
|
||||
if (options.dry) {
|
||||
logger.info('\n=== DRY RUN - Extensions that would be registered ===')
|
||||
for (const ext of IMAGE_EXTENSIONS) {
|
||||
logger.info(`\n${ext} files:`)
|
||||
for (const cmd of EXPLORER_COMMANDS) {
|
||||
logger.info(` → ${cmd.name}: ${cmd.command} ${cmd.args}`)
|
||||
}
|
||||
}
|
||||
logger.info(`\nTotal registry entries: ${entries.length}`)
|
||||
return
|
||||
}
|
||||
|
||||
await writeExplorerRegistryEntries(entries)
|
||||
|
||||
logger.info(`\n=== Registration Complete ===`)
|
||||
logger.info(`Registered ${EXPLORER_COMMANDS.length} commands for ${IMAGE_EXTENSIONS.length} file types`)
|
||||
logger.info(`Total registry entries created: ${entries.length}`)
|
||||
logger.info('\nExplorer context menu entries have been registered.')
|
||||
logger.info('You may need to restart Explorer.exe to see the changes.')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Failed to register/unregister Explorer shell extensions:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler)
|
||||
270
packages/media/src/commands/salamander.ts
Normal file
@ -0,0 +1,270 @@
|
||||
import * as CLI from 'yargs'
|
||||
import * as path from 'path'
|
||||
import { logger } from '../index.js'
|
||||
import { cli } from '../cli.js'
|
||||
import {
|
||||
sanitize,
|
||||
defaults
|
||||
} from '../_cli.js'
|
||||
|
||||
import SalamanderMenuGenerator, { SalamanderMenuConfig, SalamanderMenuGeneratorRegistry, WindowsRegistry } from '../lib/salamander/index.js'
|
||||
|
||||
export const defaultOptions = (yargs: CLI.Argv) => {
|
||||
return yargs.option('config', {
|
||||
describe: 'JSON configuration file path',
|
||||
type: 'string'
|
||||
}).option('output', {
|
||||
describe: 'Output registry file path',
|
||||
type: 'string'
|
||||
}).option('existing', {
|
||||
describe: 'Path to existing registry file to parse for insertion point',
|
||||
type: 'string'
|
||||
}).option('group', {
|
||||
describe: 'Group name to insert items into (e.g., "Media")',
|
||||
type: 'string'
|
||||
}).option('startIndex', {
|
||||
describe: 'Starting menu index (overrides auto-detection)',
|
||||
type: 'number'
|
||||
}).option('debug', {
|
||||
default: false,
|
||||
describe: 'Enable internal debug messages',
|
||||
type: 'boolean'
|
||||
}).option('verbose', {
|
||||
default: false,
|
||||
describe: 'Show internal messages',
|
||||
type: 'boolean'
|
||||
}).option('logLevel', {
|
||||
describe: 'Log level : warn, info, debug, error',
|
||||
type: 'string',
|
||||
default: 'info'
|
||||
}).option('registry', {
|
||||
describe: 'Apply directly to Windows registry (Windows only)',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('list', {
|
||||
describe: 'List current menu entries from registry',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('remove', {
|
||||
describe: 'Remove menu entries from registry',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('name', {
|
||||
describe: 'Menu item name',
|
||||
type: 'string'
|
||||
}).option('command', {
|
||||
describe: 'Command to execute',
|
||||
type: 'string'
|
||||
}).option('args', {
|
||||
describe: 'Command arguments',
|
||||
type: 'string'
|
||||
}).option('workingDir', {
|
||||
describe: 'Working directory for command execution',
|
||||
type: 'string',
|
||||
default: '$(FullPath)'
|
||||
}).option('icon', {
|
||||
describe: 'Icon path for menu item',
|
||||
type: 'string',
|
||||
default: ''
|
||||
}).option('executeThoughShell', {
|
||||
describe: 'Execute command through shell',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
}).option('closeShellWindow', {
|
||||
describe: 'Close shell window after execution',
|
||||
type: 'boolean',
|
||||
default: false
|
||||
}).option('openShellWindow', {
|
||||
describe: 'Open shell window during execution',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
}).option('showInToolbar', {
|
||||
describe: 'Show item in toolbar',
|
||||
type: 'boolean',
|
||||
default: true
|
||||
})
|
||||
}
|
||||
|
||||
export const command = 'salamander';
|
||||
export const desc = 'Generate Salamander file manager menu entries from JSON configuration';
|
||||
export const builder = defaultOptions;
|
||||
|
||||
export async function handler(argv: CLI.Arguments) {
|
||||
defaults()
|
||||
const options = sanitize(argv)
|
||||
logger.settings.minLevel = options.logLevel as any
|
||||
|
||||
try {
|
||||
// Handle listing current menu entries
|
||||
if (argv.list) {
|
||||
if (process.platform !== 'win32') {
|
||||
logger.error('Registry listing is only supported on Windows');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
logger.info('Current Salamander menu entries:');
|
||||
const entries = await SalamanderMenuGeneratorRegistry.listCurrentMenuEntries();
|
||||
|
||||
for (const entry of entries) {
|
||||
const typeStr = entry.type === 'submenu' ? '(submenu)' :
|
||||
entry.type === 'submenu-end' ? '(submenu end)' : '(command)';
|
||||
const cmdStr = entry.command ? ` → ${entry.command}` : '';
|
||||
logger.info(` [${entry.index}] ${entry.name} ${typeStr}${cmdStr}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let config: SalamanderMenuConfig;
|
||||
|
||||
// Check if we're using individual command line options or JSON config
|
||||
if (argv.name && argv.command) {
|
||||
// Create config from command line options
|
||||
logger.info('Creating menu entry from command line options');
|
||||
|
||||
config = {
|
||||
baseKey: 'HKCU\\Software\\Altap\\Altap Salamander 4.0\\User Menu',
|
||||
startIndex: 1,
|
||||
items: [{
|
||||
name: argv.name as string,
|
||||
command: argv.command as string,
|
||||
arguments: argv.args as string || '',
|
||||
initialDirectory: argv.workingDir as string,
|
||||
executeThoughShell: argv.executeThoughShell as boolean,
|
||||
closeShellWindow: argv.closeShellWindow as boolean,
|
||||
openShellWindow: argv.openShellWindow as boolean,
|
||||
icon: argv.icon as string,
|
||||
showInToolbar: argv.showInToolbar as boolean
|
||||
}]
|
||||
};
|
||||
} else if (argv.config) {
|
||||
// Load configuration from JSON
|
||||
const configPath = argv.config as string;
|
||||
logger.info(`Loading configuration from: ${configPath}`);
|
||||
config = SalamanderMenuGenerator.loadFromJson(configPath);
|
||||
} else {
|
||||
logger.error('Either --config file or --name and --command options are required');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Determine starting index
|
||||
let startIndex = argv.startIndex as number;
|
||||
|
||||
if (!startIndex) {
|
||||
if (argv.registry && process.platform === 'win32') {
|
||||
// Use registry to determine insertion point
|
||||
if (argv.group) {
|
||||
startIndex = await SalamanderMenuGeneratorRegistry.autoDetectInsertionPoint(argv.group as string);
|
||||
logger.info(`Found insertion point for group "${argv.group}": index ${startIndex}`);
|
||||
} else {
|
||||
startIndex = await SalamanderMenuGeneratorRegistry.autoDetectInsertionPoint();
|
||||
logger.info(`Detected next available index: ${startIndex}`);
|
||||
}
|
||||
} else if (argv.existing) {
|
||||
const existingPath = argv.existing as string;
|
||||
|
||||
if (argv.group) {
|
||||
startIndex = SalamanderMenuGenerator.findInsertionPoint(existingPath, argv.group as string);
|
||||
logger.info(`Found insertion point for group "${argv.group}": index ${startIndex}`);
|
||||
} else {
|
||||
const maxIndex = SalamanderMenuGenerator.parseExistingRegistry(existingPath);
|
||||
startIndex = maxIndex + 1;
|
||||
logger.info(`Detected next available index: ${startIndex}`);
|
||||
}
|
||||
} else {
|
||||
startIndex = config.startIndex || 1;
|
||||
logger.info(`Using configured start index: ${startIndex}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Update config with determined start index
|
||||
config.startIndex = startIndex;
|
||||
|
||||
// Handle direct registry operations
|
||||
if (argv.registry) {
|
||||
if (process.platform !== 'win32') {
|
||||
logger.error('Direct registry operations are only supported on Windows');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const generator = new SalamanderMenuGeneratorRegistry(config);
|
||||
|
||||
if (argv.remove) {
|
||||
logger.info('Removing menu entries from registry...');
|
||||
await generator.removeFromRegistry();
|
||||
logger.info('Menu entries removed successfully');
|
||||
} else {
|
||||
logger.info('Applying menu entries to registry...');
|
||||
await generator.applyToRegistry();
|
||||
logger.info('Menu entries applied successfully');
|
||||
logger.info('Restart Salamander to see the new menu items');
|
||||
}
|
||||
|
||||
// Show summary
|
||||
const entries = generator.generateRegistryEntries();
|
||||
logger.info(`Processed ${entries.length} menu entries`);
|
||||
|
||||
if (options.verbose) {
|
||||
logger.info('Processed entries:');
|
||||
for (const entry of entries) {
|
||||
const itemName = entry.values['"Item Name"'] as string;
|
||||
const type = entry.values['"Type"'] as string;
|
||||
const typeStr = type === 'dword:00000001' ? '(submenu)' :
|
||||
type === 'dword:00000002' ? '(submenu end)' : '(command)';
|
||||
logger.info(` ${itemName} ${typeStr}`);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate .reg file
|
||||
const generator = new SalamanderMenuGenerator(config);
|
||||
|
||||
// Generate output path if not specified
|
||||
let outputPath = argv.output as string;
|
||||
if (!outputPath) {
|
||||
if (argv.config) {
|
||||
const configPath = argv.config as string;
|
||||
const configDir = path.dirname(configPath);
|
||||
const configName = path.basename(configPath, '.json');
|
||||
outputPath = path.join(configDir, `${configName}-salamander-menu.reg`);
|
||||
} else {
|
||||
outputPath = `salamander-menu-${argv.name?.toString().toLowerCase().replace(/\s+/g, '-') || 'item'}.reg`;
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`Generating registry file: ${outputPath}`);
|
||||
|
||||
// Generate and save registry file
|
||||
generator.saveRegistryFile(outputPath);
|
||||
|
||||
// Generate summary
|
||||
const entries = generator.generateRegistryEntries();
|
||||
logger.info(`Successfully generated ${entries.length} menu entries`);
|
||||
|
||||
if (options.verbose) {
|
||||
logger.info('Generated entries:');
|
||||
for (const entry of entries) {
|
||||
const itemName = entry.values['"Item Name"'] as string;
|
||||
const type = entry.values['"Type"'] as string;
|
||||
const typeStr = type === 'dword:00000001' ? '(submenu)' :
|
||||
type === 'dword:00000002' ? '(submenu end)' : '(command)';
|
||||
logger.info(` ${itemName} ${typeStr}`);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`\nTo apply the menu entries:`);
|
||||
logger.info(`1. Double-click the generated .reg file: ${outputPath}`);
|
||||
logger.info(`2. Confirm the registry import in Windows`);
|
||||
logger.info(`3. Restart Salamander to see the new menu items`);
|
||||
|
||||
} catch (error) {
|
||||
logger.error(`Failed to generate Salamander menu:`, error.message);
|
||||
if (options.debug) {
|
||||
logger.error(error.stack);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
cli.command(command, desc, builder, handler)
|
||||
@ -1,5 +1,6 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import sharp from 'sharp';
|
||||
import pMap from 'p-map';
|
||||
import { logger } from '../../../index.js';
|
||||
import { IOptions } from '../../../types.js';
|
||||
@ -11,6 +12,7 @@ export interface BriaBackgroundRemoveOptions extends IOptions {
|
||||
sync?: boolean;
|
||||
contentModeration?: boolean;
|
||||
preserveAlpha?: boolean;
|
||||
jpg?: boolean;
|
||||
}
|
||||
|
||||
// Read image file as buffer for Bria API
|
||||
@ -34,6 +36,27 @@ async function downloadImageFromUrl(imageUrl: string, outputPath: string): Promi
|
||||
fs.writeFileSync(outputPath, buffer);
|
||||
}
|
||||
|
||||
// Convert PNG to JPG while preserving rotation and metadata
|
||||
async function convertPngToJpg(pngPath: string, jpgPath: string): Promise<void> {
|
||||
try {
|
||||
await sharp(pngPath)
|
||||
.jpeg({
|
||||
quality: 95,
|
||||
progressive: true
|
||||
})
|
||||
.withMetadata() // Preserve EXIF data including rotation
|
||||
.toFile(jpgPath);
|
||||
|
||||
// Delete the temporary PNG file
|
||||
fs.unlinkSync(pngPath);
|
||||
|
||||
logger.debug(`Converted PNG to JPG and cleaned up: ${pngPath} → ${jpgPath}`);
|
||||
} catch (error) {
|
||||
logger.error(`Failed to convert PNG to JPG: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeBriaBackground(
|
||||
inputPath: string,
|
||||
outputPath: string,
|
||||
@ -89,8 +112,18 @@ export async function removeBriaBackground(
|
||||
if (result.result_url || result.image_res) {
|
||||
// Download the processed image (Bria API uses result_url)
|
||||
const imageUrl = result.result_url || result.image_res;
|
||||
await downloadImageFromUrl(imageUrl, outputPath);
|
||||
logger.info(`Background removed: ${inputPath} → ${outputPath}`);
|
||||
|
||||
if (options.jpg && path.extname(outputPath).toLowerCase() === '.jpg') {
|
||||
// If JPG conversion is requested and output is JPG, download as PNG first then convert
|
||||
const tempPngPath = outputPath.replace(/\.jpe?g$/i, '_temp.png');
|
||||
await downloadImageFromUrl(imageUrl, tempPngPath);
|
||||
await convertPngToJpg(tempPngPath, outputPath);
|
||||
logger.info(`Background removed and converted to JPG: ${inputPath} → ${outputPath}`);
|
||||
} else {
|
||||
// Standard PNG output
|
||||
await downloadImageFromUrl(imageUrl, outputPath);
|
||||
logger.info(`Background removed: ${inputPath} → ${outputPath}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error('No image result returned from Bria API');
|
||||
}
|
||||
|
||||
496
packages/media/src/lib/salamander/index.ts
Normal file
@ -0,0 +1,496 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import regedit from 'regedit';
|
||||
|
||||
const promisified = regedit.promisified;
|
||||
|
||||
export interface SalamanderMenuItem {
|
||||
name: string;
|
||||
command?: string;
|
||||
arguments?: string;
|
||||
initialDirectory?: string;
|
||||
executeThoughShell?: boolean;
|
||||
closeShellWindow?: boolean;
|
||||
openShellWindow?: boolean;
|
||||
icon?: string;
|
||||
showInToolbar?: boolean;
|
||||
type?: 'command' | 'submenu' | 'submenu-end';
|
||||
children?: SalamanderMenuItem[];
|
||||
}
|
||||
|
||||
export interface SalamanderMenuConfig {
|
||||
baseKey: string;
|
||||
startIndex: number;
|
||||
items: SalamanderMenuItem[];
|
||||
}
|
||||
|
||||
export interface RegistryEntry {
|
||||
key: string;
|
||||
values: Record<string, string | number>;
|
||||
}
|
||||
|
||||
export class SalamanderMenuGenerator {
|
||||
private baseKey: string = 'HKEY_CURRENT_USER\\Software\\Altap\\Altap Salamander 4.0\\User Menu';
|
||||
|
||||
constructor(private config: SalamanderMenuConfig) {
|
||||
if (config.baseKey) {
|
||||
this.baseKey = config.baseKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse existing registry file to find the highest menu index
|
||||
*/
|
||||
static parseExistingRegistry(registryPath: string): number {
|
||||
if (!fs.existsSync(registryPath)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(registryPath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
let maxIndex = 0;
|
||||
|
||||
for (const line of lines) {
|
||||
const match = line.match(/\\User Menu\\(\d+)\]/);
|
||||
if (match) {
|
||||
const index = parseInt(match[1], 10);
|
||||
if (index > maxIndex) {
|
||||
maxIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate registry entries from menu configuration
|
||||
*/
|
||||
generateRegistryEntries(): RegistryEntry[] {
|
||||
const entries: RegistryEntry[] = [];
|
||||
let currentIndex = this.config.startIndex;
|
||||
|
||||
const processItems = (items: SalamanderMenuItem[], isSubMenu = false) => {
|
||||
for (const item of items) {
|
||||
if (item.children && item.children.length > 0) {
|
||||
// Submenu start
|
||||
entries.push({
|
||||
key: `[${this.baseKey}\\${currentIndex}]`,
|
||||
values: this.itemToRegistryValues(item, 'submenu')
|
||||
});
|
||||
currentIndex++;
|
||||
|
||||
// Process children
|
||||
processItems(item.children, true);
|
||||
|
||||
// Submenu end
|
||||
entries.push({
|
||||
key: `[${this.baseKey}\\${currentIndex}]`,
|
||||
values: {
|
||||
'"Item Name"': '"(Submenu End)"',
|
||||
'"Command"': '""',
|
||||
'"Arguments"': '""',
|
||||
'"Initial Directory"': '""',
|
||||
'"Execute Through Shell"': 'dword:00000000',
|
||||
'"Close Shell Window"': 'dword:00000000',
|
||||
'"Open Shell Window"': 'dword:00000000',
|
||||
'"Icon"': '""',
|
||||
'"Type"': 'dword:00000002',
|
||||
'"Show In Toolbar"': 'dword:00000000'
|
||||
}
|
||||
});
|
||||
currentIndex++;
|
||||
} else {
|
||||
// Regular command
|
||||
entries.push({
|
||||
key: `[${this.baseKey}\\${currentIndex}]`,
|
||||
values: this.itemToRegistryValues(item, 'command')
|
||||
});
|
||||
currentIndex++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
processItems(this.config.items);
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert menu item to registry values
|
||||
*/
|
||||
private itemToRegistryValues(item: SalamanderMenuItem, type: 'command' | 'submenu'): Record<string, string | number> {
|
||||
const values: Record<string, string | number> = {
|
||||
'"Item Name"': `"${item.name}"`,
|
||||
'"Command"': `"${item.command || ''}"`,
|
||||
'"Arguments"': `"${this.escapeArguments(item.arguments || '')}"`,
|
||||
'"Initial Directory"': `"${item.initialDirectory || ''}"`,
|
||||
'"Execute Through Shell"': item.executeThoughShell !== false ? 'dword:00000001' : 'dword:00000000',
|
||||
'"Close Shell Window"': item.closeShellWindow === true ? 'dword:00000001' : 'dword:00000000',
|
||||
'"Open Shell Window"': item.openShellWindow !== false ? 'dword:00000001' : 'dword:00000000',
|
||||
'"Icon"': `"${item.icon || ''}"`,
|
||||
'"Type"': type === 'submenu' ? 'dword:00000001' : 'dword:00000000',
|
||||
'"Show In Toolbar"': item.showInToolbar !== false ? 'dword:00000001' : 'dword:00000000'
|
||||
};
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape arguments for registry format
|
||||
*/
|
||||
private escapeArguments(args: string): string {
|
||||
return args
|
||||
.replace(/\\/g, '\\\\') // Escape backslashes
|
||||
.replace(/"/g, '\\\\"'); // Escape quotes
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate registry file content
|
||||
*/
|
||||
generateRegistryFile(): string {
|
||||
const entries = this.generateRegistryEntries();
|
||||
let content = 'REGEDIT4\n\n';
|
||||
|
||||
for (const entry of entries) {
|
||||
content += `${entry.key}\n`;
|
||||
|
||||
for (const [key, value] of Object.entries(entry.values)) {
|
||||
content += `${key}=${value}\n`;
|
||||
}
|
||||
|
||||
content += '\n';
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save registry file to disk
|
||||
*/
|
||||
saveRegistryFile(outputPath: string): void {
|
||||
const content = this.generateRegistryFile();
|
||||
|
||||
// Ensure output directory exists
|
||||
const dir = path.dirname(outputPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(outputPath, content, 'utf8');
|
||||
}
|
||||
|
||||
/**
|
||||
* Load menu configuration from JSON file
|
||||
*/
|
||||
static loadFromJson(jsonPath: string): SalamanderMenuConfig {
|
||||
if (!fs.existsSync(jsonPath)) {
|
||||
throw new Error(`JSON configuration file not found: ${jsonPath}`);
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(jsonPath, 'utf8');
|
||||
return JSON.parse(content) as SalamanderMenuConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find insertion point in existing registry
|
||||
*/
|
||||
static findInsertionPoint(registryPath: string, groupName?: string): number {
|
||||
if (!fs.existsSync(registryPath)) {
|
||||
return 1; // Start at index 1 if no existing registry
|
||||
}
|
||||
|
||||
const maxIndex = this.parseExistingRegistry(registryPath);
|
||||
|
||||
if (!groupName) {
|
||||
return maxIndex + 1;
|
||||
}
|
||||
|
||||
// Find group and insert before submenu end
|
||||
const content = fs.readFileSync(registryPath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
|
||||
let inTargetGroup = false;
|
||||
let groupStartIndex = -1;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
|
||||
// Check for menu entry
|
||||
const menuMatch = line.match(/\\User Menu\\(\d+)\]/);
|
||||
if (menuMatch) {
|
||||
const index = parseInt(menuMatch[1], 10);
|
||||
|
||||
// Look for item name in next few lines
|
||||
for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
|
||||
const nameLine = lines[j];
|
||||
if (nameLine.includes('"Item Name"')) {
|
||||
const nameMatch = nameLine.match(/"Item Name"="([^"]+)"/);
|
||||
if (nameMatch) {
|
||||
const itemName = nameMatch[1];
|
||||
|
||||
if (itemName === groupName) {
|
||||
inTargetGroup = true;
|
||||
groupStartIndex = index;
|
||||
} else if (itemName === '(Submenu End)' && inTargetGroup) {
|
||||
return index; // Insert before submenu end
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxIndex + 1; // Fallback to end
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Windows Registry helper for direct registry operations using regedit package
|
||||
*/
|
||||
export class WindowsRegistry {
|
||||
private static isWindows(): boolean {
|
||||
return process.platform === 'win32' && promisified !== null;
|
||||
}
|
||||
|
||||
private static ensureRegedit(): void {
|
||||
if (!this.isWindows()) {
|
||||
throw new Error('Registry operations are only supported on Windows with regedit package installed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all User Menu entries from registry
|
||||
*/
|
||||
static async readUserMenuEntries(baseKey: string = 'HKCU\\Software\\Altap\\Altap Salamander 4.0\\User Menu'): Promise<Record<string, any>> {
|
||||
this.ensureRegedit();
|
||||
|
||||
try {
|
||||
const result = await promisified.list([baseKey]);
|
||||
return result[baseKey] || { exists: false, keys: [], values: {} };
|
||||
} catch (error) {
|
||||
return { exists: false, keys: [], values: {} };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read specific menu entry by index
|
||||
*/
|
||||
static async readMenuEntry(index: number, baseKey: string = 'HKCU\\Software\\Altap\\Altap Salamander 4.0\\User Menu'): Promise<Record<string, any>> {
|
||||
this.ensureRegedit();
|
||||
|
||||
const keyPath = `${baseKey}\\${index}`;
|
||||
try {
|
||||
const result = await promisified.list([keyPath]);
|
||||
return result[keyPath] || { exists: false, keys: [], values: {} };
|
||||
} catch (error) {
|
||||
return { exists: false, keys: [], values: {} };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all existing menu indices
|
||||
*/
|
||||
static async getExistingMenuIndices(baseKey: string = 'HKCU\\Software\\Altap\\Altap Salamander 4.0\\User Menu'): Promise<number[]> {
|
||||
this.ensureRegedit();
|
||||
|
||||
const menuData = await this.readUserMenuEntries(baseKey);
|
||||
if (!menuData.exists) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return menuData.keys
|
||||
.map((key: string) => parseInt(key, 10))
|
||||
.filter((index: number) => !isNaN(index))
|
||||
.sort((a: number, b: number) => a - b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next available menu index
|
||||
*/
|
||||
static async getNextMenuIndex(baseKey: string = 'HKCU\\Software\\Altap\\Altap Salamander 4.0\\User Menu'): Promise<number> {
|
||||
const indices = await this.getExistingMenuIndices(baseKey);
|
||||
return indices.length > 0 ? Math.max(...indices) + 1 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find insertion point for a specific group
|
||||
*/
|
||||
static async findGroupInsertionPoint(groupName: string, baseKey: string = 'HKCU\\Software\\Altap\\Altap Salamander 4.0\\User Menu'): Promise<number> {
|
||||
this.ensureRegedit();
|
||||
|
||||
const indices = await this.getExistingMenuIndices(baseKey);
|
||||
|
||||
let inTargetGroup = false;
|
||||
|
||||
for (const index of indices) {
|
||||
const entry = await this.readMenuEntry(index, baseKey);
|
||||
|
||||
if (entry.exists && entry.values['Item Name']) {
|
||||
const itemName = entry.values['Item Name'].value;
|
||||
|
||||
if (itemName === groupName) {
|
||||
inTargetGroup = true;
|
||||
} else if (itemName === '(Submenu End)' && inTargetGroup) {
|
||||
return index; // Insert before submenu end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return await this.getNextMenuIndex(baseKey); // Fallback to end
|
||||
}
|
||||
|
||||
/**
|
||||
* Write registry values for a menu entry
|
||||
*/
|
||||
static async writeMenuEntry(index: number, values: Record<string, any>, baseKey: string = 'HKCU\\Software\\Altap\\Altap Salamander 4.0\\User Menu'): Promise<void> {
|
||||
this.ensureRegedit();
|
||||
|
||||
const keyPath = `${baseKey}\\${index}`;
|
||||
|
||||
// First create the key
|
||||
await promisified.createKey([keyPath]);
|
||||
|
||||
// Then write the values
|
||||
const valuesToWrite: Record<string, any> = {};
|
||||
valuesToWrite[keyPath] = {};
|
||||
|
||||
for (const [valueName, valueData] of Object.entries(values)) {
|
||||
const cleanValueName = valueName.replace(/"/g, '');
|
||||
let cleanValueData = String(valueData);
|
||||
let valueType = 'REG_SZ';
|
||||
|
||||
if (cleanValueData.startsWith('dword:')) {
|
||||
valueType = 'REG_DWORD';
|
||||
cleanValueData = parseInt(cleanValueData.replace('dword:', ''), 16).toString();
|
||||
} else {
|
||||
cleanValueData = cleanValueData.replace(/"/g, '');
|
||||
}
|
||||
|
||||
valuesToWrite[keyPath][cleanValueName] = {
|
||||
value: cleanValueData,
|
||||
type: valueType
|
||||
};
|
||||
}
|
||||
|
||||
await promisified.putValue(valuesToWrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a menu entry
|
||||
*/
|
||||
static async deleteMenuEntry(index: number, baseKey: string = 'HKCU\\Software\\Altap\\Altap Salamander 4.0\\User Menu'): Promise<void> {
|
||||
this.ensureRegedit();
|
||||
|
||||
const keyPath = `${baseKey}\\${index}`;
|
||||
|
||||
try {
|
||||
await promisified.deleteKey([keyPath]);
|
||||
} catch (error) {
|
||||
// Key might not exist, ignore error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all menu entries with their details
|
||||
*/
|
||||
static async listAllMenuEntries(baseKey: string = 'HKCU\\Software\\Altap\\Altap Salamander 4.0\\User Menu'): Promise<Array<{index: number, name: string, type: string, command?: string}>> {
|
||||
const indices = await this.getExistingMenuIndices(baseKey);
|
||||
const entries = [];
|
||||
|
||||
for (const index of indices) {
|
||||
const entry = await this.readMenuEntry(index, baseKey);
|
||||
|
||||
if (entry.exists && entry.values['Item Name']) {
|
||||
const name = entry.values['Item Name'].value;
|
||||
const type = entry.values['Type'] ? entry.values['Type'].value : 0;
|
||||
const command = entry.values['Command'] ? entry.values['Command'].value : '';
|
||||
|
||||
let typeString = 'command';
|
||||
if (type === 1 || type === '0x00000001') typeString = 'submenu';
|
||||
if (type === 2 || type === '0x00000002') typeString = 'submenu-end';
|
||||
|
||||
entries.push({
|
||||
index,
|
||||
name,
|
||||
type: typeString,
|
||||
command: command || undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extended Salamander Menu Generator with direct registry support
|
||||
*/
|
||||
export class SalamanderMenuGeneratorRegistry extends SalamanderMenuGenerator {
|
||||
|
||||
/**
|
||||
* Apply menu configuration directly to Windows registry
|
||||
*/
|
||||
async applyToRegistry(): Promise<void> {
|
||||
if (process.platform !== 'win32') {
|
||||
throw new Error('Direct registry operations are only supported on Windows');
|
||||
}
|
||||
|
||||
const entries = this.generateRegistryEntries();
|
||||
|
||||
for (const entry of entries) {
|
||||
// Extract menu index from key
|
||||
const keyMatch = entry.key.match(/\\User Menu\\(\d+)\]/);
|
||||
if (!keyMatch) continue;
|
||||
|
||||
const menuIndex = parseInt(keyMatch[1], 10);
|
||||
|
||||
await WindowsRegistry.writeMenuEntry(menuIndex, entry.values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove menu entries from Windows registry
|
||||
*/
|
||||
async removeFromRegistry(): Promise<void> {
|
||||
if (process.platform !== 'win32') {
|
||||
throw new Error('Direct registry operations are only supported on Windows');
|
||||
}
|
||||
|
||||
const entries = this.generateRegistryEntries();
|
||||
|
||||
// Remove in reverse order to avoid index issues
|
||||
for (let i = entries.length - 1; i >= 0; i--) {
|
||||
const entry = entries[i];
|
||||
const keyMatch = entry.key.match(/\\User Menu\\(\d+)\]/);
|
||||
if (keyMatch) {
|
||||
const menuIndex = parseInt(keyMatch[1], 10);
|
||||
await WindowsRegistry.deleteMenuEntry(menuIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detect insertion point from Windows registry
|
||||
*/
|
||||
static async autoDetectInsertionPoint(groupName?: string): Promise<number> {
|
||||
if (process.platform !== 'win32') {
|
||||
throw new Error('Registry operations are only supported on Windows');
|
||||
}
|
||||
|
||||
if (groupName) {
|
||||
return await WindowsRegistry.findGroupInsertionPoint(groupName);
|
||||
} else {
|
||||
return await WindowsRegistry.getNextMenuIndex();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List current menu entries from registry
|
||||
*/
|
||||
static async listCurrentMenuEntries(): Promise<Array<{index: number, name: string, type: string, command?: string}>> {
|
||||
return await WindowsRegistry.listAllMenuEntries();
|
||||
}
|
||||
}
|
||||
|
||||
export default SalamanderMenuGenerator;
|
||||
@ -9,6 +9,9 @@ import './commands/watermark.js'
|
||||
import './commands/background-remove.js'
|
||||
import './commands/background-remove-bria.js'
|
||||
import './commands/crop-foreground.js'
|
||||
import './commands/salamander.js'
|
||||
import './commands/register-commands.js'
|
||||
import './commands/register-explorer.js'
|
||||
|
||||
const argv: any = cli.argv;
|
||||
|
||||
|
||||
3453
packages/media/src/ref/config_sal_min.reg
Normal file
BIN
packages/media/src/ref/min.reg
Normal file
BIN
packages/media/src/ref/sal_conf.reg
Normal file
|
Before Width: | Height: | Size: 6.3 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 8.5 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 9.4 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.5 MiB |
@ -52,16 +52,24 @@ echo -e "${GREEN}✓ Cache functionality test completed${NC}"
|
||||
echo
|
||||
echo -e "${BLUE}Test 5: Configuration options test${NC}"
|
||||
pm-media background:remove:bria \
|
||||
--src "tests/images/in/barrel.jpg" \
|
||||
--src "tests/images/in/DSC01301.JPG" \
|
||||
--dst "tests/background-remove-bria-test-output/config-test.png" \
|
||||
--sync \
|
||||
--contentModeration \
|
||||
--preserveAlpha \
|
||||
--dry \
|
||||
--verbose
|
||||
|
||||
echo -e "${GREEN}✓ Configuration options test completed${NC}"
|
||||
|
||||
echo -e "${BLUE}Test 6: JPG conversion test (dry run)${NC}"
|
||||
pm-media background:remove:bria \
|
||||
--src "tests/images/in/DSC01177.JPG" \
|
||||
--dst "tests/background-remove-bria-test-output/jpg-conversion-test.jpg" \
|
||||
--jpg \
|
||||
--verbose
|
||||
|
||||
echo -e "${GREEN}✓ JPG conversion test completed${NC}"
|
||||
|
||||
echo -e "${BLUE}Test 7: Cache test with real file${NC}"
|
||||
# Try to process the same file again with cache enabled
|
||||
pm-media background:remove:bria \
|
||||
@ -104,9 +112,20 @@ for format in "${formats[@]}"; do
|
||||
--src "tests/images/in/*.$format" \
|
||||
--dst "tests/background-remove-bria-test-output/format-&{SRC_NAME}_nobg.png" \
|
||||
--alt \
|
||||
--dry \
|
||||
--verbose | head -5
|
||||
fi
|
||||
done
|
||||
|
||||
echo -e "${GREEN}✓ Format tests completed${NC}"
|
||||
|
||||
echo -e "${BLUE}Test 11: Batch JPG conversion with alt tokenizer${NC}"
|
||||
pm-media background:remove:bria \
|
||||
--src "tests/images/in/sub/*.JPG" \
|
||||
--dst "tests/background-remove-bria-test-output/batch-&{SRC_NAME}_nobg.jpg" \
|
||||
--jpg \
|
||||
--alt \
|
||||
--verbose
|
||||
|
||||
echo -e "${GREEN}✓ Batch JPG conversion test completed${NC}"
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 274 KiB |
@ -0,0 +1,74 @@
|
||||
REGEDIT4
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\5]
|
||||
"Item Name"="Background Remove (Bria)"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="background:remove:bria --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}_nobg.png\\\\" --jpg"
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\6]
|
||||
"Item Name"="Convert Tools"
|
||||
"Command"=""
|
||||
"Arguments"=""
|
||||
"Initial Directory"=""
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000001
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\7]
|
||||
"Item Name"="To WebP"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="convert --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}.webp\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000001
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\8]
|
||||
"Item Name"="To PNG"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="convert --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}.png\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000001
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\9]
|
||||
"Item Name"="(Submenu End)"
|
||||
"Command"=""
|
||||
"Arguments"=""
|
||||
"Initial Directory"=""
|
||||
"Execute Through Shell"=dword:00000000
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000000
|
||||
"Icon"=""
|
||||
"Type"=dword:00000002
|
||||
"Show In Toolbar"=dword:00000000
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\10]
|
||||
"Item Name"="PDF to Images"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="pdf2jpg --alt=true --logLevel=info --src=\\\\"$(FullName)\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
REGEDIT4
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\10]
|
||||
"Item Name"="Background Remove (Bria)"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="background:remove:bria --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}_nobg.png\\\\" --jpg"
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\11]
|
||||
"Item Name"="Convert Tools"
|
||||
"Command"=""
|
||||
"Arguments"=""
|
||||
"Initial Directory"=""
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000001
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\12]
|
||||
"Item Name"="To WebP"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="convert --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}.webp\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000001
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\13]
|
||||
"Item Name"="To PNG"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="convert --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}.png\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000001
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\14]
|
||||
"Item Name"="(Submenu End)"
|
||||
"Command"=""
|
||||
"Arguments"=""
|
||||
"Initial Directory"=""
|
||||
"Execute Through Shell"=dword:00000000
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000000
|
||||
"Icon"=""
|
||||
"Type"=dword:00000002
|
||||
"Show In Toolbar"=dword:00000000
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\15]
|
||||
"Item Name"="PDF to Images"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="pdf2jpg --alt=true --logLevel=info --src=\\\\"$(FullName)\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
@ -0,0 +1,74 @@
|
||||
REGEDIT4
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\4]
|
||||
"Item Name"="Background Remove (Bria)"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="background:remove:bria --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}_nobg.png\\\\" --jpg"
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\5]
|
||||
"Item Name"="Convert Tools"
|
||||
"Command"=""
|
||||
"Arguments"=""
|
||||
"Initial Directory"=""
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000001
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\6]
|
||||
"Item Name"="To WebP"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="convert --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}.webp\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000001
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\7]
|
||||
"Item Name"="To PNG"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="convert --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}.png\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000001
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\8]
|
||||
"Item Name"="(Submenu End)"
|
||||
"Command"=""
|
||||
"Arguments"=""
|
||||
"Initial Directory"=""
|
||||
"Execute Through Shell"=dword:00000000
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000000
|
||||
"Icon"=""
|
||||
"Type"=dword:00000002
|
||||
"Show In Toolbar"=dword:00000000
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\9]
|
||||
"Item Name"="PDF to Images"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="pdf2jpg --alt=true --logLevel=info --src=\\\\"$(FullName)\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
74
packages/media/tests/salamander-test-output/sample-menu.reg
Normal file
@ -0,0 +1,74 @@
|
||||
REGEDIT4
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\5]
|
||||
"Item Name"="Background Remove (Bria)"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="background:remove:bria --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}_nobg.png\\\\" --jpg"
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\6]
|
||||
"Item Name"="Convert Tools"
|
||||
"Command"=""
|
||||
"Arguments"=""
|
||||
"Initial Directory"=""
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000001
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\7]
|
||||
"Item Name"="To WebP"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="convert --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}.webp\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000001
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\8]
|
||||
"Item Name"="To PNG"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="convert --alt=true --logLevel=info --src=\\\\"$(FullName)/**/*.+(&{IMAGES})\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}.png\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000001
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\9]
|
||||
"Item Name"="(Submenu End)"
|
||||
"Command"=""
|
||||
"Arguments"=""
|
||||
"Initial Directory"=""
|
||||
"Execute Through Shell"=dword:00000000
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000000
|
||||
"Icon"=""
|
||||
"Type"=dword:00000002
|
||||
"Show In Toolbar"=dword:00000000
|
||||
|
||||
[HKEY_CURRENT_USER\Software\Altap\Altap Salamander 4.0\User Menu\10]
|
||||
"Item Name"="PDF to Images"
|
||||
"Command"="pm-media"
|
||||
"Arguments"="pdf2jpg --alt=true --logLevel=info --src=\\\\"$(FullName)\\\\" --dst=\\\\"&{SRC_DIR}/&{SRC_NAME}\\\\""
|
||||
"Initial Directory"="$(FullPath)"
|
||||
"Execute Through Shell"=dword:00000001
|
||||
"Close Shell Window"=dword:00000000
|
||||
"Open Shell Window"=dword:00000001
|
||||
"Icon"=""
|
||||
"Type"=dword:00000000
|
||||
"Show In Toolbar"=dword:00000001
|
||||
|
||||
91
packages/media/tests/salamander-test.sh
Normal file
@ -0,0 +1,91 @@
|
||||
echo "=== Salamander Menu Test Script ==="
|
||||
echo "Testing Salamander menu generation functionality"
|
||||
echo
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Create test output directory
|
||||
mkdir -p tests/salamander-test-output
|
||||
|
||||
echo -e "${BLUE}Building project...${NC}"
|
||||
npm run build
|
||||
|
||||
echo
|
||||
echo -e "${BLUE}Test 1: Generate menu from sample JSON${NC}"
|
||||
pm-media salamander \
|
||||
--config "integration/salamander-menu-sample.json" \
|
||||
--output "tests/salamander-test-output/sample-menu.reg" \
|
||||
--verbose
|
||||
|
||||
echo -e "${GREEN}✓ Sample menu generation completed${NC}"
|
||||
|
||||
echo
|
||||
echo -e "${BLUE}Test 2: Auto-detect insertion point from existing registry${NC}"
|
||||
pm-media salamander \
|
||||
--config "integration/salamander-menu-sample.json" \
|
||||
--output "tests/salamander-test-output/auto-detect-menu.reg" \
|
||||
--existing "src/ref/config_sal_min.reg" \
|
||||
--verbose
|
||||
|
||||
echo -e "${GREEN}✓ Auto-detect insertion test completed${NC}"
|
||||
|
||||
echo
|
||||
echo -e "${BLUE}Test 3: Insert into existing Media group${NC}"
|
||||
pm-media salamander \
|
||||
--config "integration/salamander-menu-sample.json" \
|
||||
--output "tests/salamander-test-output/media-group-menu.reg" \
|
||||
--existing "src/ref/config_sal_min.reg" \
|
||||
--group "Media" \
|
||||
--verbose
|
||||
|
||||
echo -e "${GREEN}✓ Media group insertion test completed${NC}"
|
||||
|
||||
echo
|
||||
echo -e "${BLUE}Test 4: Custom start index${NC}"
|
||||
pm-media salamander \
|
||||
--config "integration/salamander-menu-sample.json" \
|
||||
--output "tests/salamander-test-output/custom-index-menu.reg" \
|
||||
--startIndex 10 \
|
||||
--verbose
|
||||
|
||||
echo -e "${GREEN}✓ Custom start index test completed${NC}"
|
||||
|
||||
echo
|
||||
echo -e "${BLUE}Test 5: Help and usage${NC}"
|
||||
pm-media salamander --help | head -15
|
||||
|
||||
echo -e "${GREEN}✓ Help test completed${NC}"
|
||||
|
||||
echo
|
||||
echo -e "${YELLOW}=== Test Summary ===${NC}"
|
||||
echo "All tests completed! Check output files in: tests/salamander-test-output/"
|
||||
echo
|
||||
|
||||
echo "Generated files:"
|
||||
ls -la tests/salamander-test-output/
|
||||
|
||||
echo
|
||||
echo -e "${GREEN}✓ All Salamander menu tests passed successfully!${NC}"
|
||||
|
||||
echo
|
||||
echo -e "${BLUE}Generated menu structure preview:${NC}"
|
||||
if [ -f "tests/salamander-test-output/sample-menu.reg" ]; then
|
||||
echo "Sample menu entries:"
|
||||
grep -E "\\[|Item Name" "tests/salamander-test-output/sample-menu.reg" | head -20
|
||||
fi
|
||||
|
||||
echo
|
||||
echo -e "${BLUE}Usage examples:${NC}"
|
||||
echo "# Generate menu from JSON:"
|
||||
echo "pm-media salamander --config menu.json --output menu.reg"
|
||||
echo
|
||||
echo "# Insert into existing Media group:"
|
||||
echo "pm-media salamander --config menu.json --existing current.reg --group \"Media\""
|
||||
echo
|
||||
echo "# Auto-detect next available index:"
|
||||
echo "pm-media salamander --config menu.json --existing current.reg"
|
||||