Skip to content

Commit

Permalink
+ add api for other plugins(References #3)
Browse files Browse the repository at this point in the history
+ Better settings interface
~ migrate back to esbuild as the erroneous dependency is no longer used.
  • Loading branch information
joethei committed Nov 22, 2021
1 parent 5745116 commit 5329ddd
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 81 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# top-most EditorConfig file
root = true

[*]
charset = utf-8
insert_final_newline = true
indent_style = tab
indent_size = 4
tab_width = 4
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
npm node_modules
build
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ Plugin for [Obsidian](https://obsidian.md)
![GitHub](https://img.shields.io/github/license/joethei/obsidian-tts)
[![libera manifesto](https://img.shields.io/badge/libera-manifesto-lightgrey.svg)](https://liberamanifesto.com)
---
**This plugin is currently in beta**

You can create language specific voices, which will be used when you have a note
with
```lang: {language name}```
```lang: {languageCode}```
in the [Frontmatter](https://help.obsidian.md/Advanced+topics/YAML+front+matter).
The language code can be seen in the settings and is a two letter [ISO 639-1](https://www.loc.gov/standards/iso639-2/php/English_list.php) code.


This plugin will not work on android due to [this bug in the Webview](https://bugs.chromium.org/p/chromium/issues/detail?id=487255).
This plugin will **NOT** work on android due to [this bug in the Webview](https://bugs.chromium.org/p/chromium/issues/detail?id=487255).


## Adding languages
Expand All @@ -23,3 +25,18 @@ to add a new language reference the documentation accordingly:
- [MacOS](https://support.apple.com/guide/mac-help/change-the-system-language-mh26684/mac)
- [iOS](https://support.apple.com/guide/iphone/change-the-language-and-region-iphce20717a3/ios)
<!--- [Android](https://support.google.com/accessibility/android/answer/6006983?hl=en)-->

## API
You can use this plugins API to add Text to Speech capabilities to your plugin.

```js
//@ts-ignore
if(this.app.plugins.plugins["obsidian-tts"]) {//check if the plugin is loaded
//@ts-ignore
await this.app.plugins.plugins["obsidian-tts"].say(title, text, language);//language is optional
}
```
Parameters:
- title: Title of your text, will only be spoken if the the user has the setting enabled
- text
- language(optional): language code according to the [ISO 639-1](https://www.loc.gov/standards/iso639-2/php/English_list.php), if there is no voice configured for that language, the plugin will use the default voice.
29 changes: 29 additions & 0 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import esbuild from "esbuild";
import process from "process";
import builtins from 'builtin-modules'

const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
https://github.com/joethei/obsidian-rss
*/
`;

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));
4 changes: 2 additions & 2 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"id": "obsidian-tts",
"name": "Text to Speech",
"version": "0.1.0",
"version": "0.2.0",
"minAppVersion": "0.12.0",
"description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.",
"description": "Text to speech for Obsidian. Hear your notes.",
"author": "Johannes Theiner",
"authorUrl": "https://github.com/joethei",
"isDesktopOnly": false
Expand Down
12 changes: 5 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
{
"name": "obsidian-tts",
"version": "0.1.0",
"version": "0.2.0",
"description": "Text to speech for Obsidian. Hear your notes.",
"main": "main.js",
"scripts": {
"dev": "rollup --config rollup.config.js -w",
"build": "rollup --config rollup.config.js --environment BUILD:production",
"dev": "node esbuild.config.mjs",
"build": "node esbuild.config.mjs production",
"lint": "eslint . --ext .ts"
},
"keywords": [],
"author": "joethei",
"license": "GPL-3.0",
"devDependencies": {
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.0.6",
"@rollup/plugin-typescript": "^8.3.0",
"rollup": "^2.60.0",
"@cospired/i18n-iso-languages": "^3.1.1",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"builtin-modules": "^3.2.0",
"esbuild": "0.13.12",
"eslint": "^7.32.0",
"obsidian": "^0.12.17",
"tslib": "2.3.1",
Expand Down
31 changes: 0 additions & 31 deletions rollup.config.js

This file was deleted.

29 changes: 23 additions & 6 deletions src/LanguageVoiceModal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Modal, Setting} from "obsidian";
import {LanguageVoiceMap} from "./settings";
import {TextInputPrompt} from "./TextInputPrompt";
import TTSPlugin from "./main";
import languages from "@cospired/i18n-iso-languages";

export class LanguageVoiceModal extends Modal {
plugin: TTSPlugin;
Expand All @@ -25,13 +26,29 @@ export class LanguageVoiceModal extends Modal {

contentEl.empty();

//not know to rollup and webstorm, but exists in obsidian
//@ts-ignore
const languageNames = new Intl.DisplayNames(['en'], {type: 'language'});

new Setting(contentEl)
.setName("Language")
.setDesc("what name this will be referenced by in frontmatter")
.addText((text) => {
text.setValue(this.language)
.addDropdown(async (dropdown) => {

for (let languageCodeKey in languages.getAlpha2Codes()) {
//@ts-ignore
const displayNames = new Intl.DisplayNames([languageCodeKey], {type: 'language', fallback: 'none'});
if(displayNames) {
const name = displayNames.of(languageCodeKey);
if(name) {
dropdown.addOption(languageCodeKey, name);
}
}
}

dropdown
.setValue(this.language)
.onChange((value) => {
this.language = value;
this.language = value;
});
});

Expand All @@ -40,7 +57,7 @@ export class LanguageVoiceModal extends Modal {
.addDropdown(async (dropdown) => {
const voices = window.speechSynthesis.getVoices();
for (const voice of voices) {
dropdown.addOption(voice.name, voice.lang + " " + voice.name);
dropdown.addOption(voice.name, voice.name + " - " + languageNames.of(voice.lang));
}
dropdown
.setValue(this.voice)
Expand All @@ -55,7 +72,7 @@ export class LanguageVoiceModal extends Modal {
const input = new TextInputPrompt(this.app, "What do you want to hear?", "", "Hello world this is Text to speech running in obsidian", "Hello world this is Text to speech running in obsidian");
await input.openAndGetValue((async value => {
if (value.getValue().length === 0) return;
await this.plugin.playText(value.getValue(), this.voice);
await this.plugin.sayWithVoice('', value.getValue(), this.voice);
}));


Expand Down
74 changes: 47 additions & 27 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,17 +156,59 @@ export default class TTSPlugin extends Plugin {
});
}

async playText(text: string, voice?: string): Promise<void> {
const configuredVoice = (voice) ? voice : this.settings.defaultVoice;
async sayWithVoice(title: string, text: string, voice: string): Promise<void> {
console.log("saying " + voice);
let content = text;
if (!this.settings.speakSyntax) {
content = content.replace(/#/g, "");
content = content.replace("-", "");
content = content.replace("_", "");
}
if (!this.settings.speakLinks) {
content = content.replace(/(?:https?|ftp|file|data:):\/\/[\n\S]+/g, '');
}
if(!this.settings.speakCodeblocks) {
content = content.replace(/```[\s\S]*?```/g, '');
}

if (this.settings.speakTitle) {
content = title + " " + content;
}



const msg = new SpeechSynthesisUtterance();
msg.text = text;
msg.text = content;
msg.volume = this.settings.volume;
msg.rate = this.settings.rate;
msg.pitch = this.settings.pitch;
msg.voice = window.speechSynthesis.getVoices().filter(voice => voice.name === configuredVoice)[0];
msg.voice = window.speechSynthesis.getVoices().filter(otherVoice => otherVoice.name === voice)[0];
window.speechSynthesis.speak(msg);
this.statusbar.setText("TTS: playing");
}


getVoice(languageCode: string) : string {
const filtered = this.settings.languageVoices.filter((lang) => lang.language === languageCode);
if (filtered.length === 0) return null;
return filtered[0].voice;
}

async say(title: string, text: string, languageCode?: string): Promise<void> {
let usedVoice = this.settings.defaultVoice;
if (languageCode) {
const voice = this.getVoice(languageCode);
if (voice) {
usedVoice = voice;
} else {
new Notice("TTS: could not find voice for language " + languageCode + ". Using default voice.");
}
}

await this.sayWithVoice(title, text, usedVoice);
}


async play(view: MarkdownView): Promise<void> {
let content = view.getViewData();
let language: string;
Expand All @@ -186,30 +228,8 @@ export default class TTSPlugin extends Plugin {
content = content.substring(content.indexOf("---") + 1);
}

if (!this.settings.speakSyntax) {
content = content.replace(/#/g, "");
content = content.replace("-", "");
content = content.replace("_", "");
}
if (!this.settings.speakLinks) {
content = content.replace(/(?:https?|ftp|file|data:):\/\/[\n\S]+/g, '');
}
if (this.settings.speakTitle) {
content = view.getDisplayText() + content;
}

if (language != undefined) {
const entry = this.settings.languageVoices.filter(item => item.language == language)[0];
if (!entry) {
new Notice("TTS: could not find voice for language " + language);
return;
}
await this.playText(content, entry.voice);
} else {
await this.playText(content);
}
await this.say(view.getDisplayText(), content, language);

this.statusbar.setText("TTS: playing");
}

async onunload(): Promise<void> {
Expand Down
26 changes: 21 additions & 5 deletions src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface TTSSettings {
speakLinks: boolean;
speakFrontmatter: boolean;
speakSyntax: boolean;
speakCodeblocks: boolean;
speakTitle: boolean;
languageVoices: LanguageVoiceMap[];
}
Expand All @@ -29,6 +30,7 @@ export const DEFAULT_SETTINGS: TTSSettings = {
speakFrontmatter: false,
speakSyntax: false,
speakTitle: true,
speakCodeblocks: false,
languageVoices: []
}

Expand All @@ -52,7 +54,7 @@ export class TTSSettingsTab extends PluginSettingTab {
.addDropdown(async (dropdown) => {
const voices = window.speechSynthesis.getVoices();
for (const voice of voices) {
dropdown.addOption(voice.name, voice.lang + " " + voice.name);
dropdown.addOption(voice.name, voice.name);
}
dropdown
.setValue(this.plugin.settings.defaultVoice)
Expand All @@ -68,7 +70,7 @@ export class TTSSettingsTab extends PluginSettingTab {
const input = new TextInputPrompt(this.app, "What do you want to hear?", "", "Hello world this is Text to speech running in obsidian", "Hello world this is Text to speech running in obsidian");
await input.openAndGetValue((async value => {
if (value.getValue().length === 0) return;
await this.plugin.playText(value.getValue());
await this.plugin.say('', value.getValue());
}));


Expand All @@ -82,7 +84,7 @@ export class TTSSettingsTab extends PluginSettingTab {
.setDesc("Add a new language specific voice")
.addButton((button: ButtonComponent): ButtonComponent => {
return button
.setTooltip("add new Feed")
.setTooltip("add new language specific voice")
.setIcon("create-new")
.onClick(async () => {
const modal = new LanguageVoiceModal(this.plugin);
Expand All @@ -107,9 +109,12 @@ export class TTSSettingsTab extends PluginSettingTab {

const voicesDiv = additionalContainer.createDiv("voices");
for (const languageVoice of this.plugin.settings.languageVoices) {
console.log(languageVoice);

const setting = new Setting(voicesDiv);
setting.setName(languageVoice.language);
//@ts-ignore
const displayNames = new Intl.DisplayNames([languageVoice.language], {type: 'language', fallback: 'none'});
const setting = new Setting(voicesDiv);
setting.setName(displayNames.of(languageVoice.language) + " - " + languageVoice.language);
setting.setDesc(languageVoice.voice);

setting
Expand Down Expand Up @@ -244,6 +249,17 @@ export class TTSSettingsTab extends PluginSettingTab {
});
});

new Setting(containerEl)
.setName("Codeblocks")
.addToggle(async (toggle) => {
toggle
.setValue(this.plugin.settings.speakCodeblocks)
.onChange(async (value) => {
this.plugin.settings.speakCodeblocks = value;
await this.plugin.saveSettings();
});
});

new Setting(containerEl)
.setName("Syntax")
.addToggle(async (toggle) => {
Expand Down
3 changes: 2 additions & 1 deletion versions.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"0.1.0": "0.9.12"
"0.1.0": "0.9.12",
"0.2.0": "0.9.12"
}

0 comments on commit 5329ddd

Please sign in to comment.