mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-18 13:31:17 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6288716966 | ||
|
|
47d2cf9733 | ||
|
|
ae6a9ecee4 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.19.8",
|
"version": "0.19.10",
|
||||||
"minAppVersion": "0.9.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.",
|
"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",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.8",
|
"version": "0.19.10",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.8",
|
"version": "0.19.10",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.19.8",
|
"version": "0.19.10",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"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",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import { runWithLock } from "./lib/src/lock";
|
|||||||
import { LiveSyncCommands } from "./LiveSyncCommands";
|
import { LiveSyncCommands } from "./LiveSyncCommands";
|
||||||
import { stripAllPrefixes } from "./lib/src/path";
|
import { stripAllPrefixes } from "./lib/src/path";
|
||||||
import { PeriodicProcessor, askYesNo, disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "./utils";
|
import { PeriodicProcessor, askYesNo, disposeMemoObject, memoIfNotExist, memoObject, retrieveMemoObject, scheduleTask } from "./utils";
|
||||||
import { Semaphore } from "./lib/src/semaphore";
|
|
||||||
import { PluginDialogModal } from "./dialogs";
|
import { PluginDialogModal } from "./dialogs";
|
||||||
import { JsonResolveModal } from "./JsonResolveModal";
|
import { JsonResolveModal } from "./JsonResolveModal";
|
||||||
|
|
||||||
@@ -180,77 +179,70 @@ export class ConfigSync extends LiveSyncCommands {
|
|||||||
pluginList.set(this.pluginList)
|
pluginList.set(this.pluginList)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
scheduleTask("update-plugin-list-task", 200, async () => {
|
||||||
await runWithLock("update-plugin-list", false, async () => {
|
await runWithLock("update-plugin-list", false, async () => {
|
||||||
// if (updatedDocumentPath != "") pluginList.update(e => e.filter(ee => ee.documentPath != updatedDocumentPath));
|
const entries = [] as PluginDataExDisplay[]
|
||||||
// const work: Record<string, Record<string, Record<string, Record<string, PluginDataEntryEx>>>> = {};
|
const plugins = this.localDatabase.findEntries(ICXHeader + "", `${ICXHeader}\u{10ffff}`, { include_docs: true });
|
||||||
const entries = [] as PluginDataExDisplay[]
|
const para = Parallels();
|
||||||
const plugins = this.localDatabase.findEntries(ICXHeader + "", `${ICXHeader}\u{10ffff}`, { include_docs: true });
|
let count = 0;
|
||||||
const semaphore = Semaphore(4);
|
pluginIsEnumerating.set(true);
|
||||||
const para = Parallels();
|
let processed = false;
|
||||||
let count = 0;
|
try {
|
||||||
pluginIsEnumerating.set(true);
|
for await (const plugin of plugins) {
|
||||||
let processed = false;
|
const path = plugin.path || this.getPath(plugin);
|
||||||
try {
|
if (updatedDocumentPath && updatedDocumentPath != path) {
|
||||||
for await (const plugin of plugins) {
|
continue;
|
||||||
const path = plugin.path || this.getPath(plugin);
|
|
||||||
if (updatedDocumentPath && updatedDocumentPath != path) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
processed = true;
|
|
||||||
const oldEntry = (this.pluginList.find(e => e.documentPath == path));
|
|
||||||
if (oldEntry && oldEntry.mtime == plugin.mtime) continue;
|
|
||||||
await para.wait(5);
|
|
||||||
para.add((async (v) => {
|
|
||||||
|
|
||||||
const release = await semaphore.acquire(1);
|
|
||||||
try {
|
|
||||||
count++;
|
|
||||||
if (count % 10 == 0) Logger(`Enumerating files... ${count}`, logLevel, "get-plugins");
|
|
||||||
|
|
||||||
Logger(`plugin-${path}`, LOG_LEVEL.VERBOSE);
|
|
||||||
const wx = await this.localDatabase.getDBEntry(path, null, false, false);
|
|
||||||
if (wx) {
|
|
||||||
const data = deserialize(getDocData(wx.data), {}) as PluginDataEx;
|
|
||||||
const xFiles = [] as PluginDataExFile[];
|
|
||||||
for (const file of data.files) {
|
|
||||||
const work = { ...file };
|
|
||||||
const tempStr = getDocData(work.data);
|
|
||||||
work.data = [await hashString(tempStr)];
|
|
||||||
xFiles.push(work);
|
|
||||||
}
|
|
||||||
entries.push({
|
|
||||||
...data,
|
|
||||||
documentPath: this.getPath(wx),
|
|
||||||
files: xFiles
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
//TODO
|
|
||||||
Logger(`Something happened at enumerating customization :${v.path}`, LOG_LEVEL.NOTICE);
|
|
||||||
console.warn(ex);
|
|
||||||
} finally {
|
|
||||||
release();
|
|
||||||
}
|
}
|
||||||
|
processed = true;
|
||||||
|
const oldEntry = (this.pluginList.find(e => e.documentPath == path));
|
||||||
|
if (oldEntry && oldEntry.mtime == plugin.mtime) continue;
|
||||||
|
await para.wait(15);
|
||||||
|
para.add((async (v) => {
|
||||||
|
try {
|
||||||
|
count++;
|
||||||
|
if (count % 10 == 0) Logger(`Enumerating files... ${count}`, logLevel, "get-plugins");
|
||||||
|
Logger(`plugin-${path}`, LOG_LEVEL.VERBOSE);
|
||||||
|
const wx = await this.localDatabase.getDBEntry(path, null, false, false);
|
||||||
|
if (wx) {
|
||||||
|
const data = deserialize(getDocData(wx.data), {}) as PluginDataEx;
|
||||||
|
const xFiles = [] as PluginDataExFile[];
|
||||||
|
for (const file of data.files) {
|
||||||
|
const work = { ...file };
|
||||||
|
const tempStr = getDocData(work.data);
|
||||||
|
work.data = [await hashString(tempStr)];
|
||||||
|
xFiles.push(work);
|
||||||
|
}
|
||||||
|
entries.push({
|
||||||
|
...data,
|
||||||
|
documentPath: this.getPath(wx),
|
||||||
|
files: xFiles
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
//TODO
|
||||||
|
Logger(`Something happened at enumerating customization :${v.path}`, LOG_LEVEL.NOTICE);
|
||||||
|
console.warn(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)(plugin));
|
||||||
}
|
}
|
||||||
)(plugin));
|
await para.all();
|
||||||
|
let newList = [...this.pluginList];
|
||||||
|
for (const item of entries) {
|
||||||
|
newList = newList.filter(x => x.documentPath != item.documentPath);
|
||||||
|
newList.push(item)
|
||||||
|
}
|
||||||
|
if (updatedDocumentPath != "" && !processed) newList = newList.filter(e => e.documentPath != updatedDocumentPath);
|
||||||
|
|
||||||
|
this.pluginList = newList;
|
||||||
|
pluginList.set(newList);
|
||||||
|
|
||||||
|
|
||||||
|
Logger(`All files enumerated`, logLevel, "get-plugins");
|
||||||
|
} finally {
|
||||||
|
pluginIsEnumerating.set(false);
|
||||||
}
|
}
|
||||||
await para.all();
|
});
|
||||||
let newList = [...this.pluginList];
|
|
||||||
for (const item of entries) {
|
|
||||||
newList = newList.filter(x => x.documentPath != item.documentPath);
|
|
||||||
newList.push(item)
|
|
||||||
}
|
|
||||||
if (updatedDocumentPath != "" && !processed) newList = newList.filter(e => e.documentPath != updatedDocumentPath);
|
|
||||||
|
|
||||||
this.pluginList = newList;
|
|
||||||
pluginList.set(newList);
|
|
||||||
|
|
||||||
|
|
||||||
Logger(`All files enumerated`, logLevel, "get-plugins");
|
|
||||||
} finally {
|
|
||||||
pluginIsEnumerating.set(false);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// return entries;
|
// return entries;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { App, PluginSettingTab, Setting, sanitizeHTMLToDom, TextAreaComponent, MarkdownRenderer, stringifyYaml } from "./deps";
|
import { App, PluginSettingTab, Setting, sanitizeHTMLToDom, TextAreaComponent, MarkdownRenderer, stringifyYaml } from "./deps";
|
||||||
import { DEFAULT_SETTINGS, LOG_LEVEL, type ObsidianLiveSyncSettings, type ConfigPassphraseStore, type RemoteDBSettings } from "./lib/src/types";
|
import { DEFAULT_SETTINGS, LOG_LEVEL, type ObsidianLiveSyncSettings, type ConfigPassphraseStore, type RemoteDBSettings, type FilePathWithPrefix, type DocumentID } from "./lib/src/types";
|
||||||
import { delay } from "./lib/src/utils";
|
import { delay } from "./lib/src/utils";
|
||||||
import { Semaphore } from "./lib/src/semaphore";
|
import { Semaphore } from "./lib/src/semaphore";
|
||||||
import { versionNumberString2Number } from "./lib/src/strbin";
|
import { versionNumberString2Number } from "./lib/src/strbin";
|
||||||
@@ -7,7 +7,8 @@ import { Logger } from "./lib/src/logger";
|
|||||||
import { checkSyncInfo, isCloudantURI } from "./lib/src/utils_couchdb.js";
|
import { checkSyncInfo, isCloudantURI } from "./lib/src/utils_couchdb.js";
|
||||||
import { testCrypt } from "./lib/src/e2ee_v2";
|
import { testCrypt } from "./lib/src/e2ee_v2";
|
||||||
import ObsidianLiveSyncPlugin from "./main";
|
import ObsidianLiveSyncPlugin from "./main";
|
||||||
import { balanceChunks, localDatabaseCleanUp, performRebuildDB, remoteDatabaseCleanup, requestToCouchDB } from "./utils";
|
import { balanceChunks, isChunk, localDatabaseCleanUp, performRebuildDB, remoteDatabaseCleanup, requestToCouchDB } from "./utils";
|
||||||
|
import { stripAllPrefixes } from "./lib/src/path";
|
||||||
|
|
||||||
|
|
||||||
export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||||
@@ -111,7 +112,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MarkdownRenderer.renderMarkdown(updateInformation, informationDivEl, "/", null);
|
MarkdownRenderer.renderMarkdown(updateInformation, informationDivEl, "/", this.plugin);
|
||||||
|
|
||||||
|
|
||||||
addScreenElement("100", containerInformationEl);
|
addScreenElement("100", containerInformationEl);
|
||||||
@@ -1536,7 +1537,73 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
Logger("done", LOG_LEVEL.NOTICE, "verify");
|
Logger("done", LOG_LEVEL.NOTICE, "verify");
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
new Setting(containerHatchEl)
|
||||||
|
.setName("Check and convert non-path-obfuscated files")
|
||||||
|
.setDesc("")
|
||||||
|
.addButton((button) =>
|
||||||
|
button
|
||||||
|
.setButtonText("Perform")
|
||||||
|
.setDisabled(false)
|
||||||
|
.setWarning()
|
||||||
|
.onClick(async () => {
|
||||||
|
for await (const docName of this.plugin.localDatabase.findAllDocNames()) {
|
||||||
|
if (!docName.startsWith("f:")) {
|
||||||
|
const idEncoded = await this.plugin.path2id(docName as FilePathWithPrefix);
|
||||||
|
const doc = await this.plugin.localDatabase.getRaw(docName as DocumentID);
|
||||||
|
if (!doc) continue;
|
||||||
|
if (doc.type != "newnote" && doc.type != "plain") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (doc?.deleted ?? false) continue;
|
||||||
|
const newDoc = { ...doc };
|
||||||
|
//Prepare converted data
|
||||||
|
newDoc._id = idEncoded;
|
||||||
|
newDoc.path = docName as FilePathWithPrefix;
|
||||||
|
delete newDoc._rev;
|
||||||
|
try {
|
||||||
|
const obfuscatedDoc = await this.plugin.localDatabase.getRaw(idEncoded, { revs_info: true });
|
||||||
|
// Unfortunately we have to delete one of them.
|
||||||
|
// Just now, save it as a conflicted document.
|
||||||
|
obfuscatedDoc._revs_info?.shift(); // Drop latest revision.
|
||||||
|
const previousRev = obfuscatedDoc._revs_info?.shift(); // Use second revision.
|
||||||
|
if (previousRev) {
|
||||||
|
newDoc._rev = previousRev.rev;
|
||||||
|
} else {
|
||||||
|
//If there are no revisions, set the possibly unique one
|
||||||
|
newDoc._rev = "1-" + (`00000000000000000000000000000000${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}${~~(Math.random() * 1e9)}`.slice(-32));
|
||||||
|
}
|
||||||
|
const ret = await this.plugin.localDatabase.putRaw(newDoc, { force: true });
|
||||||
|
if (ret.ok) {
|
||||||
|
Logger(`${docName} has been converted as conflicted document`, LOG_LEVEL.NOTICE);
|
||||||
|
doc._deleted = true;
|
||||||
|
if ((await this.plugin.localDatabase.putRaw(doc)).ok) {
|
||||||
|
Logger(`Old ${docName} has been deleted`, LOG_LEVEL.NOTICE);
|
||||||
|
}
|
||||||
|
await this.plugin.showIfConflicted(docName as FilePathWithPrefix);
|
||||||
|
} else {
|
||||||
|
Logger(`Converting ${docName} Failed!`, LOG_LEVEL.NOTICE);
|
||||||
|
Logger(ret, LOG_LEVEL.VERBOSE);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex?.status == 404) {
|
||||||
|
// We can perform this safely
|
||||||
|
if ((await this.plugin.localDatabase.putRaw(newDoc)).ok) {
|
||||||
|
Logger(`${docName} has been converted`, LOG_LEVEL.NOTICE);
|
||||||
|
doc._deleted = true;
|
||||||
|
if ((await this.plugin.localDatabase.putRaw(doc)).ok) {
|
||||||
|
Logger(`Old ${docName} has been deleted`, LOG_LEVEL.NOTICE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger(`Something went wrong on converting ${docName}`, LOG_LEVEL.NOTICE);
|
||||||
|
Logger(ex, LOG_LEVEL.VERBOSE);
|
||||||
|
// Something wrong.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger(`Converting finished`, LOG_LEVEL.NOTICE);
|
||||||
|
}));
|
||||||
new Setting(containerHatchEl)
|
new Setting(containerHatchEl)
|
||||||
.setName("Suspend file watching")
|
.setName("Suspend file watching")
|
||||||
.setDesc("Stop watching for file change.")
|
.setDesc("Stop watching for file change.")
|
||||||
|
|||||||
@@ -70,5 +70,13 @@ I hope you will give it a try.
|
|||||||
- Log dialogue is now shown as one of tabs.
|
- Log dialogue is now shown as one of tabs.
|
||||||
- Fixed:
|
- Fixed:
|
||||||
- Some minor issues has been fixed.
|
- Some minor issues has been fixed.
|
||||||
|
- 0.19.9
|
||||||
|
- New feature (For fixing a problem):
|
||||||
|
- We can fix the database obfuscated and plain paths that have been mixed up.
|
||||||
|
- Improvements
|
||||||
|
- Customisation Sync performance has been improved.
|
||||||
|
- 0.19.10
|
||||||
|
- Fixed
|
||||||
|
- Fixed the issue about fixing the database.
|
||||||
|
|
||||||
... To continue on to `updates_old.md`.
|
... To continue on to `updates_old.md`.
|
||||||
|
|||||||
Reference in New Issue
Block a user