Skip to content

Commit

Permalink
Merge pull request #48 from Edo78/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Edo78 authored Feb 8, 2022
2 parents 82531b9 + d5e27dc commit 9d0fb24
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 65 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ The template receive the following arguments:
- `datetime`: 2022-01-22 09:57:29
- `page`: 19

### Book view configuration
The default template is minimal but complex
~~~markdown
# Title: <%= it.data.title %>

<progress value="<%= it.metadata.percent_finished %>" max="100"> </progress>
```dataviewjs
const title = dv.current()['koreader-sync'].metadata.managed_title
dv.pages().where(n => {
return n['koreader-sync'] && n['koreader-sync'].type == 'koreader-sync-note' && n['koreader-sync'].metadata.managed_book_title == title
}).sort(p => p['koreader-sync'].data.page).forEach(p => dv.paragraph(dv.fileLink(p.file.name, true), {style: 'test-css'}))
```
~~~
The core of this template is a js [dataview embedded](#dataview-embedded) query. Don't mess with it if you don't know what you are doing (I don't because I barely know Dataview).

The template receive exactly the same data you can see in the frontmatter. If it's not there you can't use it but you can create an issue asking for it.

#### Dataview embedded
Besides a native support for [Dataview](https://github.com/blacksmithgu/obsidian-dataview) (look at the [example](#dataview-examples)) the plugin let the user chose to automatically create a note for each book with a dataview query inside.
The note is created in the same folder of the notes of the book but can be moved and renamed and Obsidian will take care of updating the links.
Expand All @@ -63,6 +80,8 @@ The query itself will embed the single notes and a CSS will hide every `h2` and
Once the plugin is configured properly you can plug the device with KOReader and click on the icon with two documents and the tooltip `Sync your KOReader highlights`. The plugin should propmplty create a single file for each note.

### Commands
**NOTE:** if a command is suppose to set a frontmatter property equal to a certain value then it will be shown only if the open note has such property with a different value.

There are five commands:
- `Sync` it's the same as clicking on the plugin's icon, it's trigger the sync of the notes
- `Reset Sync List` empty the list of imported notes (see [Danger Zone](#danger-zone)). Always try to retrieve the deleted notes from trash before using this command because all the rightfully discarded notes will be imported again. This command will also disable itself so you have to enable in the settings again if you wish to use it again.
Expand Down
4 changes: 3 additions & 1 deletion src/koreader-metadata.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as fs from 'fs';
import * as path from 'path';

import { Books } from './types';
import finder from 'node-find-files';
import { parse } from 'lua-json';
import { Books } from './types';

export class KOReaderMetadata {
koreaderBasePath: string;
Expand All @@ -28,13 +28,15 @@ export class KOReaderMetadata {
bookmarks,
doc_props: { title },
doc_props: { authors },
percent_finished,
} = jsonMetadata;
if (Object.keys(highlight).length && Object.keys(bookmarks).length) {
metadatas[`${title} - ${authors}`] = {
title,
authors,
// highlight,
bookmarks,
percent_finished: percent_finished * 100,
};
}
}
Expand Down
220 changes: 156 additions & 64 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ interface KOReaderSettings {
keepInSync: boolean;
aFolderForEachBook: boolean;
customTemplate: boolean;
customDataviewTemplate: boolean;
templatePath?: string;
dataviewTemplatePath?: string;
createDataviewQuery: boolean;
importedNotes: { [key: string]: boolean };
enbleResetImportedNotes: boolean;
Expand All @@ -37,6 +39,7 @@ const DEFAULT_SETTINGS: KOReaderSettings = {
keepInSync: false,
aFolderForEachBook: false,
customTemplate: false,
customDataviewTemplate: false,
createDataviewQuery: false,
koreaderBasePath: '/media/user/KOBOeReader',
obsidianNoteFolder: '/',
Expand Down Expand Up @@ -110,48 +113,76 @@ export default class KOReader extends Plugin {
this.addCommand({
id: 'obsidian-koreader-plugin-set-edit',
name: 'Mark this note as Edited',
editorCallback: (editor: Editor, view: MarkdownView) => {
this.setFrontmatterProperty(
`${[KOREADERKEY]}.metadata.yet_to_be_edited`,
false,
view
);
editorCheckCallback: (
checking: boolean,
editor: Editor,
view: MarkdownView
) => {
const propertyPath = `${[KOREADERKEY]}.metadata.yet_to_be_edited`;
if (checking) {
if (this.getFrontmatterProperty(propertyPath, view) === true) {
return true;
}
return false;
}
this.setFrontmatterProperty(propertyPath, false, view);
},
});

this.addCommand({
id: 'obsidian-koreader-plugin-clear-edit',
name: 'Mark this note as NOT Edited',
editorCallback: (editor: Editor, view: MarkdownView) => {
this.setFrontmatterProperty(
`${[KOREADERKEY]}.metadata.yet_to_be_edited`,
true,
view
);
editorCheckCallback: (
checking: boolean,
editor: Editor,
view: MarkdownView
) => {
const propertyPath = `${[KOREADERKEY]}.metadata.yet_to_be_edited`;
if (checking) {
if (this.getFrontmatterProperty(propertyPath, view) === false) {
return true;
}
return false;
}
this.setFrontmatterProperty(propertyPath, true, view);
},
});

this.addCommand({
id: 'obsidian-koreader-plugin-set-sync',
name: 'Enable Sync for this note',
editorCallback: (editor: Editor, view: MarkdownView) => {
this.setFrontmatterProperty(
`${[KOREADERKEY]}.metadata.keep_in_sync`,
true,
view
);
editorCheckCallback: (
checking: boolean,
editor: Editor,
view: MarkdownView
) => {
const propertyPath = `${[KOREADERKEY]}.metadata.keep_in_sync`;
if (checking) {
if (this.getFrontmatterProperty(propertyPath, view) === false) {
return true;
}
return false;
}
this.setFrontmatterProperty(propertyPath, true, view);
},
});

this.addCommand({
id: 'obsidian-koreader-plugin-clear-sync',
name: 'Disable Sync for this note',
editorCallback: (editor: Editor, view: MarkdownView) => {
this.setFrontmatterProperty(
`${[KOREADERKEY]}.metadata.keep_in_sync`,
false,
view
);
editorCheckCallback: (
checking: boolean,
editor: Editor,
view: MarkdownView
) => {
const propertyPath = `${[KOREADERKEY]}.metadata.keep_in_sync`;
if (checking) {
if (this.getFrontmatterProperty(propertyPath, view) === true) {
return true;
}
return false;
}
this.setFrontmatterProperty(propertyPath, false, view);
},
});

Expand All @@ -161,7 +192,7 @@ export default class KOReader extends Plugin {
checkCallback: (checking: boolean) => {
if (this.settings.enbleResetImportedNotes) {
if (!checking) {
this.resetSyncList();
this.settings.importedNotes = {};
this.settings.enbleResetImportedNotes = false;
this.saveSettings();
}
Expand All @@ -184,11 +215,6 @@ export default class KOReader extends Plugin {
await this.saveData(this.settings);
}

private async resetSyncList() {
this.settings.importedNotes = {};
await this.saveSettings();
}

private getObjectProperty(object: { [x: string]: any }, path: string) {
if (path === undefined || path === null) {
return object;
Expand Down Expand Up @@ -219,19 +245,19 @@ export default class KOReader extends Plugin {
object[key] = value;
}

async setFrontmatterProperty(
property: string,
value: any,
view: MarkdownView
) {
const { data, content } = matter(view.data);
setFrontmatterProperty(property: string, value: any, view: MarkdownView) {
const { data, content } = matter(view.data, {});
this.setObjectProperty(data, property, value);
const val = this.getObjectProperty(data, property);
const note = matter.stringify(content, data);
view.setViewData(note, false);
view.requestSave();
}

getFrontmatterProperty(property: string, view: MarkdownView): any {
const { data, content } = matter(view.data, {});
return this.getObjectProperty(data, property);
}

private async createNote(note: {
path: string;
uniqueId: string;
Expand Down Expand Up @@ -310,37 +336,77 @@ Page: <%= it.page %>
return { content, frontmatterData, notePath };
}

async createDataviewQueryPerBook(dataview: {
path: string;
managedBookTitle: string;
title: string;
}) {
const { path, title, managedBookTitle } = dataview;
async createDataviewQueryPerBook(
dataview: {
path: string;
managedBookTitle: string;
book: Book;
},
updateNote?: TFile
) {
const { path, book, managedBookTitle } = dataview;
let { keepInSync } = this.settings;
if (updateNote) {
const { data, content } = matter(
await this.app.vault.read(updateNote),
{}
);
keepInSync = data[KOREADERKEY].metadata.keep_in_sync;
const yetToBeEdited = data[KOREADERKEY].metadata.yet_to_be_edited;
if (!keepInSync || !yetToBeEdited) {
return;
}
}
const frontMatter = {
cssclass: 'koreader-sync-dataview',
[KOREADERKEY]: {
uniqueId: crypto
.createHash('md5')
.update(`${book.title} - ${book.authors}}`)
.digest('hex'),
type: 'koreader-sync-dataview',
managed_title: managedBookTitle,
data: {
title: book.title,
authors: book.authors,
},
metadata: {
percent_finished: book.percent_finished,
managed_title: managedBookTitle,
keep_in_sync: keepInSync,
yet_to_be_edited: true,
},
},
};

const defaultTemplate = `# Title: <%= it.title %>
const defaultTemplate = `# Title: <%= it.data.title %>
<progress value="<%= it.metadata.percent_finished %>" max="100"> </progress>
\`\`\`dataviewjs
const title = dv.current()['koreader-sync'].managed_title
const title = dv.current()['koreader-sync'].metadata.managed_title
dv.pages().where(n => {
return n['koreader-sync'] && n['koreader-sync'].type == 'koreader-sync-note' && n['koreader-sync'].metadata.managed_book_title == title
}).sort(p => p['koreader-sync'].data.page).forEach(p => dv.paragraph(dv.fileLink(p.file.name, true), {style: 'test-css'}))
\`\`\`
`;
const template = defaultTemplate;
const content = (await eta.render(template, {
title,
})) as string;
this.app.vault.create(
`${path}/${managedBookTitle}.md`,
matter.stringify(content, frontMatter)
);

const templateFile = this.settings.customDataviewTemplate
? this.app.vault.getAbstractFileByPath(this.settings.dataviewTemplatePath)
: null;
const template = templateFile
? await this.app.vault.read(templateFile as TFile)
: defaultTemplate;
const content = (await eta.render(
template,
frontMatter[KOREADERKEY]
)) as string;
if (updateNote) {
this.app.vault.modify(updateNote, matter.stringify(content, frontMatter));
} else {
this.app.vault.create(
`${path}/${managedBookTitle}.md`,
matter.stringify(content, frontMatter)
);
}
}

async importNotes() {
Expand Down Expand Up @@ -382,15 +448,17 @@ return n['koreader-sync'] && n['koreader-sync'].type == 'koreader-sync-note' &&
}
}
// if createDataviewQuery is set, create a dataview query, for each book, with the book's managed title (if it doesn't exist)
if (
this.settings.createDataviewQuery &&
!this.app.vault.getAbstractFileByPath(`${path}/${managedBookTitle}.md`)
) {
this.createDataviewQueryPerBook({
path,
managedBookTitle,
title: data[book].title,
});
if (this.settings.createDataviewQuery) {
this.createDataviewQueryPerBook(
{
path,
managedBookTitle,
book: data[book],
},
this.app.vault.getAbstractFileByPath(
`${path}/${managedBookTitle}.md`
) as TFile
);
}

for (const bookmark in data[book].bookmarks) {
Expand Down Expand Up @@ -471,7 +539,6 @@ class KoreaderSettingTab extends PluginSettingTab {
.setPlaceholder('Enter the path wher KOReader is mounted')
.setValue(this.plugin.settings.koreaderBasePath)
.onChange(async (value) => {
console.log(`Path: ${value}`);
this.plugin.settings.koreaderBasePath = value;
await this.plugin.saveSettings();
})
Expand Down Expand Up @@ -564,6 +631,31 @@ class KoreaderSettingTab extends PluginSettingTab {
})
);

new Setting(containerEl)
.setName('Custom book template')
.setDesc('Use a custom template for the dataview')
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.customDataviewTemplate)
.onChange(async (value) => {
this.plugin.settings.customDataviewTemplate = value;
await this.plugin.saveSettings();
})
);

new Setting(containerEl)
.setName('Book template file')
.setDesc('The template file to use. Remember to add the ".md" extension')
.addText((text) =>
text
.setPlaceholder('templates/template-book.md')
.setValue(this.plugin.settings.dataviewTemplatePath)
.onChange(async (value) => {
this.plugin.settings.dataviewTemplatePath = value;
await this.plugin.saveSettings();
})
);

new Setting(containerEl)
.setName('Create a dataview query')
.setDesc(
Expand Down
1 change: 1 addition & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface Book {
authors: string;
bookmarks: Bookmarks;
highlight: any;
percent_finished: number;
}

export interface Books {
Expand Down

0 comments on commit 9d0fb24

Please sign in to comment.