Skip to content

Commit

Permalink
Upgraded file path generation to accommodate different system types, …
Browse files Browse the repository at this point in the history
…added fix for Windows file naming, and added feature to embed generated mp3 after selected text in a note
  • Loading branch information
travisvn committed Dec 3, 2024
1 parent 6c2e41a commit cfa44e9
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 39 deletions.
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "edge-tts",
"name": "Edge TTS",
"version": "1.4.0",
"version": "1.5.0",
"minAppVersion": "0.15.0",
"description": "Read notes aloud using Microsoft Edge Read Aloud API (free, high quality text-to-speech).",
"author": "Travis",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "obsidian-edge-tts",
"version": "1.4.0",
"version": "1.5.0",
"description": "This is a TTS plugin for Obsidian (https://obsidian.md).",
"main": "main.js",
"scripts": {
Expand Down
113 changes: 77 additions & 36 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Plugin, MarkdownView, Notice, PluginSettingTab, Setting, Editor, Markdo
import { EdgeTTSClient, OUTPUT_FORMAT, ProsodyOptions } from 'edge-tts-client';
import { filterFrontmatter, filterMarkdown } from 'src/utils';

import * as path from 'path';
import * as os from 'os';

// Top voices to be displayed in the dropdown
const TOP_VOICES = [
'en-US-AndrewNeural',
Expand Down Expand Up @@ -47,6 +50,8 @@ const DEFAULT_SETTINGS: EdgeTTSPluginSettings = {
replaceSpacesInFilenames: false,
}

const defaultSelectedTextMp3Name = 'note'

export default class EdgeTTSPlugin extends Plugin {
settings: EdgeTTSPluginSettings;
ribbonIconEl: HTMLElement | null = null;
Expand Down Expand Up @@ -206,48 +211,76 @@ export default class EdgeTTSPlugin extends Plugin {
}
}

async embedMP3InNote(fileName: string, folderPath: string, filePath: string): Promise<void> {
// Ensure the file exists and is a markdown file
const file = this.app.vault.getAbstractFileByPath(filePath);
if (!(file instanceof TFile)) {
if (this.settings.showNotices) new Notice(`File not found or is not a valid markdown file: ${filePath}`);
return;
}

async embedMP3InNote(relativePath: string, filePath?: string, editor?: Editor): Promise<void> {
try {
// Read the existing content of the note
const content = await this.app.vault.read(file);
if (editor) {
// Get the selected text and its position in the editor
const selectedText = editor.getSelection();
const cursorPosition = editor.getCursor('to');

// Construct the embed link
const embedLink = `\n\n![[${relativePath}]]\n`;

// Insert the embed link after the selected text
if (selectedText) {
editor.replaceSelection(`${selectedText}${embedLink}`);
} else {
// If no selection, insert at the cursor position
editor.replaceRange(embedLink, cursorPosition);
}

// Construct the embed link
const embedLink = `![[${folderPath}/${fileName}]]`;
if (this.settings.showNotices) new Notice('MP3 embedded after the selected text.');
} else {
if (!filePath) {
console.error('Error embedding MP3 in note due to filePath and editor not being passed to embedMP3InNote');
if (this.settings.showNotices) new Notice('Failed to embed MP3 in note.');
return;
}

// Fallback for non-editor scenarios
const file = this.app.vault.getAbstractFileByPath(filePath);
if (!(file instanceof TFile)) {
if (this.settings.showNotices) new Notice(`File not found or is not a valid markdown file: ${filePath}`);
return;
}

// Read the existing content of the note
const content = await this.app.vault.read(file);

// Append the embed link to the content
const updatedContent = `${content}\n\n${embedLink}`;
// Construct the embed link
const embedLink = `![[${relativePath}]]`;

// Write the updated content back to the note
await this.app.vault.modify(file, updatedContent);
// Append the embed link to the content
const updatedContent = `${content}\n\n${embedLink}`;

if (this.settings.showNotices) new Notice('MP3 embedded in note.');
// Write the updated content back to the note
await this.app.vault.modify(file, updatedContent);

if (this.settings.showNotices) new Notice('MP3 embedded in note.');
}
} catch (error) {
console.error('Error embedding MP3 in note:', error);
if (this.settings.showNotices) new Notice('Failed to embed MP3 in note.');
}
}

async saveMP3File(buffer: Buffer, filePath?: string): Promise<void> {
async saveMP3File(buffer: Buffer, filePath?: string): Promise<string | null> {
const adapter = this.app.vault.adapter;

if (!(adapter instanceof FileSystemAdapter)) {
console.error('File system adapter not available.');
if (this.settings.showNotices) new Notice('Unable to save MP3 file.');
return;
return null;
}

const basePath = adapter.getBasePath();
const fallbackFolderName = (this.settings.replaceSpacesInFilenames) ? 'Note_Narration_Audio' : 'Note Narration Audio';
const fallbackFolderName = this.settings.replaceSpacesInFilenames
? 'Note_Narration_Audio'
: 'Note Narration Audio';
const folderPath = this.settings.outputFolder || fallbackFolderName;

const relativeFolderPath = folderPath; // Path relative to vault root
const absoluteFolderPath = `${basePath}/${relativeFolderPath}`; // Full system path
const absoluteFolderPath = path.join(basePath, relativeFolderPath); // Platform-agnostic path

try {
// Ensure the relative output folder exists
Expand All @@ -266,41 +299,43 @@ export default class EdgeTTSPlugin extends Plugin {
hour12: false,
}).format(now);

// Replace commas with spaces and trim
let sanitizedDate = formattedDate.replace(/,/g, '').trim();

// Check for Windows and adjust the date format to remove colons
if (os.platform() === 'win32') {
sanitizedDate = sanitizedDate.replace(/:/g, '-');
}

// Generate the file name
let noteName = filePath ? filePath.split('/').pop()?.replace('.md', '') || 'note' : 'note';
let noteName = filePath
? path.basename(filePath, '.md') || defaultSelectedTextMp3Name
: defaultSelectedTextMp3Name;

// Replace spaces with underscores or dashes if the setting is enabled
if (this.settings.replaceSpacesInFilenames) {
noteName = noteName.replace(/\s+/g, '_'); // Use underscores or replace '_' with '-' if desired
noteName = noteName.replace(/\s+/g, '_');
sanitizedDate = sanitizedDate.replace(/\s+/g, '_');
}

const fileName = (this.settings.replaceSpacesInFilenames)
const fileName = this.settings.replaceSpacesInFilenames
? `${noteName}_-_${sanitizedDate}.mp3`
: `${noteName} - ${sanitizedDate}.mp3`;
const relativeFilePath = `${relativeFolderPath}/${fileName}`;
const absoluteFilePath = `${absoluteFolderPath}/${fileName}`;
const relativeFilePath = path.join(relativeFolderPath, fileName);
const absoluteFilePath = path.join(absoluteFolderPath, fileName);

// Explicitly create an empty file before writing
await adapter.write(relativeFilePath, '');

// Write the MP3 file
await adapter.writeBinary(relativeFilePath, buffer);

if (this.settings.showNotices) {
if (this.settings.showNotices) new Notice(`MP3 saved to: ${absoluteFilePath}`);
}
if (this.settings.showNotices) new Notice(`MP3 saved to: ${absoluteFilePath}`);

// Optionally embed the MP3 in the note
if (this.settings.embedInNote && filePath) {
await this.embedMP3InNote(fileName, folderPath, filePath);
}
// return fileName; // Return the file name
return relativeFilePath;
} catch (error) {
console.error('Error saving MP3:', error);
if (this.settings.showNotices) new Notice('Failed to save MP3 file.');
return null;
}
}

Expand Down Expand Up @@ -343,7 +378,13 @@ export default class EdgeTTSPlugin extends Plugin {
readable.on('data', (data: Uint8Array) => audioBuffer.push(data));
readable.on('end', async () => {
const completeBuffer = Buffer.concat(audioBuffer);
await this.saveMP3File(completeBuffer, filePath);

// Save MP3 file and get fileName
const savedMp3RelativePath = await this.saveMP3File(completeBuffer, filePath);

if (savedMp3RelativePath && this.settings.embedInNote) {
await this.embedMP3InNote(savedMp3RelativePath, filePath, editor);
}
});
} catch (error) {
console.error('Error generating MP3:', error);
Expand Down
3 changes: 2 additions & 1 deletion versions.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@
"1.1.0": "0.15.0",
"1.2.0": "0.15.0",
"1.3.0": "0.15.0",
"1.4.0": "0.15.0"
"1.4.0": "0.15.0",
"1.5.0": "0.15.0"
}

0 comments on commit cfa44e9

Please sign in to comment.