Skip to content

Commit 5057710

Browse files
committed
feat: ✨ introduce new command to adjust filenames
add a new command that will clean up the filenames of all notes in your Readwise Library folder. If enabled, it will also slugify the names.
1 parent 76044a7 commit 5057710

File tree

1 file changed

+96
-9
lines changed

1 file changed

+96
-9
lines changed

Diff for: src/main.ts

+96-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import slugify from '@sindresorhus/slugify';
22
import filenamify from 'filenamify';
33
import { type ConfigureOptions, Environment, Template } from 'nunjucks';
4-
import { type CachedMetadata, Plugin, type TFile, normalizePath } from 'obsidian';
4+
import { Plugin, TFile, TFolder, normalizePath } from 'obsidian';
55
import { AuthorParser } from 'services/author-parser';
66
import { DeduplicatingVaultWriter } from 'services/deduplicating-vault-writer';
77
import { FrontmatterManager } from 'services/frontmatter-manager';
@@ -341,12 +341,14 @@ export default class ReadwiseMirror extends Plugin {
341341
const template = this.settings.filenameTemplate;
342342
const context = {
343343
title: book.title,
344-
author: this.settings.normalizeAuthorNames ?
345-
new AuthorParser({
346-
normalizeCase: true,
347-
removeTitles: this.settings.stripTitlesFromAuthors
348-
}).parse(book.author).join(', ') :
349-
book.author,
344+
author: this.settings.normalizeAuthorNames
345+
? new AuthorParser({
346+
normalizeCase: true,
347+
removeTitles: this.settings.stripTitlesFromAuthors,
348+
})
349+
.parse(book.author)
350+
.join(', ')
351+
: book.author,
350352
category: book.category,
351353
source: book.source_url,
352354
book_id: book.user_book_id,
@@ -356,7 +358,17 @@ export default class ReadwiseMirror extends Plugin {
356358
filename = book.title;
357359
}
358360

359-
const normalizedTitle = this.settings.useSlugify
361+
return this.normalizeFilename(filename);
362+
}
363+
364+
/**
365+
* Normalizes the filename by replacing critical characters
366+
* and ensuring it is a valid filename
367+
* @param filename - The filename to normalize
368+
* @returns The normalized filename
369+
*/
370+
private normalizeFilename(filename: string) {
371+
const normalizedFilename = this.settings.useSlugify
360372
? slugify(filename.replace(/:/g, this.settings.colonSubstitute ?? '-'), {
361373
separator: this.settings.slugifySeparator,
362374
lowercase: this.settings.slugifyLowercase,
@@ -372,7 +384,7 @@ export default class ReadwiseMirror extends Plugin {
372384
.replace(/ +/g, ' ')
373385
.trim();
374386

375-
return normalizePath(normalizedTitle);
387+
return normalizePath(normalizedFilename);
376388
}
377389

378390
async deleteLibraryFolder() {
@@ -468,6 +480,72 @@ export default class ReadwiseMirror extends Plugin {
468480
return spacetime.now().since(spacetime(this.settings.lastUpdated)).rounded;
469481
}
470482

483+
/**
484+
* Handles the adjustment of filenames in the Readwise folder.
485+
*/
486+
async handleFilenameAdjustment() {
487+
const vault = this.app.vault;
488+
const path = `${this.settings.baseFolderName}`;
489+
const readwiseFolder = vault.getAbstractFileByPath(path);
490+
if (readwiseFolder && readwiseFolder instanceof TFolder) {
491+
// Iterate all files in the Readwise folder and "fix" their names according to the current settings using
492+
// this.normalizeFilename()
493+
const renamedFiles = await this.iterativeReadwiseRenamer(readwiseFolder);
494+
if (renamedFiles > 0) {
495+
this.notify.notice(`Readwise: Renamed ${renamedFiles} files. Check console for renaming errors.`);
496+
} else {
497+
this.notify.notice('Readwise: No files renamed. Check console for renaming errors.');
498+
}
499+
}
500+
}
501+
502+
/**
503+
* Iteratively renames files in the Readwise folder.
504+
* @param folder - The folder to iterate through
505+
* @returns
506+
*/
507+
private async iterativeReadwiseRenamer(folder: TFolder): Promise<number> {
508+
const files = folder.children;
509+
let countRenamed = 0;
510+
for (const file of files) {
511+
if (file instanceof TFolder) {
512+
// Skip folders
513+
countRenamed += await this.iterativeReadwiseRenamer(file);
514+
}
515+
516+
if (file instanceof TFile && file.extension === 'md') {
517+
const result = await this.renameReadwiseNote(file);
518+
if (result) {
519+
countRenamed++;
520+
}
521+
}
522+
}
523+
return countRenamed;
524+
}
525+
526+
/**
527+
* Formats the filename of a Readwise note based on the settings.
528+
*
529+
* @param file The file to format.
530+
*/
531+
private async renameReadwiseNote(file: TFile): Promise<boolean> {
532+
const newFilename = this.normalizeFilename(file.basename);
533+
534+
// Only rename if there's a difference
535+
if (newFilename !== file.basename) {
536+
const newPath = `${file.parent.path}/${newFilename}.md`;
537+
try {
538+
await this.app.fileManager.renameFile(file, newPath);
539+
this.logger.info(`Renamed file '${file.name}' to '${newFilename}.md'`);
540+
return true;
541+
} catch (error) {
542+
this.logger.error(`Error renaming file: '${file.name}' to '${newFilename}.md': ${error}`);
543+
return false;
544+
}
545+
}
546+
return false;
547+
}
548+
471549
// Reload settings after external change (e.g. after sync)
472550
async onExternalSettingsChange() {
473551
this.logger.info('Reloading settings due to external change');
@@ -566,6 +644,15 @@ export default class ReadwiseMirror extends Plugin {
566644
callback: this.sync.bind(this),
567645
});
568646

647+
this.addCommand({
648+
id: 'adjust-filenames',
649+
name: 'Adjust Filenames to current settings',
650+
callback: async () => {
651+
this.notify.notice('Readwise: Filename adjustment started');
652+
await this.handleFilenameAdjustment();
653+
},
654+
});
655+
569656
this.registerInterval(
570657
window.setInterval(() => {
571658
if (/Synced/.test(this.notify.getStatusBarText())) {

0 commit comments

Comments
 (0)