mirror of
https://github.com/lenalebt/obsidian-pocketbook-cloud-highlight-importer.git
synced 2026-07-22 07:40:24 +00:00
first working version of the plugin
This commit is contained in:
parent
fa129bcf07
commit
739d7bd448
30 changed files with 6314 additions and 253 deletions
|
|
@ -1,10 +1,12 @@
|
|||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{js,ts,json,css,html}]
|
||||
indent_size = 2
|
||||
max_line_length = 180
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -20,3 +20,5 @@ data.json
|
|||
|
||||
# Exclude macOS Finder (System Explorer) View States
|
||||
.DS_Store
|
||||
|
||||
build/
|
||||
10
.prettierrc
Normal file
10
.prettierrc
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"arrowParens": "avoid",
|
||||
"bracketSpacing": true,
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 160
|
||||
}
|
||||
|
||||
34
README.md
34
README.md
|
|
@ -1,33 +1,12 @@
|
|||
# Obsidian Sample Plugin
|
||||
# Obsidian Pocketbook Cloud Highlights Plugin
|
||||
|
||||
This is a sample plugin for Obsidian (https://obsidian.md).
|
||||
This is a plugin for Obsidian (https://obsidian.md) to import highlights that you made on your Pocketbook E-Ink reader, or in the Pocketbook App on your phone.
|
||||
|
||||
This project uses Typescript to provide type checking and documentation.
|
||||
The repo depends on the latest plugin API (obsidian.d.ts) in Typescript Definition format, which contains TSDoc comments describing what it does.
|
||||
It is in its early stages of development, and I'm new to Typescript, so it does
|
||||
a few dumb things under the hood that should change soon, like storing passwords
|
||||
in clear text :-). You have been warned!
|
||||
|
||||
**Note:** The Obsidian API is still in early alpha and is subject to change at any time!
|
||||
|
||||
This sample plugin demonstrates some of the basic functionality the plugin API can do.
|
||||
- Changes the default font color to red using `styles.css`.
|
||||
- Adds a ribbon icon, which shows a Notice when clicked.
|
||||
- Adds a command "Open Sample Modal" which opens a Modal.
|
||||
- Adds a plugin setting tab to the settings page.
|
||||
- Registers a global click event and output 'click' to the console.
|
||||
- Registers a global interval which logs 'setInterval' to the console.
|
||||
|
||||
## First time developing plugins?
|
||||
|
||||
Quick starting guide for new plugin devs:
|
||||
|
||||
- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with.
|
||||
- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it).
|
||||
- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder.
|
||||
- Install NodeJS, then run `npm i` in the command line under your repo folder.
|
||||
- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`.
|
||||
- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`.
|
||||
- Reload Obsidian to load the new version of your plugin.
|
||||
- Enable plugin in settings window.
|
||||
- For updates to the Obsidian API run `npm update` in the command line under your repo folder.
|
||||
Requires the "dataview" plugin to work correctly - and does not check that it's installed ;-).
|
||||
|
||||
## Releasing new releases
|
||||
|
||||
|
|
@ -58,6 +37,7 @@ Quick starting guide for new plugin devs:
|
|||
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`.
|
||||
|
||||
## Improve code quality with eslint (optional)
|
||||
|
||||
- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code.
|
||||
- To use eslint with this project, make sure to install eslint from terminal:
|
||||
- `npm install -g eslint`
|
||||
|
|
|
|||
|
|
@ -1,43 +1,43 @@
|
|||
import esbuild from "esbuild";
|
||||
import process from "process";
|
||||
import builtins from "builtin-modules";
|
||||
import esbuild from 'esbuild';
|
||||
import process from 'process';
|
||||
import builtins from 'builtin-modules';
|
||||
|
||||
const banner =
|
||||
`/*
|
||||
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
|
||||
*/
|
||||
`;
|
||||
|
||||
const prod = (process.argv[2] === "production");
|
||||
const prod = process.argv[2] === 'production';
|
||||
|
||||
const context = await esbuild.context({
|
||||
banner: {
|
||||
js: banner,
|
||||
},
|
||||
entryPoints: ["main.ts"],
|
||||
entryPoints: ['src/main.ts'],
|
||||
bundle: true,
|
||||
external: [
|
||||
"obsidian",
|
||||
"electron",
|
||||
"@codemirror/autocomplete",
|
||||
"@codemirror/collab",
|
||||
"@codemirror/commands",
|
||||
"@codemirror/language",
|
||||
"@codemirror/lint",
|
||||
"@codemirror/search",
|
||||
"@codemirror/state",
|
||||
"@codemirror/view",
|
||||
"@lezer/common",
|
||||
"@lezer/highlight",
|
||||
"@lezer/lr",
|
||||
...builtins],
|
||||
format: "cjs",
|
||||
target: "es2018",
|
||||
logLevel: "info",
|
||||
sourcemap: prod ? false : "inline",
|
||||
'obsidian',
|
||||
'electron',
|
||||
'@codemirror/autocomplete',
|
||||
'@codemirror/collab',
|
||||
'@codemirror/commands',
|
||||
'@codemirror/language',
|
||||
'@codemirror/lint',
|
||||
'@codemirror/search',
|
||||
'@codemirror/state',
|
||||
'@codemirror/view',
|
||||
'@lezer/common',
|
||||
'@lezer/highlight',
|
||||
'@lezer/lr',
|
||||
...builtins,
|
||||
],
|
||||
format: 'cjs',
|
||||
target: 'es2018',
|
||||
logLevel: 'info',
|
||||
sourcemap: prod ? false : 'inline',
|
||||
treeShaking: true,
|
||||
outfile: "main.js",
|
||||
outfile: 'build/main.js',
|
||||
});
|
||||
|
||||
if (prod) {
|
||||
|
|
|
|||
137
main.ts
137
main.ts
|
|
@ -1,137 +0,0 @@
|
|||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
|
||||
|
||||
// Remember to rename these classes and interfaces!
|
||||
|
||||
interface MyPluginSettings {
|
||||
mySetting: string;
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: MyPluginSettings = {
|
||||
mySetting: 'default'
|
||||
}
|
||||
|
||||
export default class MyPlugin extends Plugin {
|
||||
settings: MyPluginSettings;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
// This creates an icon in the left ribbon.
|
||||
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
|
||||
// Called when the user clicks the icon.
|
||||
new Notice('This is a notice!');
|
||||
});
|
||||
// Perform additional things with the ribbon
|
||||
ribbonIconEl.addClass('my-plugin-ribbon-class');
|
||||
|
||||
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
|
||||
const statusBarItemEl = this.addStatusBarItem();
|
||||
statusBarItemEl.setText('Status Bar Text');
|
||||
|
||||
// This adds a simple command that can be triggered anywhere
|
||||
this.addCommand({
|
||||
id: 'open-sample-modal-simple',
|
||||
name: 'Open sample modal (simple)',
|
||||
callback: () => {
|
||||
new SampleModal(this.app).open();
|
||||
}
|
||||
});
|
||||
// This adds an editor command that can perform some operation on the current editor instance
|
||||
this.addCommand({
|
||||
id: 'sample-editor-command',
|
||||
name: 'Sample editor command',
|
||||
editorCallback: (editor: Editor, view: MarkdownView) => {
|
||||
console.log(editor.getSelection());
|
||||
editor.replaceSelection('Sample Editor Command');
|
||||
}
|
||||
});
|
||||
// This adds a complex command that can check whether the current state of the app allows execution of the command
|
||||
this.addCommand({
|
||||
id: 'open-sample-modal-complex',
|
||||
name: 'Open sample modal (complex)',
|
||||
checkCallback: (checking: boolean) => {
|
||||
// Conditions to check
|
||||
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (markdownView) {
|
||||
// If checking is true, we're simply "checking" if the command can be run.
|
||||
// If checking is false, then we want to actually perform the operation.
|
||||
if (!checking) {
|
||||
new SampleModal(this.app).open();
|
||||
}
|
||||
|
||||
// This command will only show up in Command Palette when the check function returns true
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// This adds a settings tab so the user can configure various aspects of the plugin
|
||||
this.addSettingTab(new SampleSettingTab(this.app, this));
|
||||
|
||||
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
|
||||
// Using this function will automatically remove the event listener when this plugin is disabled.
|
||||
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
|
||||
console.log('click', evt);
|
||||
});
|
||||
|
||||
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
|
||||
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
|
||||
}
|
||||
|
||||
onunload() {
|
||||
|
||||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
|
||||
class SampleModal extends Modal {
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const {contentEl} = this;
|
||||
contentEl.setText('Woah!');
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const {contentEl} = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
||||
class SampleSettingTab extends PluginSettingTab {
|
||||
plugin: MyPlugin;
|
||||
|
||||
constructor(app: App, plugin: MyPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const {containerEl} = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'});
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Setting #1')
|
||||
.setDesc('It\'s a secret')
|
||||
.addText(text => text
|
||||
.setPlaceholder('Enter your secret')
|
||||
.setValue(this.plugin.settings.mySetting)
|
||||
.onChange(async (value) => {
|
||||
console.log('Secret: ' + value);
|
||||
this.plugin.settings.mySetting = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"id": "obsidian-sample-plugin",
|
||||
"name": "Sample Plugin",
|
||||
"version": "1.0.0",
|
||||
"id": "obsidian-pocketbook-cloud-highlight-importer",
|
||||
"name": "Pocketbook Cloud Highlight Importer",
|
||||
"version": "0.1.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.",
|
||||
"author": "Obsidian",
|
||||
"authorUrl": "https://obsidian.md",
|
||||
"fundingUrl": "https://obsidian.md/pricing",
|
||||
"description": "Imports notes and highlights from your Pocketbook Cloud account.",
|
||||
"author": "Lena Brüder",
|
||||
"authorUrl": "https://github.com/lenalebt/obsidian-pocketbook-cloud-highlight-importer",
|
||||
"fundingUrl": "https://github.com/lenalebt/obsidian-pocketbook-cloud-highlight-importer",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
|
|
|
|||
5051
package-lock.json
generated
Normal file
5051
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -6,7 +6,8 @@
|
|||
"scripts": {
|
||||
"dev": "node esbuild.config.mjs",
|
||||
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
|
||||
"version": "node version-bump.mjs && git add manifest.json versions.json"
|
||||
"version": "node version-bump.mjs && git add manifest.json versions.json",
|
||||
"test-install": "npm run build && cp manifest.json build/main.js test-vault/.obsidian/plugins/pocketbook-cloud-highlight-importer/"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
|
@ -17,8 +18,13 @@
|
|||
"@typescript-eslint/parser": "5.29.0",
|
||||
"builtin-modules": "3.3.0",
|
||||
"esbuild": "0.17.3",
|
||||
"npm-check-updates": "^16.6.2",
|
||||
"obsidian": "latest",
|
||||
"prettier": "^2.8.1",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"epub-cfi-resolver": "^1.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
174
src/apiclient.ts
Normal file
174
src/apiclient.ts
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
import { requestUrl } from 'obsidian';
|
||||
import * as internal from 'stream';
|
||||
|
||||
export interface PocketbookCloudBookCover {
|
||||
width: number;
|
||||
height: number;
|
||||
path: string;
|
||||
}
|
||||
export interface PocketbookCloudBookMetadata {
|
||||
title: string;
|
||||
authors: string;
|
||||
year: string;
|
||||
isbn: string;
|
||||
cover: PocketbookCloudBookCover[];
|
||||
}
|
||||
export interface PocketbookCloudBook {
|
||||
id: string;
|
||||
title: string;
|
||||
fast_hash: string;
|
||||
path: string;
|
||||
link: string;
|
||||
metadata: PocketbookCloudBookMetadata;
|
||||
}
|
||||
export interface PocketbookCloudNoteInfo {
|
||||
type: string; //TODO: guess this can be an enum or so?
|
||||
uuid: string; // UUID type? should exist I guess
|
||||
updated: Date; //TODO: should be a date type, what should I use over here?
|
||||
}
|
||||
export interface PocketbookCloudColorType {
|
||||
value: string;
|
||||
updated: Date; // TODO: date
|
||||
}
|
||||
export interface PocketbookCloudNoteType {
|
||||
value: string;
|
||||
updated: Date; // TODO: date
|
||||
}
|
||||
export interface PocketbookCloudNoteContent {
|
||||
text: string;
|
||||
updated: Date; // TODO: date
|
||||
}
|
||||
export interface PocketbookCloudMarkType {
|
||||
anchor: string;
|
||||
created: Date; // TODO: date, but epoch
|
||||
updated: Date; // TODO: date
|
||||
}
|
||||
export interface PocketbookCloudQuotationType {
|
||||
begin: string;
|
||||
end: string;
|
||||
text: string;
|
||||
updated: Date; // TODO: date
|
||||
}
|
||||
export interface PocketbookCloudNote {
|
||||
color: PocketbookCloudColorType;
|
||||
type: PocketbookCloudNoteType;
|
||||
note: PocketbookCloudNoteContent;
|
||||
mark: PocketbookCloudMarkType;
|
||||
quotation: PocketbookCloudQuotationType;
|
||||
uuid: string; //TODO: uuid type?
|
||||
}
|
||||
|
||||
/**
|
||||
* Main things to know about the API:
|
||||
*
|
||||
* - fast_hash is a hash of the book file, it is used to identify the book
|
||||
*/
|
||||
export class PocketbookCloudApiClient {
|
||||
constructor(private login_client: PocketbookCloudLoginClient) {}
|
||||
|
||||
async getBooks(): Promise<PocketbookCloudBook[]> {
|
||||
const access_token = await this.login_client.getAccessToken();
|
||||
const books = await requestUrl({
|
||||
url: 'https://cloud.pocketbook.digital/api/v1.0/books?limit=500', //TODO: pagination!
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${access_token}`,
|
||||
},
|
||||
}).then(response => response.json.items);
|
||||
|
||||
return books;
|
||||
}
|
||||
|
||||
async getHighlightIdsForBook(fast_hash: string): Promise<PocketbookCloudNoteInfo[]> {
|
||||
const access_token = await this.login_client.getAccessToken();
|
||||
const highlights = await requestUrl({
|
||||
url: `https://cloud.pocketbook.digital/api/v1.0/notes?` + new URLSearchParams({ fast_hash }), //TODO: pagination!? unsure, never had that many notes.
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${access_token}`,
|
||||
},
|
||||
}).then(response => response.json);
|
||||
|
||||
return highlights;
|
||||
}
|
||||
|
||||
async getHighlight(uuid: string, fast_hash: string): Promise<PocketbookCloudNote> {
|
||||
const access_token = await this.login_client.getAccessToken();
|
||||
const highlight = await requestUrl({
|
||||
url: `https://cloud.pocketbook.digital/api/v1.0/notes/${uuid}?` + new URLSearchParams({ fast_hash }),
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${access_token}`,
|
||||
},
|
||||
}).then(response => response.json);
|
||||
|
||||
return highlight;
|
||||
}
|
||||
|
||||
//for some reason, the access tokens in the provided link seem to be invalid
|
||||
async getBookCover(book: PocketbookCloudBook): Promise<ArrayBuffer> {
|
||||
const access_token = await this.login_client.getAccessToken();
|
||||
const cover = await requestUrl({
|
||||
url: book.metadata.cover[0].path,
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Bearer ${access_token}`,
|
||||
},
|
||||
}).then(response => response.arrayBuffer);
|
||||
|
||||
return cover;
|
||||
}
|
||||
}
|
||||
|
||||
export class PocketbookCloudLoginClient {
|
||||
// these values are taken from a login session of the official pocketbook cloud app
|
||||
// apparently they're not using PKCE; I take this as public knowledge and simply check it in.
|
||||
private client_id: string = 'qNAx1RDb';
|
||||
private client_secret: string = 'K3YYSjCgDJNoWKdGVOyO1mrROp3MMZqqRNXNXTmh';
|
||||
|
||||
constructor(private username: string, private password: string, private shop_name: string, private access_token: string, private refresh_token: string) {}
|
||||
|
||||
async login() {
|
||||
const shop_id = await fetch(
|
||||
'https://cloud.pocketbook.digital/api/v1.0/auth/login?' +
|
||||
new URLSearchParams({
|
||||
username: this.username,
|
||||
client_id: this.client_id,
|
||||
client_secret: this.client_secret,
|
||||
})
|
||||
)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
return data;
|
||||
})
|
||||
.then(data => data.providers.filter((item: any) => item.name.includes(this.shop_name)))
|
||||
.then(data => data[0].shop_id);
|
||||
|
||||
const login_response = await requestUrl({
|
||||
url: 'https://cloud.pocketbook.digital/api/v1.0/auth/login/knv',
|
||||
method: 'POST',
|
||||
contentType: 'application/x-www-form-urlencoded',
|
||||
body: new URLSearchParams({
|
||||
shop_id,
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
client_id: this.client_id,
|
||||
client_secret: this.client_secret,
|
||||
}).toString(),
|
||||
}).then(response => response.json);
|
||||
|
||||
console.log(login_response);
|
||||
|
||||
this.access_token = login_response.access_token;
|
||||
this.refresh_token = login_response.refresh_token;
|
||||
}
|
||||
|
||||
async getAccessToken(): Promise<string> {
|
||||
// TODO: check if access token is still valid, use refresh token to get a new one, ...
|
||||
if (!this.access_token) {
|
||||
await this.login();
|
||||
}
|
||||
|
||||
return this.access_token;
|
||||
}
|
||||
}
|
||||
124
src/import.ts
Normal file
124
src/import.ts
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
import { App, Notice, TFile } from 'obsidian';
|
||||
import { PocketbookCloudApiClient, PocketbookCloudLoginClient } from './apiclient';
|
||||
import { PocketbookCloudHighlightsImporterPluginSettings } from './settings';
|
||||
|
||||
var CFI = require('epub-cfi-resolver');
|
||||
|
||||
export class PocketbookCloudHighlightsImporter {
|
||||
login_client: PocketbookCloudLoginClient;
|
||||
api_client: PocketbookCloudApiClient;
|
||||
|
||||
constructor(private app: App, private settings: PocketbookCloudHighlightsImporterPluginSettings) {
|
||||
this.login_client = new PocketbookCloudLoginClient(settings.username, settings.password, settings.shop_name, settings.access_token, settings.refresh_token);
|
||||
this.api_client = new PocketbookCloudApiClient(this.login_client);
|
||||
}
|
||||
|
||||
async importHighlights() {
|
||||
new Notice('Importing highlights...');
|
||||
const books = await this.api_client.getBooks();
|
||||
|
||||
for (const book of books) {
|
||||
const highlightIds = await this.api_client.getHighlightIdsForBook(book.fast_hash);
|
||||
|
||||
const highlights = await Promise.all(highlightIds.map(highlightInfo => this.api_client.getHighlight(highlightInfo.uuid, book.fast_hash)));
|
||||
if (highlights.length > 0) {
|
||||
const sanitized_book_title = book.title.replace(/[^a-zA-Z0-9 \-]/g, '');
|
||||
const folder = `${this.settings.import_folder}/${sanitized_book_title}`;
|
||||
this.createFolder(folder);
|
||||
this.createFolder(`${folder}/highlights`);
|
||||
|
||||
const metadata_filename = `${folder}/metadata.md`;
|
||||
|
||||
// does not work for now, see API client comment
|
||||
//const cover_filename = `${folder}/cover.jpg`;
|
||||
//await this.writeFileBinary(cover_filename, await this.api_client.getBookCover(book));
|
||||
|
||||
// write metadata file, which should be used to get all highlights together
|
||||
await this.writeFile(
|
||||
metadata_filename,
|
||||
`---
|
||||
title: ${book.title}
|
||||
authors: ${book.metadata.authors}
|
||||
isbn: ${book.metadata.isbn}
|
||||
year: ${book.metadata.year}
|
||||
id: ${book.id}
|
||||
fast_hash: ${book.fast_hash}
|
||||
type: book
|
||||
plugin: pocketbook-cloud-highlights-importer
|
||||
---
|
||||
|
||||
\`\`\`dataview
|
||||
LIST WITHOUT ID text
|
||||
WHERE book_id=${book.id} AND type = "highlight" and plugin = "pocketbook-cloud-highlights-importer"
|
||||
SORT sort_order
|
||||
\`\`\`
|
||||
`
|
||||
);
|
||||
|
||||
try {
|
||||
// if sorting works, fine. if not, also fine, using date then.
|
||||
highlights.sort((a, b) => CFI.compare(this.cfi(a.quotation.begin), this.cfi(b.quotation.begin)));
|
||||
} catch (e) {
|
||||
highlights.sort((a, b) => +a.quotation.updated - +b.quotation.updated);
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
for (const highlight of highlights) {
|
||||
i++;
|
||||
const file_name = `${folder}/highlights/${highlight.uuid}.md`;
|
||||
const content = `---
|
||||
id: ${highlight.uuid}
|
||||
book_id: ${book.id}
|
||||
book_fast_hash: ${book.fast_hash}
|
||||
color: ${highlight.color?.value ?? 'unknown'}
|
||||
note: ${highlight.note?.text ?? ''}
|
||||
text: ${highlight.quotation?.text ?? ''}
|
||||
pointer:
|
||||
begin: ${highlight.quotation?.begin ?? ''}
|
||||
end: ${highlight.quotation?.end ?? ''}
|
||||
updated: ${highlight.quotation.updated}
|
||||
type: highlight
|
||||
plugin: pocketbook-cloud-highlights-importer
|
||||
sort_order: ${i}
|
||||
---
|
||||
${highlight.quotation?.text ?? ''}
|
||||
|
||||
> [!note]
|
||||
> ${highlight.note?.text ?? ''}
|
||||
|
||||
`;
|
||||
await this.writeFile(file_name, content);
|
||||
}
|
||||
}
|
||||
}
|
||||
new Notice('Import done');
|
||||
}
|
||||
|
||||
private async createFolder(folder: string) {
|
||||
if (!this.app.vault.getAbstractFileByPath(folder)) {
|
||||
await this.app.vault.createFolder(folder);
|
||||
}
|
||||
}
|
||||
|
||||
private async writeFile(file_name: string, content: string) {
|
||||
let file = this.app.vault.getAbstractFileByPath(file_name) as TFile;
|
||||
if (!file) {
|
||||
file = await this.app.vault.create(file_name, '');
|
||||
}
|
||||
|
||||
this.app.vault.modify(file, content);
|
||||
}
|
||||
|
||||
private async writeFileBinary(file_name: string, content: ArrayBuffer) {
|
||||
let file = this.app.vault.getAbstractFileByPath(file_name) as TFile;
|
||||
if (!file) {
|
||||
file = await this.app.vault.createBinary(file_name, content);
|
||||
}
|
||||
|
||||
this.app.vault.modifyBinary(file, content);
|
||||
}
|
||||
|
||||
private cfi(cfi: string) {
|
||||
return new CFI(cfi.substring(cfi.indexOf('epubcfi')));
|
||||
}
|
||||
}
|
||||
37
src/main.ts
Normal file
37
src/main.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
|
||||
import { PocketbookCloudHighlightsImporter } from './import';
|
||||
import { DEFAULT_SETTINGS, PocketbookCloudHighlightsImporterPluginSettings, PocketbookCloudHighlightsImporterSettingTab } from './settings';
|
||||
|
||||
// Remember to rename these classes and interfaces!
|
||||
|
||||
export default class PocketbookCloudHighlightsImporterPlugin extends Plugin {
|
||||
settings: PocketbookCloudHighlightsImporterPluginSettings;
|
||||
importer: PocketbookCloudHighlightsImporter;
|
||||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
this.importer = new PocketbookCloudHighlightsImporter(this.app, this.settings);
|
||||
|
||||
// This adds a simple command that can be triggered anywhere
|
||||
this.addCommand({
|
||||
id: 'pocketbook-cloud-importer-perform-import',
|
||||
name: 'Pocketbook Cloud: Import highlights & notes',
|
||||
callback: () => {
|
||||
this.importer.importHighlights();
|
||||
},
|
||||
});
|
||||
|
||||
// This adds a settings tab so the user can configure various aspects of the plugin
|
||||
this.addSettingTab(new PocketbookCloudHighlightsImporterSettingTab(this.app, this));
|
||||
}
|
||||
|
||||
onunload() {}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
await this.saveData(this.settings);
|
||||
}
|
||||
}
|
||||
115
src/settings.ts
Normal file
115
src/settings.ts
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
import { App, PluginSettingTab, Setting } from 'obsidian';
|
||||
import PocketbookCloudHighlightsImporterPlugin from './main';
|
||||
|
||||
export interface PocketbookCloudHighlightsImporterPluginSettings {
|
||||
username: string;
|
||||
password: string;
|
||||
shop_name: string;
|
||||
access_token: string;
|
||||
refresh_token: string;
|
||||
import_folder: string;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: PocketbookCloudHighlightsImporterPluginSettings = {
|
||||
username: '',
|
||||
password: '',
|
||||
shop_name: '',
|
||||
access_token: '',
|
||||
refresh_token: '',
|
||||
import_folder: '',
|
||||
};
|
||||
|
||||
export class PocketbookCloudHighlightsImporterSettingTab extends PluginSettingTab {
|
||||
plugin: PocketbookCloudHighlightsImporterPlugin;
|
||||
|
||||
constructor(app: App, plugin: PocketbookCloudHighlightsImporterPlugin) {
|
||||
super(app, plugin);
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
|
||||
containerEl.empty();
|
||||
|
||||
containerEl.createEl('h2', { text: 'Settings for Pocketbook Cloud Highlights Importer Plugin' });
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Username')
|
||||
.setDesc('The mail address you log in to the pocketbook cloud with')
|
||||
.addText(text =>
|
||||
text
|
||||
.setPlaceholder('crocodile@example.com')
|
||||
.setValue(this.plugin.settings.username)
|
||||
.onChange(async value => {
|
||||
this.plugin.settings.username = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Password')
|
||||
.setDesc('Your login password')
|
||||
.addText(text =>
|
||||
text
|
||||
.setPlaceholder('Enter your password')
|
||||
.setValue(this.plugin.settings.password)
|
||||
.onChange(async value => {
|
||||
this.plugin.settings.password = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Shop name')
|
||||
.setDesc('The name of the shop you are logging in to. Leave empty if you only have one; you can use a substring of the shop name here.')
|
||||
.addText(text =>
|
||||
text
|
||||
.setPlaceholder('')
|
||||
.setValue(this.plugin.settings.shop_name)
|
||||
.onChange(async value => {
|
||||
this.plugin.settings.shop_name = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Access Token')
|
||||
.setDesc('This should be a hidden setting later on')
|
||||
.addText(text =>
|
||||
text
|
||||
.setPlaceholder('Enter your access token')
|
||||
.setValue(this.plugin.settings.access_token)
|
||||
.onChange(async value => {
|
||||
this.plugin.settings.access_token = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Refresh Token')
|
||||
.setDesc('This should be a hidden setting later on')
|
||||
.addText(text =>
|
||||
text
|
||||
.setPlaceholder('Enter your refresh token')
|
||||
.setValue(this.plugin.settings.refresh_token)
|
||||
.onChange(async value => {
|
||||
this.plugin.settings.refresh_token = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Import Folder')
|
||||
.setDesc('The folder the plugin will write to. Should be empty, do not store other data here.')
|
||||
.addText(text =>
|
||||
text
|
||||
.setPlaceholder('Enter your folder path from vault root')
|
||||
.setValue(this.plugin.settings.import_folder)
|
||||
.onChange(async value => {
|
||||
this.plugin.settings.import_folder = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
1
test-vault/.gitignore
vendored
Normal file
1
test-vault/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
.trash/
|
||||
1
test-vault/.obsidian/app.json
vendored
Normal file
1
test-vault/.obsidian/app.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
3
test-vault/.obsidian/appearance.json
vendored
Normal file
3
test-vault/.obsidian/appearance.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"accentColor": ""
|
||||
}
|
||||
4
test-vault/.obsidian/community-plugins.json
vendored
Normal file
4
test-vault/.obsidian/community-plugins.json
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[
|
||||
"dataview",
|
||||
"obsidian-pocketbook-cloud-highlight-importer"
|
||||
]
|
||||
29
test-vault/.obsidian/core-plugins-migration.json
vendored
Normal file
29
test-vault/.obsidian/core-plugins-migration.json
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"page-preview": true,
|
||||
"daily-notes": true,
|
||||
"templates": true,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"slash-command": false,
|
||||
"editor-status": true,
|
||||
"starred": true,
|
||||
"markdown-importer": false,
|
||||
"zk-prefixer": false,
|
||||
"random-note": false,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
"audio-recorder": false,
|
||||
"workspaces": false,
|
||||
"file-recovery": true,
|
||||
"publish": false,
|
||||
"sync": false
|
||||
}
|
||||
20
test-vault/.obsidian/core-plugins.json
vendored
Normal file
20
test-vault/.obsidian/core-plugins.json
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
[
|
||||
"file-explorer",
|
||||
"global-search",
|
||||
"switcher",
|
||||
"graph",
|
||||
"backlink",
|
||||
"canvas",
|
||||
"outgoing-link",
|
||||
"tag-pane",
|
||||
"page-preview",
|
||||
"daily-notes",
|
||||
"templates",
|
||||
"note-composer",
|
||||
"command-palette",
|
||||
"editor-status",
|
||||
"starred",
|
||||
"outline",
|
||||
"word-count",
|
||||
"file-recovery"
|
||||
]
|
||||
1
test-vault/.obsidian/hotkeys.json
vendored
Normal file
1
test-vault/.obsidian/hotkeys.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
1
test-vault/.obsidian/plugins/.gitignore
vendored
Normal file
1
test-vault/.obsidian/plugins/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
data.json
|
||||
10
test-vault/.obsidian/plugins/dataview/manifest.json
vendored
Normal file
10
test-vault/.obsidian/plugins/dataview/manifest.json
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"id": "dataview",
|
||||
"name": "Dataview",
|
||||
"version": "0.5.55",
|
||||
"minAppVersion": "0.13.11",
|
||||
"description": "Complex data views for the data-obsessed.",
|
||||
"author": "Michael Brenan <blacksmithgu@gmail.com>",
|
||||
"authorUrl": "https://github.com/blacksmithgu",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
146
test-vault/.obsidian/plugins/dataview/styles.css
vendored
Normal file
146
test-vault/.obsidian/plugins/dataview/styles.css
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/** Live Preview padding fixes, specifically for DataviewJS custom HTML elements. */
|
||||
.is-live-preview .block-language-dataviewjs > p, .is-live-preview .block-language-dataviewjs > span {
|
||||
line-height: 1.0;
|
||||
}
|
||||
|
||||
.block-language-dataview {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/*****************/
|
||||
/** Table Views **/
|
||||
/*****************/
|
||||
|
||||
/* List View Default Styling; rendered internally as a table. */
|
||||
.table-view-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-view-table > thead > tr, .table-view-table > tbody > tr {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.table-view-table > tbody > tr:hover {
|
||||
background-color: var(--text-selection) !important;
|
||||
}
|
||||
|
||||
.table-view-table > thead > tr > th {
|
||||
font-weight: 700;
|
||||
font-size: larger;
|
||||
border-top: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-bottom: solid;
|
||||
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.table-view-table > tbody > tr > td {
|
||||
text-align: left;
|
||||
border: none;
|
||||
font-weight: 400;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.table-view-table ul, .table-view-table ol {
|
||||
margin-block-start: 0.2em !important;
|
||||
margin-block-end: 0.2em !important;
|
||||
}
|
||||
|
||||
/** Rendered value styling for any view. */
|
||||
.dataview-result-list-root-ul {
|
||||
padding: 0em !important;
|
||||
margin: 0em !important;
|
||||
}
|
||||
|
||||
.dataview-result-list-ul {
|
||||
margin-block-start: 0.2em !important;
|
||||
margin-block-end: 0.2em !important;
|
||||
}
|
||||
|
||||
/** Generic grouping styling. */
|
||||
.dataview.result-group {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
/*******************/
|
||||
/** Inline Fields **/
|
||||
/*******************/
|
||||
|
||||
.dataview.inline-field-key {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
font-family: var(--font-monospace);
|
||||
background-color: var(--background-primary-alt);
|
||||
color: var(--text-nav-selected);
|
||||
}
|
||||
|
||||
.dataview.inline-field-value {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
font-family: var(--font-monospace);
|
||||
background-color: var(--background-secondary-alt);
|
||||
color: var(--text-nav-selected);
|
||||
}
|
||||
|
||||
.dataview.inline-field-standalone-value {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
font-family: var(--font-monospace);
|
||||
background-color: var(--background-secondary-alt);
|
||||
color: var(--text-nav-selected);
|
||||
}
|
||||
|
||||
/***************/
|
||||
/** Task View **/
|
||||
/***************/
|
||||
|
||||
.dataview.task-list-item, .dataview.task-list-basic-item {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
.dataview.task-list-item:hover, .dataview.task-list-basic-item:hover {
|
||||
background-color: var(--text-selection);
|
||||
box-shadow: -40px 0 0 var(--text-selection);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*****************/
|
||||
/** Error Views **/
|
||||
/*****************/
|
||||
|
||||
div.dataview-error-box {
|
||||
width: 100%;
|
||||
min-height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 4px dashed var(--background-secondary);
|
||||
}
|
||||
|
||||
.dataview-error-message {
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/*************************/
|
||||
/** Additional Metadata **/
|
||||
/*************************/
|
||||
|
||||
.dataview.small-text {
|
||||
font-size: smaller;
|
||||
color: var(--text-muted);
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.dataview.small-text::before {
|
||||
content: "(";
|
||||
}
|
||||
|
||||
.dataview.small-text::after {
|
||||
content: ")";
|
||||
}
|
||||
11
test-vault/.obsidian/plugins/pocketbook-cloud-highlight-importer/manifest.json
vendored
Normal file
11
test-vault/.obsidian/plugins/pocketbook-cloud-highlight-importer/manifest.json
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"id": "obsidian-pocketbook-cloud-highlight-importer",
|
||||
"name": "Pocketbook Cloud Highlight Importer",
|
||||
"version": "0.1.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Imports notes and highlights from your Pocketbook Cloud account.",
|
||||
"author": "Lena Brüder",
|
||||
"authorUrl": "https://github.com/lenalebt/obsidian-pocketbook-cloud-highlight-importer",
|
||||
"fundingUrl": "https://github.com/lenalebt/obsidian-pocketbook-cloud-highlight-importer",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
8
test-vault/.obsidian/plugins/pocketbook-cloud-highlight-importer/styles.css
vendored
Normal file
8
test-vault/.obsidian/plugins/pocketbook-cloud-highlight-importer/styles.css
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
|
||||
This CSS file will be included with your plugin, and
|
||||
available in the app when your plugin is enabled.
|
||||
|
||||
If your plugin does not need CSS, delete this file.
|
||||
|
||||
*/
|
||||
154
test-vault/.obsidian/workspace.json
vendored
Normal file
154
test-vault/.obsidian/workspace.json
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
{
|
||||
"main": {
|
||||
"id": "67d4a1f8b6367b5d",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "b9af23949ad9bf6b",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "5a8f7af4213cc25f",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "empty",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
},
|
||||
"left": {
|
||||
"id": "a1f02312e0c8fdf4",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "5924363dc8cba6c7",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "d252e907b82daee6",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "file-explorer",
|
||||
"state": {
|
||||
"sortOrder": "alphabetical"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "158ff13be7a2a388",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "e7bbed21ab80b1e9",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "starred",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300
|
||||
},
|
||||
"right": {
|
||||
"id": "da8cc437f215281d",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "7a41090a183073e2",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "2ffee0c3dfcff08d",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
"showSearch": false,
|
||||
"searchQuery": "",
|
||||
"backlinkCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "8a5521676553242f",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "252abadc7e76d9d0",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "tag",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"useHierarchy": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "54acd6b9aff56f08",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300,
|
||||
"collapsed": true
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"switcher:Open quick switcher": false,
|
||||
"graph:Open graph view": false,
|
||||
"canvas:Create new canvas": false,
|
||||
"daily-notes:Open today's daily note": false,
|
||||
"templates:Insert template": false,
|
||||
"command-palette:Open command palette": false
|
||||
}
|
||||
},
|
||||
"active": "d252e907b82daee6",
|
||||
"lastOpenFiles": [
|
||||
"Pocketbook Cloud Highlights API.md",
|
||||
"Highlights/Reinventing Organizations A Guide to Creating Organizations Inspired by the Next Stage of Human Consciousness/metadata.md",
|
||||
"Highlights/No Rules Rules/metadata.md",
|
||||
"Highlights/Good StrategyBad Strategy/metadata.md",
|
||||
"Highlights/Getting Things Done The Art of Stress-free Productivity/metadata.md",
|
||||
"Highlights/Escaping the Build Trap/metadata.md",
|
||||
"Highlights/Befriend Your Brain/metadata.md",
|
||||
"Highlights/Escaping the Build Trap/highlights/6C527597-BECA-5EE3-9F72-ACFBBBCA7FE2.md",
|
||||
"Highlights/Befriend Your Brain/highlights/FA4CBDE2-C8C7-5700-B4BA-4BBB1E7557D2.md",
|
||||
"Highlights/Befriend Your Brain/highlights/C7A84480-2246-5AF0-B6AA-2218FD312B38.md"
|
||||
]
|
||||
}
|
||||
1
test-vault/Highlights/.gitignore
vendored
Normal file
1
test-vault/Highlights/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.md
|
||||
314
test-vault/Pocketbook Cloud Highlights API.md
Normal file
314
test-vault/Pocketbook Cloud Highlights API.md
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
# Authorization
|
||||
Seems to be documented actually
|
||||
|
||||
# Notes / Highlights
|
||||
## Creation
|
||||
in yellow, frontpage
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/notes?fast_hash=a322134ccec26d4339d095e490bfb96b' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json;charset=utf-8' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Origin: https://cloud.pocketbook.digital' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache' --data-raw '{"type":{"value":"highlight","updated":"2023-01-26T19:33:38+03:00"},"color":{"value":"yellow","updated":"2023-01-26T19:33:38+03:00"},"mark":{"anchor":"#epubcfi(/6/4[from_conflict_to_community-epub]!/4[from_conflict_to_community-epub]/2[u7qZ8BeZlVOdKM8KRrUbszG]/18/2/1:2)","created":1674761618,"updated":"2023-01-26T19:33:38+03:00"},"quotation":{"begin":"#epubcfi(/6/4[from_conflict_to_community-epub]!/4[from_conflict_to_community-epub]/2[u7qZ8BeZlVOdKM8KRrUbszG]/18/2/1:2)","end":"#epubcfi(/6/4[from_conflict_to_community-epub]!/4[from_conflict_to_community-epub]/2[u7qZ8BeZlVOdKM8KRrUbszG]/18/2/1:22)","text":"is is Microcosm #607","updated":"2023-01-26T19:33:38+03:00"}}'
|
||||
```
|
||||
### Response
|
||||
`{"uuid":"45cdb958-6059-48df-a76f-376046d03fe5","updated":"2023-01-26T19:33:38Z"}`
|
||||
## Reading
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/notes/45cdb958-6059-48df-a76f-376046d03fe5?fast_hash=a322134ccec26d4339d095e490bfb96b' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache'
|
||||
```
|
||||
### Response
|
||||
`{"type":{"value":"highlight","updated":"2023-01-26T19:33:38Z"},"color":{"value":"yellow","updated":"2023-01-26T19:33:38Z"},"mark":{"anchor":"#epubcfi(/6/4[from_conflict_to_community-epub]!/4[from_conflict_to_community-epub]/2[u7qZ8BeZlVOdKM8KRrUbszG]/18/2/1:2)","created":1674761618,"updated":"2023-01-26T19:33:38Z"},"quotation":{"begin":"#epubcfi(/6/4[from_conflict_to_community-epub]!/4[from_conflict_to_community-epub]/2[u7qZ8BeZlVOdKM8KRrUbszG]/18/2/1:2)","end":"#epubcfi(/6/4[from_conflict_to_community-epub]!/4[from_conflict_to_community-epub]/2[u7qZ8BeZlVOdKM8KRrUbszG]/18/2/1:22)","text":"is is Microcosm #607","updated":"2023-01-26T19:33:38Z"},"uuid":"45cdb958-6059-48df-a76f-376046d03fe5"}`
|
||||
|
||||
## Creation
|
||||
In cyan, page 2
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/notes?fast_hash=a322134ccec26d4339d095e490bfb96b' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json;charset=utf-8' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Origin: https://cloud.pocketbook.digital' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache' --data-raw '{"type":{"value":"highlight","updated":"2023-01-26T19:38:33+03:00"},"color":{"value":"cian","updated":"2023-01-26T19:38:33+03:00"},"mark":{"anchor":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/4/2/1:0)","created":1674761914,"updated":"2023-01-26T19:38:33+03:00"},"quotation":{"begin":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/4/2/1:0)","end":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/4/2/1:20)","text":"Microcosm Publishing","updated":"2023-01-26T19:38:33+03:00"}}'
|
||||
```
|
||||
### Response
|
||||
`{"uuid":"4213c9e0-2f8d-46e0-8ae0-e2fdb45cd590","updated":"2023-01-26T19:38:33Z"}`
|
||||
## Reading
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/notes/4213c9e0-2f8d-46e0-8ae0-e2fdb45cd590?fast_hash=a322134ccec26d4339d095e490bfb96b' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache'
|
||||
```
|
||||
### Response
|
||||
`{"type":{"value":"highlight","updated":"2023-01-26T19:38:33Z"},"color":{"value":"cian","updated":"2023-01-26T19:38:33Z"},"mark":{"anchor":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/4/2/1:0)","created":1674761914,"updated":"2023-01-26T19:38:33Z"},"quotation":{"begin":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/4/2/1:0)","end":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/4/2/1:20)","text":"Microcosm Publishing","updated":"2023-01-26T19:38:33Z"},"uuid":"4213c9e0-2f8d-46e0-8ae0-e2fdb45cd590"}`
|
||||
|
||||
## Writing a note
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/notes?fast_hash=a322134ccec26d4339d095e490bfb96b' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json;charset=utf-8' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Origin: https://cloud.pocketbook.digital' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache' --data-raw '{"color":{"value":"red","updated":"2023-01-26T20:26:03+03:00"},"type":{"value":"note","updated":"2023-01-26T20:26:03+03:00"},"note":{"text":"This is a test note!","updated":"2023-01-26T20:26:03+03:00"},"mark":{"anchor":"#epubcfi(/6/8[id2]!/4[from_conflict_to_community-epub]/4[uBkoRfi6keB2AXobgOHPmA4]/30/2/1:11)","created":1674764764,"updated":"2023-01-26T20:26:03+03:00"},"quotation":{"begin":"#epubcfi(/6/8[id2]!/4[from_conflict_to_community-epub]/4[uBkoRfi6keB2AXobgOHPmA4]/30/2/1:11)","end":"#epubcfi(/6/8[id2]!/4[from_conflict_to_community-epub]/4[uBkoRfi6keB2AXobgOHPmA4]/30/2/1:56)","text":"ven: Cognitive Practices: Mental Models and P","updated":"2023-01-26T20:26:03+03:00"}}'
|
||||
```
|
||||
### Response
|
||||
`{"uuid":"218a7270-d3da-4eeb-ab10-019a80f46f8f","updated":"2023-01-26T20:26:03Z"}`
|
||||
|
||||
### Reading a note
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/notes/218a7270-d3da-4eeb-ab10-019a80f46f8f?fast_hash=a322134ccec26d4339d095e490bfb96b' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache'
|
||||
```
|
||||
|
||||
### Response
|
||||
```json
|
||||
{
|
||||
"color": {
|
||||
"value": "red",
|
||||
"updated": "2023-01-26T20:26:03Z"
|
||||
},
|
||||
"type": {
|
||||
"value": "note",
|
||||
"updated": "2023-01-26T20:26:03Z"
|
||||
},
|
||||
"note": {
|
||||
"text": "This is a test note!",
|
||||
"updated": "2023-01-26T20:26:03Z"
|
||||
},
|
||||
"mark": {
|
||||
"anchor": "#epubcfi(/6/8[id2]!/4[from_conflict_to_community-epub]/4[uBkoRfi6keB2AXobgOHPmA4]/30/2/1:11)",
|
||||
"created": 1674764764,
|
||||
"updated": "2023-01-26T20:26:03Z"
|
||||
},
|
||||
"quotation": {
|
||||
"begin": "#epubcfi(/6/8[id2]!/4[from_conflict_to_community-epub]/4[uBkoRfi6keB2AXobgOHPmA4]/30/2/1:11)",
|
||||
"end": "#epubcfi(/6/8[id2]!/4[from_conflict_to_community-epub]/4[uBkoRfi6keB2AXobgOHPmA4]/30/2/1:56)",
|
||||
"text": "ven: Cognitive Practices: Mental Models and P",
|
||||
"updated": "2023-01-26T20:26:03Z"
|
||||
},
|
||||
"uuid": "218a7270-d3da-4eeb-ab10-019a80f46f8f"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Listing notes
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/notes?fast_hash=a322134ccec26d4339d095e490bfb96b' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/
|
||||
109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Authorization: Bearer dTo3MzhjYTpmYTM1MDUxMGMyN2JmNjUyYTk1Zjg4YjQ4OGVmODkwZD
|
||||
A2OA' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: emp
|
||||
ty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache'
|
||||
```
|
||||
### Response
|
||||
`[{"uuid":"4213c9e0-2f8d-46e0-8ae0-e2fdb45cd590","type":"highlight","updated":"2023-01-26T19:38:33Z"},{"uuid":"45cdb958-6059-48df-a76f-376046d03fe5","type":"highlight","updated":"2023-01-26T19:33:38Z"}]`
|
||||
|
||||
### Another example
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/notes?fast_hash=bcff1fd58edebfa374ff191a1e3749bf' -H 'Authorization: Bearer REDACTED'
|
||||
```
|
||||
^^works as well
|
||||
|
||||
# Reading position
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/books/read-position?fast_hash=a322134ccec26d4339d095e490bfb96b' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json;charset=utf-8' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Origin: https://cloud.pocketbook.digital' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache' --data-raw '{"offs":1,"pointer_pb":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)","pointer":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)"}'
|
||||
```
|
||||
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/books/read-position?fast_hash=a322134ccec26d4339d095e490bfb96b' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json;charset=utf-8' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Origin: https://cloud.pocketbook.digital' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache' --data-raw '{"offs":1,"pointer_pb":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)","pointer":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)"}'
|
||||
```
|
||||
|
||||
# Setup or so
|
||||
## Fileops
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/fileops/info/?fast_hash=a322134ccec26d4339d095e490bfb96b&include_metadata=true' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/reader_new/?a322134ccec26d4339d095e490bfb96b' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache'
|
||||
```
|
||||
### Response
|
||||
`{"id":47564749,"path":"/fromconflicttocommunity.epub","name":"fromconflicttocommunity.epub","is_folder":false,"mtime":"2023-01-26T19:46:41Z","has_file":true,"download_link":"https://cloud.pocketbook.digital/api/v1.0/files/fromconflicttocommunity.epub?fast_hash=a322134ccec26d4339d095e490bfb96b&access_token=REDACTED","resource_id":null,"created_at":"2022-12-18T11:26:46Z","mime_type":"application/epub+zip","bytes":1235531,"md5_hash":"07YuAiHdaMe6GYLMHmgAoQ==","fast_hash":"a322134ccec26d4339d095e490bfb96b","client_mtime":"2022-12-18T11:26:46Z","favorite":false,"metadata":{"title":"From Conflict to Community","authors":"Gwendolyn Olton","genres":null,"publisher":"Microcosm Publishing LLC","year":"2022","lang":"en-US","isbn":null,"series":null,"series_ord":null,"annotation":null,"book_id":["calibre:201","uuid:0b4dc1c5-f2b2-4f17-8bc1-71bfb210b755","urn:uuid:29d919dd-24f5-4384-be78-b447c9dc299b"],"cover":[{"path":"https://cloud.pocketbook.digital/api/v1.0/fileops/cover/fromconflicttocommunity.epub.cover_s.jpg?fast_hash=a322134ccec26d4339d095e490bfb96b&access_token=REDACTED","width":250,"height":400},{"path":"https://cloud.pocketbook.digital/api/v1.0/fileops/cover/fromconflicttocommunity.epub.cover_b.jpg?fast_hash=a322134ccec26d4339d095e490bfb96b&access_token=REDACTED","width":500,"height":800}]},"position":{"pointer":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)","pointer_pb":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)","percent":1,"updated":"2023-01-26T19:46:25Z"}}`
|
||||
|
||||
## User-Info
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/user' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/browser/en/user/' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache'
|
||||
```
|
||||
### Response
|
||||
`{"login":"lena@lena-brueder.de","vendor_login":"lena@lena-brueder.de","email":"lena@lena-brueder.de","first_name":"Lena","last_name":"Brüder","full_name":"Lena Brüder","register_date":"2019-08-05T17:02:02Z","role":"USER","recommendations":[],"user_id":473290,"provider_id":4,"provider_name":"KNV","partner_shop_id":61717,"partner_shop_name":"Janssen Bücher","language":null,"files_size_limit":5368709120,"files_size":427228490,"books_size":427228490,"audio_books_size":0,"internal_account":{"vendor_id":"pocketbook","email":"user473290.knv@pbsync.com","password":"VTrLXnGU0EXliknKXhJPpw==","adobe_uuid":"00478684-b7a3-11e9-bfca-8ecd8f5cd1a8","serial":"53B7-CB81-3CC2-8B77","is_main":true},"shop":{"id":"61717","name":"Janssen Bücher","icon":"http://www.janssen-buecher.de/wcsstore/24682/Attachment/Images/Logo/ShopLogo.png","home_url":"https://v91-prod.zeitfracht.digital/61717","reset_password_url":null,"login_url":null,"isbn_search_url":"http://janssen-buecher.buchkatalog.de/isbnScanner/61717/10002/-3/"}}`
|
||||
|
||||
## Last-Opened
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/books/last-opened' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/browser/en/user/' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache'
|
||||
```
|
||||
### Response
|
||||
`{"id":"47564749","path":"/fromconflicttocommunity.epub","title":"From Conflict to Community","mime_type":"application/epub+zip","created_at":"2022-12-18T11:26:46Z","purchased":false,"resource_id":null,"bytes":1235531,"client_mtime":"2022-12-18T11:26:46Z","collections":null,"fast_hash":"a322134ccec26d4339d095e490bfb96b","favorite":false,"read_status":"reading","link":"https://cloud.pocketbook.digital/api/v1.0/files/fromconflicttocommunity.epub?fast_hash=a322134ccec26d4339d095e490bfb96b&access_token=REDACTED","hasLinks":true,"format":"epub","md5_hash":"07YuAiHdaMe6GYLMHmgAoQ==","mtime":"2023-01-26T19:46:41Z","name":"fromconflicttocommunity.epub","read_percent":1,"percent":"1","isDrm":false,"isLcp":false,"isAudioBook":false,"metadata":{"title":"From Conflict to Community","track_number":null,"authors":"Gwendolyn Olton","cover":[{"width":250,"height":400,"path":"https://cloud.pocketbook.digital/api/v1.0/fileops/cover/fromconflicttocommunity.epub.cover_s.jpg?fast_hash=a322134ccec26d4339d095e490bfb96b&access_token=REDACTED"},{"width":500,"height":800,"path":"https://cloud.pocketbook.digital/api/v1.0/fileops/cover/fromconflicttocommunity.epub.cover_b.jpg?fast_hash=a322134ccec26d4339d095e490bfb96b&access_token=REDACTED"}],"genres":null,"lang":"en-US","publisher":"Microcosm Publishing LLC","size":null,"duration":null,"updated":"2022-12-18T11:26:49Z","year":2022,"isbn":null,"series":null,"annotation":null,"book_id":["calibre:201","uuid:0b4dc1c5-f2b2-4f17-8bc1-71bfb210b755","urn:uuid:29d919dd-24f5-4384-be78-b447c9dc299b"],"fixed_layout":false,"series_ord":null},"position":{"pointer":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)","pointer_pb":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)","percent":1,"page":null,"pages_total":0,"updated":"2023-01-26T19:46:25Z","offs":0},"read_position":{"pointer":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)","pointer_pb":"#epubcfi(/6/6[id1]!/4[from_conflict_to_community-epub]/4[uccXMJsePnAE7GhjoFg8Z89]/2/2/1:0)","percent":1,"page":null,"pages_total":0,"updated":"2023-01-26T19:46:25Z","offs":0},"action":"create","action_date":"2022-12-18T11:26:46Z"}`
|
||||
|
||||
## Books Listing
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/books?limit=64' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Authorization: Bearer REDACTED' -H 'X-WEB-CLIENT: 1674760246501' -H 'Cache-Control: no-cache' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/browser/en/user/' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache'
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 64,
|
||||
"items": [
|
||||
{
|
||||
"id": "2753187",
|
||||
"path": "/google-sre.epub",
|
||||
"title": "Site Reliability Engineering: How Google Runs Production Systems",
|
||||
"mime_type": "application/epub+zip",
|
||||
"created_at": "2019-08-27T19:54:02Z",
|
||||
"purchased": false,
|
||||
"resource_id": null,
|
||||
"bytes": 4920608,
|
||||
"client_mtime": "2019-08-27T19:54:02Z",
|
||||
"collections": "Software",
|
||||
"fast_hash": "be63d4d4163a4994b4a1a1fa96508e4f",
|
||||
"favorite": false,
|
||||
"read_status": "reading",
|
||||
"link": "https://cloud.pocketbook.digital/api/v1.0/files/google-sre.epub?fast_hash=be63d4d4163a4994b4a1a1fa96508e4f&access_token=REDACTED",
|
||||
"hasLinks": true,
|
||||
"format": "epub",
|
||||
"md5_hash": null,
|
||||
"mtime": "2022-06-16T15:50:10Z",
|
||||
"name": "google-sre.epub",
|
||||
"read_percent": 31,
|
||||
"percent": "31",
|
||||
"isDrm": false,
|
||||
"isLcp": false,
|
||||
"isAudioBook": false,
|
||||
"metadata": {
|
||||
"title": "Site Reliability Engineering: How Google Runs Production Systems",
|
||||
"track_number": null,
|
||||
"authors": "Betsy Beyer",
|
||||
"cover": [
|
||||
{
|
||||
"width": 300,
|
||||
"height": 400,
|
||||
"path": "https://cloud.pocketbook.digital/api/v1.0/fileops/cover/google-sre.epub.cover_s.jpg?fast_hash=be63d4d4163a4994b4a1a1fa96508e4f&access_token=REDACTED"
|
||||
},
|
||||
{
|
||||
"width": 600,
|
||||
"height": 800,
|
||||
"path": "https://cloud.pocketbook.digital/api/v1.0/fileops/cover/google-sre.epub.cover_b.jpg?fast_hash=be63d4d4163a4994b4a1a1fa96508e4f&access_token=REDACTED"
|
||||
}
|
||||
],
|
||||
"genres": null,
|
||||
"lang": "en-US",
|
||||
"publisher": null,
|
||||
"size": null,
|
||||
"duration": null,
|
||||
"updated": "2022-06-16T15:50:10Z",
|
||||
"year": 2016,
|
||||
"isbn": null,
|
||||
"series": null,
|
||||
"annotation": null,
|
||||
"book_id": null,
|
||||
"fixed_layout": false,
|
||||
"series_ord": null
|
||||
},
|
||||
"position": {
|
||||
"pointer": "#epubcfi(/6/40!/4/2[chapter-12---effective-troubleshooting]/18[in-practice-8ksluy]/10[examine-MQsnhjuB]/6/3:47)",
|
||||
"pointer_pb": "pbr:/external##epubcfi(/6/40!/4/2[chapter-12---effective-troubleshooting]/18[in-practice-8ksluy]/10[examine-MQsnhjuB]/6/3:47)",
|
||||
"percent": 31,
|
||||
"page": "162",
|
||||
"pages_total": 0,
|
||||
"updated": "2022-06-16T15:50:10Z",
|
||||
"offs": 0
|
||||
},
|
||||
"read_position": {
|
||||
"pointer": "#epubcfi(/6/40!/4/2[chapter-12---effective-troubleshooting]/18[in-practice-8ksluy]/10[examine-MQsnhjuB]/6/3:47)",
|
||||
"pointer_pb": "pbr:/external##epubcfi(/6/40!/4/2[chapter-12---effective-troubleshooting]/18[in-practice-8ksluy]/10[examine-MQsnhjuB]/6/3:47)",
|
||||
"percent": 31,
|
||||
"page": "162",
|
||||
"pages_total": 0,
|
||||
"updated": "2022-06-16T15:50:10Z",
|
||||
"offs": 0
|
||||
},
|
||||
"action": "create",
|
||||
"action_date": "2019-08-27T19:54:02Z"
|
||||
},
|
||||
{
|
||||
"id": "2753229",
|
||||
"path": "/google-the-site-reliability-workbook.epub",
|
||||
"title": "The Site Reliability Workbook: Practical Ways to Implement SRE",
|
||||
"mime_type": "application/epub+zip",
|
||||
"created_at": "2019-08-27T19:58:27Z",
|
||||
"purchased": false,
|
||||
"resource_id": null,
|
||||
"bytes": 8258348,
|
||||
"client_mtime": "2019-08-27T19:58:27Z",
|
||||
"collections": "Software",
|
||||
"fast_hash": "57d07344d92fde6a1c57556e57f3d9f2",
|
||||
"favorite": false,
|
||||
"read_status": "reading",
|
||||
"link": "https://cloud.pocketbook.digital/api/v1.0/files/google-the-site-reliability-workbook%5B1%5D.epub?fast_hash=57d07344d92fde6a1c57556e57f3d9f2&access_token=REDACTED",
|
||||
"hasLinks": true,
|
||||
"format": "epub",
|
||||
"md5_hash": null,
|
||||
"mtime": "2022-06-16T15:50:10Z",
|
||||
"name": "google-the-site-reliability-workbook[1].epub",
|
||||
"read_percent": 4,
|
||||
"percent": "4",
|
||||
"isDrm": false,
|
||||
"isLcp": false,
|
||||
"isAudioBook": false,
|
||||
"metadata": {
|
||||
"title": "The Site Reliability Workbook: Practical Ways to Implement SRE",
|
||||
"track_number": null,
|
||||
"authors": "Betsy Beyer",
|
||||
"cover": [
|
||||
{
|
||||
"width": 300,
|
||||
"height": 400,
|
||||
"path": "https://cloud.pocketbook.digital/api/v1.0/fileops/cover/google-the-site-reliability-workbook%5B1%5D.epub.cover_s.jpg?fast_hash=57d07344d92fde6a1c57556e57f3d9f2&access_token=REDACTED"
|
||||
},
|
||||
{
|
||||
"width": 600,
|
||||
"height": 800,
|
||||
"path": "https://cloud.pocketbook.digital/api/v1.0/fileops/cover/google-the-site-reliability-workbook%5B1%5D.epub.cover_b.jpg?fast_hash=57d07344d92fde6a1c57556e57f3d9f2&access_token=REDACTED"
|
||||
}
|
||||
],
|
||||
"genres": null,
|
||||
"lang": "en-US",
|
||||
"publisher": null,
|
||||
"size": null,
|
||||
"duration": null,
|
||||
"updated": "2022-06-16T15:50:10Z",
|
||||
"year": 2018,
|
||||
"isbn": null,
|
||||
"series": null,
|
||||
"annotation": null,
|
||||
"book_id": null,
|
||||
"fixed_layout": false,
|
||||
"series_ord": null
|
||||
},
|
||||
"position": {
|
||||
"pointer": "#epubcfi(/6/14!/4/2/2/1)",
|
||||
"pointer_pb": "pbr:/external##epubcfi(/6/14!/4/2/2/1)",
|
||||
"percent": 4,
|
||||
"page": "21",
|
||||
"pages_total": 0,
|
||||
"updated": "2022-06-16T15:50:10Z",
|
||||
"offs": 0
|
||||
},
|
||||
"read_position": {
|
||||
"pointer": "#epubcfi(/6/14!/4/2/2/1)",
|
||||
"pointer_pb": "pbr:/external##epubcfi(/6/14!/4/2/2/1)",
|
||||
"percent": 4,
|
||||
"page": "21",
|
||||
"pages_total": 0,
|
||||
"updated": "2022-06-16T15:50:10Z",
|
||||
"offs": 0
|
||||
},
|
||||
"action": "create",
|
||||
"action_date": "2019-08-27T19:58:27Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# Login
|
||||
## first
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/auth/login?username=lena@lena-brueder.de&client_id=qNAx1RDb&client_secret=K3YYSjCgDJNoWKdGVOyO1mrROp3MMZqqRNXNXTmh&language=en' -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/browser/en' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache'
|
||||
```
|
||||
### Response
|
||||
`{"providers":[{"alias":"knv","name":"LITFASS Bücher & Medien","shop_id":"57209","icon":"http://www.litfass-buecher.de/wcsstore/27964/Attachment/Images/Logo/logo3.png","icon_eink":"http://www.litfass-buecher.de/wcsstore/27964/Attachment/Images/Logo/logo3.png","logged_by":"password"},{"alias":"knv","name":"Janssen Bücher","shop_id":"61717","icon":"http://www.janssen-buecher.de/wcsstore/24682/Attachment/Images/Logo/ShopLogo.png","icon_eink":"http://www.janssen-buecher.de/wcsstore/24682/Attachment/Images/Logo/ShopLogo.png","logged_by":"password"}]}`
|
||||
|
||||
## second
|
||||
```
|
||||
curl 'https://cloud.pocketbook.digital/api/v1.0/auth/login/knv' -X POST -H 'User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0' -H 'Accept: application/json, text/plain, */*' -H 'Accept-Language: en-US,en;q=0.5' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/x-www-form-urlencoded' -H 'Origin: https://cloud.pocketbook.digital' -H 'DNT: 1' -H 'Connection: keep-alive' -H 'Referer: https://cloud.pocketbook.digital/browser/en' -H 'Sec-Fetch-Dest: empty' -H 'Sec-Fetch-Mode: cors' -H 'Sec-Fetch-Site: same-origin' -H 'Pragma: no-cache' -H 'Cache-Control: no-cache' --data-raw 'shop_id=61717&username=lena%40lena-brueder.de&client_id=qNAx1RDb&client_secret=K3YYSjCgDJNoWKdGVOyO1mrROp3MMZqqRNXNXTmh&grant_type=password&password=66C7ati8b9EK8UcV&language=en'
|
||||
```
|
||||
### Response
|
||||
`{"access_token":"dTo3MzhjYToyMjU1ZmJhNjk1MjNjNWRkYmM2ODQ4YTJhZDFiOGZjZGFjYw","token_type":"Bearer","expires_in":7200,"refresh_token":"dTo3MzhjYTo5NWFhMjQ4NWMwMTY3ZDEwOTk4NzE4N2ZhM2VhMTUxM2RmZA"}`
|
||||
|
||||
# Learnings
|
||||
- The identifiers seem to be EPUB CFI, which is "EPUB canonical fragment identifier", which is used to identify something within an EPUB file. I guess I need the EPUB file to interpret this to which page something is.
|
||||
- I get the text from the notes API, which makes it really easier to get the data exported, no need for the EPUB to do that, at least.
|
||||
- The notes have a UUID, so I can list stuff via that UUID
|
||||
- There is a `fast_hash`, which seems to be used to identify the context we're talking about. That `fast_hash` can be queried from the books listing endpoint.
|
||||
- Login is documented over here: https://cloud.pocketbook.digital/browser/en/for-developers/how-to-connect-store-to-pb-cloud
|
||||
|
||||
# Next steps
|
||||
|
||||
- https://phibr0.medium.com/how-to-create-your-own-obsidian-plugin-53f2d5d44046
|
||||
- Code in /home/lena/projects/open_source/obsidian/obsidian-pocketbook-cloud-highlight-importer/
|
||||
- manifest is changed
|
||||
|
|
@ -5,20 +5,13 @@
|
|||
"inlineSources": true,
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
"allowJs": true,
|
||||
"allowJs": false,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES5",
|
||||
"ES6",
|
||||
"ES7"
|
||||
]
|
||||
"lib": ["DOM", "ES5", "ES6", "ES7"]
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
]
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue