Add custom output filename template (ENG-649)

This commit is contained in:
Ethan Troy 2026-03-17 20:38:04 -04:00
parent a93ed8341d
commit cac2a1bbd8
6 changed files with 72 additions and 8 deletions

View file

@ -13,6 +13,7 @@ import { checkDependencies, installPackage } from './src/utils/python';
import {
getVaultBasePath,
resolveOutputFolder,
resolveFilenameTemplate,
resolveImageDir,
toVaultRelative,
} from './src/utils/paths';
@ -123,8 +124,11 @@ export default class MarkitdownPlugin extends Plugin {
const inputPath = path.join(vaultPath, file.path);
const outputFolder = resolveOutputFolder(vaultPath, this.settings.outputPath);
const baseName = file.basename;
const outputPath = path.join(outputFolder, `${baseName}.md`);
const resolvedName = resolveFilenameTemplate(
this.settings.outputFilenameTemplate || '{filename}',
inputPath
);
const outputPath = path.join(outputFolder, `${resolvedName}.md`);
const options = this.buildConversionOptions(outputPath);
new Notice('Converting file...');

View file

@ -3,7 +3,7 @@ import * as path from 'path';
import * as fs from 'fs';
import type MarkitdownPlugin from '../../main';
import { FILE_INPUT_ACCEPT } from '../utils/fileTypes';
import { getVaultBasePath, resolveOutputFolder, toVaultRelative } from '../utils/paths';
import { getVaultBasePath, resolveOutputFolder, resolveFilenameTemplate, toVaultRelative } from '../utils/paths';
export class FileConvertModal extends Modal {
private plugin: MarkitdownPlugin;
@ -51,8 +51,11 @@ export class FileConvertModal extends Modal {
}
const outputFolder = resolveOutputFolder(vaultPath, this.plugin.settings.outputPath);
const baseName = path.basename(file.name, path.extname(file.name));
const outputPath = path.join(outputFolder, `${baseName}.md`);
const resolvedName = resolveFilenameTemplate(
this.plugin.settings.outputFilenameTemplate || '{filename}',
file.name
);
const outputPath = path.join(outputFolder, `${resolvedName}.md`);
// Write the DOM File to a temp file on disk
const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, '_');

View file

@ -3,7 +3,7 @@ import * as path from 'path';
import * as fs from 'fs';
import type MarkitdownPlugin from '../../main';
import { EXTENSION_GROUPS } from '../utils/fileTypes';
import { getVaultBasePath, resolveOutputFolder } from '../utils/paths';
import { getVaultBasePath, resolveOutputFolder, resolveFilenameTemplate } from '../utils/paths';
import { BatchProgressModal } from './BatchProgressModal';
export class FolderConvertModal extends Modal {
@ -119,8 +119,11 @@ export class FolderConvertModal extends Modal {
const file = files[i];
progressModal?.updateProgress(i, file.name, success, failed);
const baseName = path.basename(file.name, path.extname(file.name));
const outputPath = path.join(outputFolder, `${baseName}.md`);
const resolvedName = resolveFilenameTemplate(
this.plugin.settings.outputFilenameTemplate || '{filename}',
file.name
);
const outputPath = path.join(outputFolder, `${resolvedName}.md`);
const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, '_');
const tempFilePath = path.join(outputFolder, `tmp_${Date.now()}_${i}_${safeName}`);

View file

@ -76,6 +76,21 @@ export class SettingsTab extends PluginSettingTab {
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Output filename template')
.setDesc(
'Template for converted filenames. Variables: {filename} (original name), ' +
'{ext} (original extension), {date} (YYYY-MM-DD), {datetime} (YYYY-MM-DD_HHmmss). ' +
'The .md extension is always appended automatically.'
)
.addText(text => text
.setPlaceholder('{filename}')
.setValue(this.plugin.settings.outputFilenameTemplate)
.onChange(async (value) => {
this.plugin.settings.outputFilenameTemplate = value || '{filename}';
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Extract images')
.setDesc('Extract embedded images from PDFs and save as separate files')

View file

@ -13,6 +13,7 @@ export interface MarkitdownSettings {
imageSubfolderTemplate: string;
enableBatchProgress: boolean;
enableContextMenu: boolean;
outputFilenameTemplate: string;
}
export const DEFAULT_SETTINGS: MarkitdownSettings = {
@ -25,6 +26,7 @@ export const DEFAULT_SETTINGS: MarkitdownSettings = {
imageSubfolderTemplate: '{filename}-images',
enableBatchProgress: true,
enableContextMenu: true,
outputFilenameTemplate: '{filename}',
};
export interface ConversionOptions {

View file

@ -37,6 +37,43 @@ export function resolveOutputFolder(vaultPath: string, outputSetting: string): s
return outputFolder;
}
/**
* Resolve an output filename template using variables from the input path.
* Supported variables:
* {filename} original file name without extension
* {ext} original file extension (without dot)
* {date} conversion date as YYYY-MM-DD
* {datetime} conversion datetime as YYYY-MM-DD_HHmmss
*
* Returns the resolved name without .md extension (caller appends it).
* Characters invalid in filenames are stripped from the result.
*/
export function resolveFilenameTemplate(template: string, inputPath: string): string {
const baseName = path.basename(inputPath, path.extname(inputPath));
const ext = path.extname(inputPath).replace(/^\./, '');
const now = new Date();
const pad = (n: number) => String(n).padStart(2, '0');
const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
const datetime = `${date}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
let resolved = template
.replace(/\{filename\}/g, baseName)
.replace(/\{ext\}/g, ext)
.replace(/\{date\}/g, date)
.replace(/\{datetime\}/g, datetime);
// Remove characters invalid in filenames
resolved = resolved.replace(/[/\\:*?"<>|]/g, '');
// Fall back to the original filename if the result is empty after sanitisation
if (!resolved.trim()) {
resolved = baseName;
}
return resolved;
}
/**
* Compute the image extraction directory for a given output markdown file.
* Applies the template (e.g., "{filename}-images") to generate the folder name.