mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-03-15 14:28:47 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
395b7fbc42 | ||
|
|
3773e57429 | ||
|
|
4835fce62a | ||
|
|
ff814be4a0 | ||
|
|
b271b63efa | ||
|
|
23419e476a |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "obsidian-livesync",
|
||||
"name": "Self-hosted LiveSync",
|
||||
"version": "0.19.20",
|
||||
"version": "0.19.23",
|
||||
"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
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.19.20",
|
||||
"version": "0.19.23",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.19.20",
|
||||
"version": "0.19.23",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"diff-match-patch": "^1.0.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsidian-livesync",
|
||||
"version": "0.19.20",
|
||||
"version": "0.19.23",
|
||||
"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",
|
||||
"type": "module",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { App, PluginSettingTab, Setting, sanitizeHTMLToDom, TextAreaComponent, MarkdownRenderer, stringifyYaml } from "./deps";
|
||||
import { DEFAULT_SETTINGS, type ObsidianLiveSyncSettings, type ConfigPassphraseStore, type RemoteDBSettings, type FilePathWithPrefix, type HashAlgorithm, type DocumentID, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "./lib/src/types";
|
||||
import { DEFAULT_SETTINGS, type ObsidianLiveSyncSettings, type ConfigPassphraseStore, type RemoteDBSettings, type FilePathWithPrefix, type HashAlgorithm, type DocumentID, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, LOG_LEVEL_INFO } from "./lib/src/types";
|
||||
import { delay } from "./lib/src/utils";
|
||||
import { Semaphore } from "./lib/src/semaphore";
|
||||
import { versionNumberString2Number } from "./lib/src/strbin";
|
||||
@@ -303,46 +303,44 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
const checkConfig = async () => {
|
||||
Logger(`Checking database configuration`, LOG_LEVEL_INFO);
|
||||
|
||||
const emptyDiv = createDiv();
|
||||
emptyDiv.innerHTML = "<span></span>";
|
||||
checkResultDiv.replaceChildren(...[emptyDiv]);
|
||||
const addResult = (msg: string, classes?: string[]) => {
|
||||
const tmpDiv = createDiv();
|
||||
tmpDiv.addClass("ob-btn-config-fix");
|
||||
if (classes) {
|
||||
tmpDiv.addClasses(classes);
|
||||
}
|
||||
tmpDiv.innerHTML = `${msg}`;
|
||||
checkResultDiv.appendChild(tmpDiv);
|
||||
};
|
||||
try {
|
||||
|
||||
if (isCloudantURI(this.plugin.settings.couchDB_URI)) {
|
||||
Logger("This feature cannot be used with IBM Cloudant.", LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
}
|
||||
|
||||
const r = await requestToCouchDB(this.plugin.settings.couchDB_URI, this.plugin.settings.couchDB_USER, this.plugin.settings.couchDB_PASSWORD, window.origin);
|
||||
|
||||
Logger(JSON.stringify(r.json, null, 2));
|
||||
|
||||
const responseConfig = r.json;
|
||||
|
||||
const emptyDiv = createDiv();
|
||||
emptyDiv.innerHTML = "<span></span>";
|
||||
checkResultDiv.replaceChildren(...[emptyDiv]);
|
||||
const addResult = (msg: string, classes?: string[]) => {
|
||||
const tmpDiv = createDiv();
|
||||
tmpDiv.addClass("ob-btn-config-fix");
|
||||
if (classes) {
|
||||
tmpDiv.addClasses(classes);
|
||||
}
|
||||
tmpDiv.innerHTML = `${msg}`;
|
||||
checkResultDiv.appendChild(tmpDiv);
|
||||
};
|
||||
const addConfigFixButton = (title: string, key: string, value: string) => {
|
||||
const tmpDiv = createDiv();
|
||||
tmpDiv.addClass("ob-btn-config-fix");
|
||||
tmpDiv.innerHTML = `<label>${title}</label><button>Fix</button>`;
|
||||
const x = checkResultDiv.appendChild(tmpDiv);
|
||||
x.querySelector("button").addEventListener("click", async () => {
|
||||
console.dir({ key, value });
|
||||
Logger(`CouchDB Configuration: ${title} -> Set ${key} to ${value}`)
|
||||
const res = await requestToCouchDB(this.plugin.settings.couchDB_URI, this.plugin.settings.couchDB_USER, this.plugin.settings.couchDB_PASSWORD, undefined, key, value);
|
||||
console.dir(res);
|
||||
if (res.status == 200) {
|
||||
Logger(`${title} successfully updated`, LOG_LEVEL_NOTICE);
|
||||
Logger(`CouchDB Configuration: ${title} successfully updated`, LOG_LEVEL_NOTICE);
|
||||
checkResultDiv.removeChild(x);
|
||||
checkConfig();
|
||||
} else {
|
||||
Logger(`${title} failed`, LOG_LEVEL_NOTICE);
|
||||
Logger(res.text);
|
||||
Logger(`CouchDB Configuration: ${title} failed`, LOG_LEVEL_NOTICE);
|
||||
Logger(res.text, LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -352,7 +350,6 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
["ob-btn-config-info"]
|
||||
);
|
||||
|
||||
addResult("Your configuration is dumped to Log", ["ob-btn-config-info"]);
|
||||
addResult("--Config check--", ["ob-btn-config-head"]);
|
||||
|
||||
// Admin check
|
||||
@@ -453,9 +450,16 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
}
|
||||
addResult("--Done--", ["ob-btn-config-head"]);
|
||||
addResult("If you have some trouble with Connection-check even though all Config-check has been passed, Please check your reverse proxy's configuration.", ["ob-btn-config-info"]);
|
||||
Logger(`Checking configuration done`, LOG_LEVEL_INFO);
|
||||
} catch (ex) {
|
||||
Logger(`Checking configuration failed`, LOG_LEVEL_NOTICE);
|
||||
Logger(ex);
|
||||
if (ex?.status == 401) {
|
||||
addResult(`❗ Access forbidden.`);
|
||||
addResult(`We could not continue the test.`);
|
||||
Logger(`Checking configuration done`, LOG_LEVEL_INFO);
|
||||
} else {
|
||||
Logger(`Checking configuration failed`, LOG_LEVEL_NOTICE);
|
||||
Logger(ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
await checkConfig();
|
||||
@@ -1183,26 +1187,27 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
});
|
||||
}
|
||||
|
||||
new Setting(containerSyncSettingEl)
|
||||
.setName("Scan for hidden files before replication")
|
||||
.setDesc("This configuration will be ignored if monitoring changes is enabled.")
|
||||
.setClass("wizardHidden")
|
||||
.addToggle((toggle) =>
|
||||
toggle.setValue(this.plugin.settings.syncInternalFilesBeforeReplication).onChange(async (value) => {
|
||||
this.plugin.settings.syncInternalFilesBeforeReplication = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
if (!this.plugin.settings.watchInternalFileChanges) {
|
||||
new Setting(containerSyncSettingEl)
|
||||
.setName("Scan for hidden files before replication")
|
||||
.setClass("wizardHidden")
|
||||
.addToggle((toggle) =>
|
||||
toggle.setValue(this.plugin.settings.syncInternalFilesBeforeReplication).onChange(async (value) => {
|
||||
this.plugin.settings.syncInternalFilesBeforeReplication = value;
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
new Setting(containerSyncSettingEl)
|
||||
.setName("Scan hidden files periodically")
|
||||
.setDesc("Seconds, 0 to disable. This configuration will be ignored if monitoring changes is enabled.")
|
||||
.setDesc("Seconds, 0 to disable")
|
||||
.setClass("wizardHidden")
|
||||
.addText((text) => {
|
||||
text.setPlaceholder("")
|
||||
.setValue(this.plugin.settings.syncInternalFilesInterval + "")
|
||||
.onChange(async (value) => {
|
||||
let v = Number(value);
|
||||
if (isNaN(v) || v < 10) {
|
||||
if (v !== 0 && (isNaN(v) || v < 10)) {
|
||||
v = 10;
|
||||
}
|
||||
this.plugin.settings.syncInternalFilesInterval = v;
|
||||
@@ -1508,10 +1513,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
pluginConfig.passphrase = REDACTED;
|
||||
pluginConfig.encryptedPassphrase = REDACTED;
|
||||
pluginConfig.encryptedCouchDBConnection = REDACTED;
|
||||
pluginConfig.pluginSyncExtendedSetting = {};
|
||||
|
||||
const msgConfig = `----remote config----
|
||||
${stringifyYaml(responseConfig)}
|
||||
---- Plug-in config ---
|
||||
version:${manifestVersion}
|
||||
${stringifyYaml(pluginConfig)}`;
|
||||
console.log(msgConfig);
|
||||
await navigator.clipboard.writeText(msgConfig);
|
||||
@@ -1859,16 +1866,18 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
})
|
||||
);
|
||||
|
||||
new Setting(containerPluginSettings)
|
||||
.setName("Scan customization periodically")
|
||||
.setDesc("Scan customization every 1 minute. This configuration will be ignored if monitoring changes of hidden files has been enabled.")
|
||||
.addToggle((toggle) =>
|
||||
toggle.setValue(this.plugin.settings.autoSweepPluginsPeriodic).onChange(async (value) => {
|
||||
this.plugin.settings.autoSweepPluginsPeriodic = value;
|
||||
updateDisabledOfDeviceAndVaultName();
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
if (!this.plugin.settings.watchInternalFileChanges) {
|
||||
new Setting(containerPluginSettings)
|
||||
.setName("Scan customization periodically")
|
||||
.setDesc("Scan customization every 1 minute.")
|
||||
.addToggle((toggle) =>
|
||||
toggle.setValue(this.plugin.settings.autoSweepPluginsPeriodic).onChange(async (value) => {
|
||||
this.plugin.settings.autoSweepPluginsPeriodic = value;
|
||||
updateDisabledOfDeviceAndVaultName();
|
||||
await this.plugin.saveSettings();
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
new Setting(containerPluginSettings)
|
||||
.setName("Notify customized")
|
||||
|
||||
35
src/main.ts
35
src/main.ts
@@ -64,7 +64,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
replicator!: LiveSyncDBReplicator;
|
||||
|
||||
statusBar?: HTMLElement;
|
||||
suspended: boolean = true;
|
||||
suspended: boolean = false;
|
||||
deviceAndVaultName: string = "";
|
||||
isMobile = false;
|
||||
isReady = false;
|
||||
@@ -459,8 +459,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
return false;
|
||||
}
|
||||
}
|
||||
await this.realizeSettingSyncMode();
|
||||
this.registerWatchEvents();
|
||||
await this.realizeSettingSyncMode();
|
||||
this.swapSaveCommand();
|
||||
if (this.settings.syncOnStart) {
|
||||
this.replicator.openReplication(this.settings, false, false);
|
||||
@@ -576,7 +576,13 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
this.addRibbonIcon("view-log", "Show log", () => {
|
||||
this.showView(VIEW_TYPE_LOG);
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "view-log",
|
||||
name: "Show log",
|
||||
callback: () => {
|
||||
this.showView(VIEW_TYPE_LOG);
|
||||
}
|
||||
});
|
||||
this.addSettingTab(new ObsidianLiveSyncSettingTab(this.app, this));
|
||||
this.app.workspace.onLayoutReady(this.onLayoutReady.bind(this));
|
||||
|
||||
@@ -1415,10 +1421,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
const now = new Date().getTime();
|
||||
if (queue.missingChildren.length == 0) {
|
||||
queue.done = true;
|
||||
if (isInternalMetadata(queue.entry._id)) {
|
||||
if (isInternalMetadata(queue.entry._id) && this.settings.syncInternalFiles) {
|
||||
//system file
|
||||
const filename = this.getPathWithoutPrefix(queue.entry);
|
||||
this.addOnHiddenFileSync.procInternalFile(filename);
|
||||
this.isTargetFile(filename).then((ret) => ret ? this.addOnHiddenFileSync.procInternalFile(filename) : Logger(`Skipped (Not target:${filename})`, LOG_LEVEL_VERBOSE));
|
||||
} else if (isValidPath(this.getPath(queue.entry))) {
|
||||
this.handleDBChanged(queue.entry);
|
||||
} else {
|
||||
@@ -1460,7 +1466,14 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
if (!await this.isTargetFile(path)) return;
|
||||
const skipOldFile = this.settings.skipOlderFilesOnSync && false; //patched temporary.
|
||||
// Do not handle internal files if the feature has not been enabled.
|
||||
if (isInternalMetadata(doc._id) && !this.settings.syncInternalFiles) return;
|
||||
if (isInternalMetadata(doc._id) && !this.settings.syncInternalFiles) {
|
||||
Logger(`Skipped: ${path} (${doc._id}, ${doc._rev}) Hidden file sync is disabled.`, LOG_LEVEL_VERBOSE);
|
||||
return;
|
||||
}
|
||||
if (isCustomisationSyncMetadata(doc._id) && !this.settings.usePluginSync) {
|
||||
Logger(`Skipped: ${path} (${doc._id}, ${doc._rev}) Customization sync is disabled.`, LOG_LEVEL_VERBOSE);
|
||||
return;
|
||||
}
|
||||
// It is better for your own safety, not to handle the following files
|
||||
const ignoreFiles = [
|
||||
"_design/replicate",
|
||||
@@ -1534,12 +1547,22 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
|
||||
if (change.type != "leaf" && change.type != "versioninfo" && change.type != "milestoneinfo" && change.type != "nodeinfo") {
|
||||
if (this.settings.suspendParseReplicationResult) {
|
||||
if (isInternalMetadata(change._id) && !this.settings.syncInternalFiles) {
|
||||
continue;
|
||||
}
|
||||
if (isCustomisationSyncMetadata(change._id) && !this.settings.usePluginSync) {
|
||||
continue;
|
||||
}
|
||||
if (!change.path) {
|
||||
continue;
|
||||
}
|
||||
const newQueue = {
|
||||
entry: change,
|
||||
missingChildren: [] as string[],
|
||||
timeout: 0,
|
||||
};
|
||||
Logger(`Processing scheduled: ${change.path}`, LOG_LEVEL_INFO);
|
||||
this.queuedFiles = this.queuedFiles.filter(e => e.entry.path != change.path);
|
||||
this.queuedFiles.push(newQueue);
|
||||
this.saveQueuedFiles();
|
||||
continue;
|
||||
|
||||
25
updates.md
25
updates.md
@@ -11,7 +11,6 @@ We can use the new feature with the same configuration. Only the menu on the com
|
||||
|
||||
I hope you will give it a try.
|
||||
|
||||
|
||||
#### Minors
|
||||
|
||||
- 0.19.1 to 0.19.17 has been moved into the updates_old.md
|
||||
@@ -28,7 +27,7 @@ I hope you will give it a try.
|
||||
- 0.19.20
|
||||
- New feature:
|
||||
- `Sync on Editor save` has been implemented
|
||||
- We can start synchronisation when we save from the Obsidian explicitly.
|
||||
- We can start synchronisation when we save from the Obsidian explicitly.
|
||||
- Now we can use the `Hidden file sync` and the `Customization sync` cooperatively.
|
||||
- We can exclude files from `Hidden file sync` which is already handled in Customization sync.
|
||||
- We can ignore specific plugins in Customization sync.
|
||||
@@ -44,4 +43,24 @@ I hope you will give it a try.
|
||||
- Missing dialogue titles have been shown now.
|
||||
- We can click close buttons on mobile now.
|
||||
- Conflicted Customisation sync files will be resolved automatically by their modified time.
|
||||
... To continue on to `updates_old.md`.
|
||||
- 0.19.21
|
||||
- Fixed:
|
||||
- Hidden files are no longer handled in the initial replication.
|
||||
- Report from `Making report` fixed
|
||||
- No longer contains customisation sync information.
|
||||
- Version of LiveSync has been added.
|
||||
- 0.19.22
|
||||
- Fixed:
|
||||
- Now the synchronisation will begin without our interaction.
|
||||
- No longer puts the configuration of the remote database into the log while checking configuration.
|
||||
- Some outdated description notes have been removed.
|
||||
- Options that are meaningless depending on other settings configured are now hidden.
|
||||
- Scan for hidden files before replication
|
||||
- Scan customization periodically
|
||||
- 0.19.23
|
||||
-Improved:
|
||||
- We can open the log pane also from the command palette now.
|
||||
- Now, the hidden file scanning interval could be configured to 0.
|
||||
- `Check database configuration` now points out that we do not have administrator permission.
|
||||
|
||||
... To continue on to `updates_old.md`.
|
||||
Reference in New Issue
Block a user