Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
602e611
Add specifying parent folder for binary files
matthewturk Jun 12, 2023
d324275
change version and authors
willjasen Dec 22, 2023
9aea5f7
disabling mobile for now
willjasen Dec 22, 2023
687489a
compare folder path to parent folder in settings
willjasen Dec 22, 2023
d5d776f
update gui to show that subfolders are not watched
willjasen Dec 22, 2023
4e3c502
binary file moves to attachment folder but links not updating
willjasen Dec 22, 2023
7f7a5cd
binary file moves and links are updated properly
willjasen Dec 22, 2023
eee3fd5
moving gui settings around
willjasen Dec 22, 2023
1e2ef15
switch back to allow mobile
willjasen Dec 22, 2023
ed82521
adding that binary file can be moved
willjasen Dec 22, 2023
b09df2d
refactoring move to path
willjasen Dec 22, 2023
e8dd09a
changing setting name
willjasen Dec 22, 2023
97a1ff5
update header and add version button
willjasen Dec 22, 2023
7528627
adding notice of moved file
willjasen Dec 22, 2023
29dc15d
created moveBinaryFile function
willjasen Dec 22, 2023
dde7df7
moved file will be renamed if one there already exists
willjasen Dec 22, 2023
6b4e0a3
update version to 0.4.0
willjasen Dec 22, 2023
9d92a04
Merge pull request #1 from willjasen/binary-folder-allow-list
willjasen Dec 22, 2023
8fab813
initial commit of build script
willjasen Jan 2, 2024
907ad89
enable execute permissions
willjasen Jan 2, 2024
a94b1c8
fixing variable name
willjasen Jan 2, 2024
f628505
standardizing cp commands
willjasen Jan 2, 2024
2dee737
switched to array and for loop
willjasen Jan 2, 2024
0819de5
comments
willjasen Jan 2, 2024
4ac848b
changing plugin directory
willjasen Feb 12, 2024
26b1a1e
create the plugin directory if needed
willjasen Feb 12, 2024
6491a3a
change generated note format
willjasen Apr 2, 2024
8cf406e
generate tags property properly
willjasen Apr 2, 2024
63f3b20
tags are being added (but not fully working)
willjasen Apr 2, 2024
7017fda
removing tags settings in favor of templater
willjasen Aug 20, 2024
f9f30ab
add some verbose
willjasen Aug 20, 2024
d30d7a7
output where file is copied to
willjasen Aug 20, 2024
723fc70
updated npm packages
willjasen Aug 20, 2024
1cd18fd
generator working with templater
willjasen Aug 20, 2024
3dc32c2
Merge pull request #2 from willjasen/dev
willjasen Aug 20, 2024
d0896fc
make npm silent
willjasen Aug 20, 2024
1ce1d62
fix the path
willjasen Aug 20, 2024
ef604f9
adding a sleep due to conflicts with another plugin
willjasen Aug 20, 2024
a2ac8f9
refactoring moving the file
willjasen Aug 21, 2024
70fd508
bump the version to 0.4.2
willjasen Aug 21, 2024
bef0fdc
Merge pull request #3 from willjasen/dev
willjasen Aug 21, 2024
c84fa2f
change comment
willjasen Aug 21, 2024
48badec
fix typo
willjasen Aug 21, 2024
a0cbb84
remove the tags setting
willjasen Aug 21, 2024
4a4ba81
hide the template file location setting if templater not used
willjasen Aug 21, 2024
982cafc
bump version to 0.4.3
willjasen Aug 21, 2024
c60f4d6
Merge pull request #4 from willjasen/dev
willjasen Aug 21, 2024
18ce6e6
fix "Invalid option in build() call: "watch"
willjasen Nov 23, 2025
a3459cd
bump to version 0.4.4
willjasen Nov 23, 2025
f0cbc39
Merge pull request #5 from willjasen/main
willjasen Apr 21, 2026
93202fe
fix pathing issue
willjasen Apr 21, 2026
818c014
add right-click option to create note where it is at
willjasen Apr 21, 2026
66bf40c
show extensions to be watched as chips
willjasen Apr 21, 2026
f3e52c6
edit the notification
willjasen Apr 21, 2026
727768a
use a different templater config for right-click
willjasen Apr 21, 2026
da08663
update version to 0.5.0
willjasen Apr 21, 2026
c13feae
Merge pull request #6 from willjasen/dev
willjasen Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
## Binary File Manager Plugin
## Obsidian Binary File Manager

This plugin detects new binary files in the vault and create markdown files with metadata.
![GitHub manifest version (path)](https://img.shields.io/github/manifest-json/v/willjasen/obsidian-binary-file-manager-plugin)

This plugin detects new binary files in the vault and creates markdown files with metadata. The new binary file can then be moved into a different directory, like attachments.

By using metadata files, you can take advantage of the rich functionality provied by Obsidian such as
- full text search,
- tags and aliases,
- full text search,
- tags and aliases,
- internal links, and so on.

For example, if you add tags to the metadata of an image file, then you can indirectly access the image file by tag-searching (and following an internal link in the metadata).
Expand Down
30 changes: 30 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/sh

# This script builds the plugin and copies the output files into the associated plugin directory
# After this build script is run, this plugin in Obisidian should disabled/enabled to refresh it

# variables
SOURCE_DIR="/Users/willjasen/GitHub/obsidian-binary-file-manager-plugin";
PLUGIN_DIR="/Users/willjasen/Library/Mobile Documents/iCloud~md~obsidian/Documents/willjasen/.obsidian/plugins/obsidian-binary-file-manager-plugin";
FILES=( "main.js" "manifest.json" "styles.css" );


# Create the directory if needed (handle spaces and parents)
if [ -d "$PLUGIN_DIR" ]; then
echo "Plugin directory already exists.";
else
echo "Creating plugin directory at $PLUGIN_DIR"; mkdir -p "$PLUGIN_DIR";
fi

# Go to the source directory and build this plugin

cd "$SOURCE_DIR";
npm i --silent; npm run build > /dev/null 2>&1;

# Copy built files into the plugin directory
for file in "${FILES[@]}"
do
echo "Copying file: $file --> $PLUGIN_DIR/$file";
cp "$SOURCE_DIR/$file" "$PLUGIN_DIR/$file";
done
echo "All files copied!";
43 changes: 26 additions & 17 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,29 @@ if you want to view the source, please visit the github repository of this plugi

const prod = process.argv[2] === 'production';

esbuild
.build({
banner: {
js: banner,
},
entryPoints: ['src/main.ts'],
bundle: true,
external: ['obsidian', 'electron', ...builtins],
format: 'cjs',
watch: !prod,
target: 'es2016',
logLevel: 'info',
sourcemap: prod ? false : 'inline',
treeShaking: true,
outfile: 'main.js',
})
.catch(() => process.exit(1));
const buildOptions = {
banner: { js: banner },
entryPoints: ['src/main.ts'],
bundle: true,
external: ['obsidian', 'electron', ...builtins],
format: 'cjs',
target: 'es2016',
logLevel: 'info',
sourcemap: prod ? false : 'inline',
treeShaking: true,
outfile: 'main.js',
};

if (prod) {
esbuild.build(buildOptions).catch(() => process.exit(1));
} else {
(async () => {
try {
const ctx = await esbuild.context(buildOptions);
await ctx.watch();
console.log('Watching for changes...');
} catch (e) {
process.exit(1);
}
})();
}
8 changes: 4 additions & 4 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"id": "obsidian-binary-file-manager-plugin",
"name": "Binary File Manager",
"version": "0.3.0",
"version": "0.5.0",
"minAppVersion": "0.12.0",
"description": "Detects new binary files in the vault and create markdown files with metadata.",
"author": "qawatake",
"authorUrl": "https://github.com/qawatake/obsidian-binary-file-manager-plugin",
"description": "Detects new binary files in the vault and creates markdown files with metadata.",
"author": "qawatake, matthewturk, willjasen",
"authorUrl": "https://github.com/willjasen/obsidian-binary-file-manager-plugin",
"isDesktopOnly": false
}
26 changes: 13 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@
"author": "qawatake",
"license": "MIT",
"devDependencies": {
"@types/node": "^17.0.5",
"@typescript-eslint/eslint-plugin": "^5.8.1",
"@typescript-eslint/parser": "^5.8.1",
"builtin-modules": "^3.2.0",
"esbuild": "~0.13.12",
"eslint": "^8.5.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-react": "^7.28.0",
"@types/node": "^22.4.1",
"@typescript-eslint/eslint-plugin": "^8.2.0",
"@typescript-eslint/parser": "^8.2.0",
"builtin-modules": "^4.0.0",
"esbuild": "~0.23.1",
"eslint": "^9.9.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react": "^7.35.0",
"npm-run-all": "^4.1.5",
"obsidian": "^0.13.11",
"prettier": "^2.5.1",
"tslib": "^2.3.1",
"typescript": "^4.5.4"
"obsidian": "^1.6.6",
"prettier": "^3.3.3",
"tslib": "^2.6.3",
"typescript": "^5.5.4"
},
"dependencies": {
"@popperjs/core": "^2.11.0"
"@popperjs/core": "^2.11.8"
}
}
134 changes: 108 additions & 26 deletions src/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,10 @@ import {
Plugin,
} from 'obsidian';
import { UncoveredApp } from 'Uncover';
import { retry } from 'Util';
import { retry, sleep } from 'Util';

const TEMPLATER_PLUGIN_NAME = 'templater-obsidian';
const DEFAULT_TEMPLATE_CONTENT = `![[{{PATH}}]]
LINK: [[{{PATH}}]]
CREATED At: {{CDATE:YYYY-MM-DD}}
FILE TYPE: {{EXTENSION:UP}}
`;
const DEFAULT_TEMPLATE_CONTENT = ``;

const RETRY_NUMBER = 1000;
const TIMEOUT_MILLISECOND = 1000;
Expand All @@ -35,6 +31,17 @@ export class MetaDataGenerator {
return false;
}

let filePath = file.path.toString();
let folderPath = filePath.substring(0,filePath.lastIndexOf("/"));

if (
!(folderPath === (
normalizePath(this.plugin.settings.binaryFilePath)
))
) {
return false;
}

const matchedExtension =
this.plugin.fileExtensionManager.getExtensionMatchedBest(file.name);
if (!matchedExtension) {
Expand All @@ -48,13 +55,26 @@ export class MetaDataGenerator {
return true;
}

async create(file: TFile) {

/**
* Create a metadata note for a binary file.
* @param file The binary file
* @param baseDir Optional base directory for the note and attachments (default: plugin.settings.folder)
*/
/**
* Create a metadata note for a binary file.
* @param file The binary file
* @param baseDir Optional base directory for the note and attachments (default: plugin.settings.folder)
* @param templatePathOverride Optional template path to use instead of the default
*/
async create(file: TFile, baseDir?: string, templatePathOverride?: string) {
const folder = baseDir || this.plugin.settings.folder;
const attachmentsFolder = `${folder}/_attachments`;
const metaDataFileName = this.uniquefyMetaDataFileName(
this.generateMetaDataFileName(file)
this.generateMetaDataFileName(file), folder
);
const metaDataFilePath = `${this.plugin.settings.folder}/${metaDataFileName}`;

await this.createMetaDataFile(metaDataFilePath, file as TFile);
const metaDataFilePath = `${folder}/${metaDataFileName}`;
await this.createMetaDataFile(metaDataFilePath, file as TFile, attachmentsFolder, templatePathOverride);
}

private generateMetaDataFileName(file: TFile): string {
Expand All @@ -66,9 +86,9 @@ export class MetaDataGenerator {
return metaDataFileName;
}

private uniquefyMetaDataFileName(metaDataFileName: string): string {
private uniquefyMetaDataFileName(metaDataFileName: string, folder: string): string {
const metaDataFilePath = normalizePath(
`${this.plugin.settings.folder}/${metaDataFileName}`
`${folder}/${metaDataFileName}`
);
if (this.app.vault.getAbstractFileByPath(metaDataFilePath)) {
return `CONFLICT-${moment().format(
Expand All @@ -79,20 +99,60 @@ export class MetaDataGenerator {
}
}

private uniquefyBinaryFileName(binaryFileName: string, attachmentsFolder: string): string {
const attachmentFullFilePath = attachmentsFolder+"/"+binaryFileName;
if (this.app.vault.getAbstractFileByPath(attachmentFullFilePath)) {
return `CONFLICT-${moment().format(
'YYYY-MM-DD-hh-mm-ss'
)}-${binaryFileName}`;
} else {
return binaryFileName;
}
}

private async moveBinaryFile(
binaryFile: TFile,
attachmentsFolder: string
): Promise<string> {
const binaryFileName = this.uniquefyBinaryFileName(
binaryFile.basename+"."+binaryFile.extension,
attachmentsFolder
);
const fullFilePath = attachmentsFolder+"/"+binaryFileName;
// Ensure attachments folder exists
let folder = this.app.vault.getAbstractFileByPath(attachmentsFolder);
if (!folder) {
await this.app.vault.createFolder(attachmentsFolder);
}
// move binary file into the attachments folder
try {
await this.app.fileManager.renameFile(binaryFile, fullFilePath);
new Notice(`Binary file of ${binaryFileName} has been moved.`);
return fullFilePath;
} catch (err) {
new Notice(`Problem moving the binary file of ${binaryFileName} into the attachments folder.`);
alert(err);
return binaryFile.path;
}
}

private async createMetaDataFile(
metaDataFilePath: string,
binaryFile: TFile
binaryFile: TFile,
attachmentsFolder: string,
templatePathOverride?: string
): Promise<void> {
const templateContent = await this.fetchTemplateContent();

const templateContent = await this.fetchTemplateContent(templatePathOverride);
// Move the binary file to the attachments folder
const fullFilePath = await this.moveBinaryFile(binaryFile, attachmentsFolder);
// process by Templater
const templaterPlugin = await this.getTemplaterPlugin();
if (!(this.plugin.settings.useTemplater && templaterPlugin)) {
this.app.vault.create(
metaDataFilePath,
this.plugin.formatter.format(
templateContent,
binaryFile.path,
fullFilePath,
binaryFile.stat.ctime
)
);
Expand All @@ -101,46 +161,44 @@ export class MetaDataGenerator {
metaDataFilePath,
''
);

try {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const content = await templaterPlugin.templater.parse_template(
{ target_file: targetFile, run_mode: 4 },
this.plugin.formatter.format(
templateContent,
binaryFile.path,
fullFilePath,
binaryFile.stat.ctime
)
);
this.app.vault.modify(targetFile, content);
} catch (err) {
new Notice(
'ERROR in Binary File Manager Plugin: failed to connect to Templater. Your Templater version may not be supported'
'ERROR in Binary File Manager Plugin: failed to connect to Templater. Your Templater version may not be supported.'
);
console.log(err);
}
}
}

private async fetchTemplateContent(): Promise<string> {
if (this.plugin.settings.templatePath === '') {
private async fetchTemplateContent(templatePathOverride?: string): Promise<string> {
const templatePath = templatePathOverride !== undefined ? templatePathOverride : this.plugin.settings.templatePath;
if (templatePath === '') {
return DEFAULT_TEMPLATE_CONTENT;
}

const templateFile = await retry(
() => {
return this.app.vault.getAbstractFileByPath(
this.plugin.settings.templatePath
);
return this.app.vault.getAbstractFileByPath(templatePath);
},
TIMEOUT_MILLISECOND,
RETRY_NUMBER,
(abstractFile) => abstractFile !== null
);

if (!(templateFile instanceof TFile)) {
const msg = `Template file ${this.plugin.settings.templatePath} is invalid`;
const msg = `Template file ${templatePath} is invalid`;
console.log(msg);
new Notice(msg);
return DEFAULT_TEMPLATE_CONTENT;
Expand Down Expand Up @@ -182,4 +240,28 @@ export class MetaDataGenerator {

return unlinkedBinaries;
}

findLinkedBinaries(): TFile[] {
const linkedBinaries: TFile[] = [];
const linkedPaths = new Set<string>();

// collect all link destinations
Object.values(this.app.metadataCache.resolvedLinks).forEach((links) => {
Object.keys(links).forEach((dest) => {
linkedPaths.add(dest);
});
});

// collect only unlinked binaries
this.app.vault.getFiles().forEach((file) => {
const isUnlinkedBinary =
!linkedPaths.has(file.path) &&
this.plugin.fileExtensionManager.verify(file.path);
if (!isUnlinkedBinary) {
linkedBinaries.push(file);
}
});

return linkedBinaries;
}
}
Loading