Improved:

Folder deletion and renaming are now tracked.
Database update fixed up. But be a little heavier.
Touched up the readme.
This commit is contained in:
vorotamoroz
2021-11-09 15:05:12 +09:00
parent 53b4d4cd20
commit 04e3004aca
5 changed files with 95 additions and 38 deletions

View File

@@ -1,4 +1,5 @@
# Self-hosted LiveSync
**Renamed from: obsidian-livesync**
This is the obsidian plugin that enables livesync between multi-devices with self-hosted database.
@@ -11,7 +12,7 @@ Community implementation, not compatible with official "Sync".
**It's getting almost stable now, But Please make sure to back your vault up!**
Limitations: Folder deletion handling is not completed.
Limitations: ~~Folder deletion handling is not completed.~~ **It would work now.**
## This plugin enables..
@@ -77,7 +78,6 @@ Note: The figure is drawn as single-directional, between two devices. But everyt
![dedupe](images/2.png)
## Cloudant Setup
### Creating an Instance
@@ -105,7 +105,7 @@ Select Multitenant(it's the default) and the region as you like.
![step 8](instruction_images/cloudant_8.png)
7. In resource details, there's information to connect from self-hosted-livesync.
Copy the "External Endpoint(preferred)" address. <sup>(\*1)</sup>
Copy the "External Endpoint(preferred)" address. <sup>(\*1)</sup>. We use this address later, with the database name.
![step 9](instruction_images/cloudant_9.png)
### CouchDB setup
@@ -120,11 +120,13 @@ Select Multitenant(it's the default) and the region as you like.
_NOTE: of course We want to set "app://obsidian.md" but it's not acceptable on Cloudant._
![step 2](instruction_images/couchdb_2.png)
1. And open the "Databases" tab and hit the "Create Database" button.
1. And open the "Databases" tab and hit the "Create Database" button.
Enter the name as you like <sup>(\*2)</sup> and Hit the "Create" button below.
![step 3](instruction_images/couchdb_3.png)
1. If the database was shown with joyful messages, then you can close this browser tab now.
1. If the database was shown with joyful messages, setup is almost done.
And, once you have confirmed that you can create a database, usullay there is no need to open this screen.
You can create a database from Self-hosted LiveSync.
![step 4](instruction_images/couchdb_4.png)
### Credentials Setup
@@ -135,7 +137,7 @@ Select Multitenant(it's the default) and the region as you like.
1. The dialog to create a credential will be shown.
type any name or leave it default, hit the "Add" button.
![step 2](instruction_images/credentials_2.png)
_NOTE: This "name" is not related to your username that uses in self-hosted-livesync._
_NOTE: This "name" is not related to your username that uses in Self-hosted LiveSync._
1. Back to "Service credentials", the new credential should be created.
open details.
@@ -145,16 +147,16 @@ Select Multitenant(it's the default) and the region as you like.
follow the figure, it's
"apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5"<sup>(\*3)</sup> and "c2c11651d75497fa3d3c486e4c8bdf27"<sup>(\*4)</sup>
### self-hosted-livesync setting
### Self-hosted LiveSync setting
![xx](instruction_images/obsidian_sync_1.png)
example values.
| Items | Value | example |
| ------------------- | ----------- | --------------------------------------------------------------------------- |
| CouchDB Remote URI: | (\*1)/(\*2) | https://xxxxxxxxxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud/sync-test |
| CouchDB Username | (\*3) | apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5 |
| CouchDB Password | (\*4) | c2c11651d75497fa3d3c486e4c8bdf27 |
| Items | Value | example |
| ------------------- | -------------------------------- | --------------------------------------------------------------------------- |
| CouchDB Remote URI: | (\*1)/(\*2) or any favorite name | https://xxxxxxxxxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud/sync-test |
| CouchDB Username | (\*3) | apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5 |
| CouchDB Password | (\*4) | c2c11651d75497fa3d3c486e4c8bdf27 |
# License

99
main.ts
View File

@@ -1,4 +1,4 @@
import { App, debounce, Modal, Notice, Plugin, PluginSettingTab, Setting, TFile, addIcon, TFolder, normalizePath } from "obsidian";
import { App, debounce, Modal, Notice, Plugin, PluginSettingTab, Setting, TFile, addIcon, TFolder, normalizePath, TAbstractFile } from "obsidian";
import { PouchDB } from "./pouchdb-browser-webpack/dist/pouchdb-browser";
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
import xxhash from "xxhash-wasm";
@@ -663,7 +663,7 @@ class LocalPouchDB {
console.log("!" + v.id);
} else {
if (!v.id.startsWith("h:")) {
console.log("?" + v.id);
// console.log("?" + v.id);
}
}
}
@@ -1285,9 +1285,14 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
if (delay < 200) delay = 200;
if (delay > 5000) delay = 5000;
this.watchVaultChange = debounce(this.watchVaultChange.bind(this), delay, false);
this.watchVaultDelete = debounce(this.watchVaultDelete.bind(this), delay, false);
this.watchVaultRename = debounce(this.watchVaultRename.bind(this), delay, false);
// this.watchVaultDelete = debounce(this.watchVaultDelete.bind(this), delay, false);
// this.watchVaultRename = debounce(this.watchVaultRename.bind(this), delay, false);
// this.watchVaultChange = this.watchVaultChange.bind(this);
this.watchVaultDelete = this.watchVaultDelete.bind(this);
this.watchVaultRename = this.watchVaultRename.bind(this);
this.watchWorkspaceOpen = debounce(this.watchWorkspaceOpen.bind(this), delay, false);
this.watchWindowVisiblity = debounce(this.watchWindowVisiblity.bind(this), delay, false);
this.registerWatchEvents();
this.parseReplicationResult = this.parseReplicationResult.bind(this);
@@ -1409,16 +1414,19 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
}
watchWindowVisiblity() {
this.watchWindowVisiblityAsync();
}
async watchWindowVisiblityAsync() {
if (this.settings.suspendFileWatching) return;
let isHidden = document.hidden;
if (isHidden) {
this.localDatabase.closeReplication();
} else {
if (this.settings.liveSync) {
this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult);
await this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult);
}
if (this.settings.syncOnStart) {
this.localDatabase.openReplication(this.settings, false, false, this.parseReplicationResult);
await this.localDatabase.openReplication(this.settings, false, false, this.parseReplicationResult);
}
}
this.gcHook();
@@ -1426,35 +1434,82 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
watchWorkspaceOpen(file: TFile) {
if (this.settings.suspendFileWatching) return;
this.watchWorkspaceOpenAsync(file);
}
async watchWorkspaceOpenAsync(file: TFile) {
if (file == null) return;
this.localDatabase.disposeHashCache();
this.showIfConflicted(file);
await this.showIfConflicted(file);
this.gcHook();
}
watchVaultChange(file: TFile, ...args: any[]) {
if (this.settings.suspendFileWatching) return;
this.updateIntoDB(file);
this.watchVaultChangeAsync(file, ...args);
}
batchFileChange: string[] = [];
async watchVaultChangeAsync(file: TFile, ...args: any[]) {
await this.updateIntoDB(file);
this.gcHook();
}
watchVaultDelete(file: TFile & TFolder) {
watchVaultDelete(file: TFile | TFolder) {
if (this.settings.suspendFileWatching) return;
if (file.children) {
//folder
this.deleteFolderOnDB(file);
// this.app.vault.delete(file);
} else {
this.deleteFromDB(file);
this.watchVaultDeleteAsync(file);
}
async watchVaultDeleteAsync(file: TFile | TFolder) {
if (file instanceof TFile) {
await this.deleteFromDB(file);
} else if (file instanceof TFolder) {
await this.deleteFolderOnDB(file);
}
this.gcHook();
}
watchVaultRename(file: TFile & TFolder, oldFile: any) {
if (this.settings.suspendFileWatching) return;
if (file.children) {
// this.renameFolder(file,oldFile);
Logger(`folder name changed:(this operation is not supported) ${file.path}`, LOG_LEVEL.NOTICE);
GetAllFilesRecursively(file: TAbstractFile): TFile[] {
if (file instanceof TFile) {
return [file];
} else if (file instanceof TFolder) {
let result: TFile[] = [];
for (var v of file.children) {
result.push(...this.GetAllFilesRecursively(v));
}
return result;
} else {
this.updateIntoDB(file);
this.deleteFromDBbyPath(oldFile);
Logger(`Filetype error:${file.path}`, LOG_LEVEL.NOTICE);
throw new Error(`Filetype error:${file.path}`);
}
}
watchVaultRename(file: TFile | TFolder, oldFile: any) {
if (this.settings.suspendFileWatching) return;
this.watchVaultRenameAsync(file, oldFile);
}
getFilePath(file: TAbstractFile): string {
if (file instanceof TFolder) {
if (file.isRoot()) return "";
return this.getFilePath(file.parent) + "/" + file.name;
}
if (file instanceof TFile) {
return this.getFilePath(file.parent) + "/" + file.name;
}
}
async watchVaultRenameAsync(file: TFile | TFolder, oldFile: any) {
Logger(`${oldFile} renamed to ${file.path}`, LOG_LEVEL.VERBOSE);
if (file instanceof TFolder) {
const newFiles = this.GetAllFilesRecursively(file);
// for guard edge cases. this won't happen and each file's event will be raise.
for (const i of newFiles) {
let newFilePath = normalizePath(this.getFilePath(i));
let newFile = this.app.vault.getAbstractFileByPath(newFilePath);
if (newFile instanceof TFile) {
Logger(`save ${newFile.path} into db`);
await this.updateIntoDB(newFile);
}
}
Logger(`delete below ${oldFile} from db`);
await this.deleteFromDBbyPath(oldFile);
} else if (file instanceof TFile) {
Logger(`file save ${file.path} into db`);
await this.updateIntoDB(file);
Logger(`deleted ${oldFile} into db`);
await this.deleteFromDBbyPath(oldFile);
}
this.gcHook();
}

View File

@@ -1,7 +1,7 @@
{
"id": "obsidian-livesync",
"name": "Self-hosted LiveSync",
"version": "0.1.11",
"version": "0.1.12",
"minAppVersion": "0.9.12",
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
"author": "vorotamoroz",

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "obsidian-livesync",
"version": "0.1.11",
"version": "0.1.12",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "obsidian-livesync",
"version": "0.1.11",
"version": "0.1.12",
"license": "MIT",
"dependencies": {
"diff-match-patch": "^1.0.5",

View File

@@ -1,6 +1,6 @@
{
"name": "obsidian-livesync",
"version": "0.1.11",
"version": "0.1.12",
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
"main": "main.js",
"scripts": {