Compare commits

...

5 Commits

Author SHA1 Message Date
vorotamoroz
b02596dfa1 bump 2023-04-11 12:45:40 +09:00
vorotamoroz
02c69b202e Improved:
- The setting pane refined.
- We can enable `hidden files sync` with several initial behaviours: `Merge`, `Fetch` remote, and `Overwrite` remote.
- No longer `Touch hidden files`
2023-04-11 12:45:24 +09:00
vorotamoroz
6b2c7b56a5 add note. 2023-04-10 15:18:32 +09:00
vorotamoroz
820168a5ab bump. 2023-04-10 15:15:20 +09:00
vorotamoroz
40015642e4 Fixed
- fixed type annotation
- update lib
2023-04-10 15:14:47 +09:00
8 changed files with 100 additions and 61 deletions

View File

@@ -1,7 +1,7 @@
{ {
"id": "obsidian-livesync", "id": "obsidian-livesync",
"name": "Self-hosted LiveSync", "name": "Self-hosted LiveSync",
"version": "0.18.0", "version": "0.18.2",
"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
View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "obsidian-livesync", "name": "obsidian-livesync",
"version": "0.18.0", "version": "0.18.2",
"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",

View File

@@ -215,7 +215,7 @@ export class HiddenFileSync extends LiveSyncCommands {
} }
//TODO: Tidy up. Even though it is experimental feature, So dirty... //TODO: Tidy up. Even though it is experimental feature, So dirty...
async syncInternalFilesAndDatabase(direction: "push" | "pull" | "safe", showMessage: boolean, files: InternalFileInfo[] | false = false, targetFiles: string[] | false = false) { async syncInternalFilesAndDatabase(direction: "push" | "pull" | "safe" | "pullForce" | "pushForce", showMessage: boolean, files: InternalFileInfo[] | false = false, targetFiles: string[] | false = false) {
await this.resolveConflictOnInternalFiles(); await this.resolveConflictOnInternalFiles();
const logLevel = showMessage ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO; const logLevel = showMessage ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO;
Logger("Scanning hidden files.", logLevel, "sync_internal"); Logger("Scanning hidden files.", logLevel, "sync_internal");
@@ -289,43 +289,43 @@ export class HiddenFileSync extends LiveSyncCommands {
p.push(addProc(async () => { p.push(addProc(async () => {
const xFileOnStorage = fileOnStorage; const xFileOnStorage = fileOnStorage;
const xfileOnDatabase = fileOnDatabase; const xFileOnDatabase = fileOnDatabase;
if (xFileOnStorage && xfileOnDatabase) { if (xFileOnStorage && xFileOnDatabase) {
// Both => Synchronize // Both => Synchronize
if (xfileOnDatabase.mtime == cache.docMtime && xFileOnStorage.mtime == cache.storageMtime) { if ((direction != "pullForce" && direction != "pushForce") && xFileOnDatabase.mtime == cache.docMtime && xFileOnStorage.mtime == cache.storageMtime) {
return; return;
} }
const nw = compareMTime(xFileOnStorage.mtime, xfileOnDatabase.mtime); const nw = compareMTime(xFileOnStorage.mtime, xFileOnDatabase.mtime);
if (nw > 0) { if (nw > 0 || direction == "pushForce") {
await this.storeInternalFileToDatabase(xFileOnStorage); await this.storeInternalFileToDatabase(xFileOnStorage);
} }
if (nw < 0) { if (nw < 0 || direction == "pullForce") {
// skip if not extraction performed. // skip if not extraction performed.
if (!await this.extractInternalFileFromDatabase(filename)) if (!await this.extractInternalFileFromDatabase(filename))
return; return;
} }
// If process successfully updated or file contents are same, update cache. // If process successfully updated or file contents are same, update cache.
cache.docMtime = xfileOnDatabase.mtime; cache.docMtime = xFileOnDatabase.mtime;
cache.storageMtime = xFileOnStorage.mtime; cache.storageMtime = xFileOnStorage.mtime;
caches[filename] = cache; caches[filename] = cache;
countUpdatedFolder(filename); countUpdatedFolder(filename);
} else if (!xFileOnStorage && xfileOnDatabase) { } else if (!xFileOnStorage && xFileOnDatabase) {
if (direction == "push") { if (direction == "push" || direction == "pushForce") {
if (xfileOnDatabase.deleted) if (xFileOnDatabase.deleted)
return; return;
await this.deleteInternalFileOnDatabase(filename, false); await this.deleteInternalFileOnDatabase(filename, false);
} else if (direction == "pull") { } else if (direction == "pull" || direction == "pullForce") {
if (await this.extractInternalFileFromDatabase(filename)) { if (await this.extractInternalFileFromDatabase(filename)) {
countUpdatedFolder(filename); countUpdatedFolder(filename);
} }
} else if (direction == "safe") { } else if (direction == "safe") {
if (xfileOnDatabase.deleted) if (xFileOnDatabase.deleted)
return; return;
if (await this.extractInternalFileFromDatabase(filename)) { if (await this.extractInternalFileFromDatabase(filename)) {
countUpdatedFolder(filename); countUpdatedFolder(filename);
} }
} }
} else if (xFileOnStorage && !xfileOnDatabase) { } else if (xFileOnStorage && !xFileOnDatabase) {
await this.storeInternalFileToDatabase(xFileOnStorage); await this.storeInternalFileToDatabase(xFileOnStorage);
} else { } else {
throw new Error("Invalid state on hidden file sync"); throw new Error("Invalid state on hidden file sync");
@@ -337,7 +337,7 @@ export class HiddenFileSync extends LiveSyncCommands {
await this.kvDB.set("diff-caches-internal", caches); await this.kvDB.set("diff-caches-internal", caches);
// When files has been retrieved from the database. they must be reloaded. // When files has been retrieved from the database. they must be reloaded.
if (direction == "pull" && filesChanged != 0) { if (direction == "pull" || direction == "pullForce" && filesChanged != 0) {
const configDir = normalizePath(this.app.vault.configDir); const configDir = normalizePath(this.app.vault.configDir);
// Show notification to restart obsidian when something has been changed in configDir. // Show notification to restart obsidian when something has been changed in configDir.
if (configDir in updatedFolders) { if (configDir in updatedFolders) {
@@ -468,7 +468,7 @@ export class HiddenFileSync extends LiveSyncCommands {
}; };
} else { } else {
if (isDocContentSame(old.data, content) && !forceWrite) { if (isDocContentSame(old.data, content) && !forceWrite) {
// Logger(`internal files STORAGE --> DB:${file.path}: Not changed`); // Logger(`STORAGE --> DB:${file.path}: (hidden) Not changed`, LOG_LEVEL.VERBOSE);
return; return;
} }
saveData = saveData =

View File

@@ -966,7 +966,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
}); });
c.addClass("op-warn"); c.addClass("op-warn");
} }
containerSyncSettingEl.createEl("h3", { text: "Synchronization Methods" });
const syncLive: Setting[] = []; const syncLive: Setting[] = [];
const syncNonLive: Setting[] = []; const syncNonLive: Setting[] = [];
syncLive.push( syncLive.push(
@@ -1019,6 +1019,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
text.inputEl.setAttribute("type", "number"); text.inputEl.setAttribute("type", "number");
}), }),
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Sync on Save") .setName("Sync on Save")
.setDesc("When you save file, sync automatically") .setDesc("When you save file, sync automatically")
@@ -1060,7 +1061,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
}) })
), ),
); );
containerSyncSettingEl.createEl("h3", { text: "File deletion" });
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Use Trash for deleted files") .setName("Use Trash for deleted files")
.setDesc("Do not delete files that are deleted in remote, just move to trash.") .setDesc("Do not delete files that are deleted in remote, just move to trash.")
@@ -1081,6 +1082,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
}) })
); );
containerSyncSettingEl.createEl("h3", { text: "Conflict resolution" });
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Use newer file if conflicted (beta)") .setName("Use newer file if conflicted (beta)")
.setDesc("Resolve conflicts by newer files automatically.") .setDesc("Resolve conflicts by newer files automatically.")
@@ -1119,14 +1121,63 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
}) })
); );
new Setting(containerSyncSettingEl) containerSyncSettingEl.createEl("h3", { text: "Hidden files" });
.setName("Sync hidden files") const LABEL_ENABLED = "🔁 : Enabled";
.addToggle((toggle) => const LABEL_DISABLED = "⏹️ : Disabled"
toggle.setValue(this.plugin.settings.syncInternalFiles).onChange(async (value) => {
this.plugin.settings.syncInternalFiles = value; const hiddenFileSyncSetting = new Setting(containerSyncSettingEl)
await this.plugin.saveSettings(); .setName("Hidden file synchronization")
const hiddenFileSyncSettingEl = hiddenFileSyncSetting.settingEl
const hiddenFileSyncSettingDiv = hiddenFileSyncSettingEl.createDiv("");
hiddenFileSyncSettingDiv.innerText = this.plugin.settings.syncInternalFiles ? LABEL_ENABLED : LABEL_DISABLED;
if (this.plugin.settings.syncInternalFiles) {
new Setting(containerSyncSettingEl)
.setName("Disable Hidden files sync")
.addButton((button) => {
button.setButtonText("Disable")
.onClick(async () => {
this.plugin.settings.syncInternalFiles = false;
await this.plugin.saveSettings();
this.display();
})
}) })
); } else {
new Setting(containerSyncSettingEl)
.setName("Enable Hidden files sync")
.addButton((button) => {
button.setButtonText("Merge")
.onClick(async () => {
this.plugin.settings.syncInternalFiles = true;
this.display();
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("safe", true);
await this.plugin.saveSettings();
this.display();
})
})
.addButton((button) => {
button.setButtonText("Fetch")
.onClick(async () => {
this.plugin.settings.syncInternalFiles = true;
this.display();
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pullForce", true);
await this.plugin.saveSettings();
Logger(`Restarting the app is strongly recommended!`, LOG_LEVEL.NOTICE);
this.display();
})
})
.addButton((button) => {
button.setButtonText("Overwrite")
.onClick(async () => {
this.plugin.settings.syncInternalFiles = true;
this.display();
await this.plugin.addOnHiddenFileSync.syncInternalFilesAndDatabase("pushForce", true);
await this.plugin.saveSettings();
this.display();
})
});
}
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Scan for hidden files before replication") .setName("Scan for hidden files before replication")
@@ -1191,31 +1242,9 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
}) })
}) })
new Setting(containerSyncSettingEl)
.setName("Touch hidden files")
.setDesc("Update the modified time of all hidden files to the current time.")
.addButton((button) =>
button
.setButtonText("Touch")
.setWarning()
.setDisabled(false)
.onClick(async () => {
const filesAll = await this.plugin.addOnHiddenFileSync.scanInternalFiles();
const targetFiles = await this.plugin.filterTargetFiles(filesAll);
const now = Date.now();
const newFiles = targetFiles.map(e => ({ ...e, mtime: now }));
let i = 0;
const maxFiles = newFiles.length;
for (const file of newFiles) {
i++;
Logger(`Touched:${file.path} (${i}/${maxFiles})`, LOG_LEVEL.NOTICE, "touch-files");
await this.plugin.applyMTimeToFile(file);
}
})
)
containerSyncSettingEl.createEl("h3", { containerSyncSettingEl.createEl("h3", {
text: sanitizeHTMLToDom(`Experimental`), text: sanitizeHTMLToDom(`Synchronization filters`),
}); });
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Regular expression to ignore files") .setName("Regular expression to ignore files")
@@ -1263,7 +1292,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
return text; return text;
} }
); );
containerSyncSettingEl.createEl("h3", { text: "Efficiency" });
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Chunk size") .setName("Chunk size")
.setDesc("Customize chunk size for binary files (0.1MBytes). This cannot be increased when using IBM Cloudant.") .setDesc("Customize chunk size for binary files (0.1MBytes). This cannot be increased when using IBM Cloudant.")
@@ -1282,7 +1311,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
}); });
new Setting(containerSyncSettingEl) new Setting(containerSyncSettingEl)
.setName("Read chunks online.") .setName("Read chunks online")
.setDesc("If this option is enabled, LiveSync reads chunks online directly instead of replicating them locally. Increasing Custom chunk size is recommended.") .setDesc("If this option is enabled, LiveSync reads chunks online directly instead of replicating them locally. Increasing Custom chunk size is recommended.")
.addToggle((toggle) => { .addToggle((toggle) => {
toggle toggle
@@ -1292,8 +1321,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}) })
return toggle; return toggle;
} });
);
containerSyncSettingEl.createEl("h3", { containerSyncSettingEl.createEl("h3", {
text: sanitizeHTMLToDom(`Advanced settings`), text: sanitizeHTMLToDom(`Advanced settings`),
}); });
@@ -1487,8 +1515,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
} }
} }
this.plugin.saveSettings(); this.plugin.saveSettings();
await this.plugin.realizeSettingSyncMode();
this.display(); this.display();
await this.plugin.realizeSettingSyncMode();
if (inWizard) { if (inWizard) {
// @ts-ignore // @ts-ignore
this.plugin.app.setting.close() this.plugin.app.setting.close()

View File

@@ -1,7 +1,7 @@
import { Plugin_2, TAbstractFile, TFile, TFolder } from "./deps"; import { Plugin_2, TAbstractFile, TFile, TFolder } from "./deps";
import { isPlainText, shouldBeIgnored } from "./lib/src/path"; import { isPlainText, shouldBeIgnored } from "./lib/src/path";
import { getGlobalStore } from "./lib/src/store"; import { getGlobalStore } from "./lib/src/store";
import { ObsidianLiveSyncSettings } from "./lib/src/types"; import { FilePath, ObsidianLiveSyncSettings } from "./lib/src/types";
import { FileEventItem, FileEventType, FileInfo, InternalFileInfo, queueItem } from "./types"; import { FileEventItem, FileEventType, FileInfo, InternalFileInfo, queueItem } from "./types";
import { recentlyTouched } from "./utils"; import { recentlyTouched } from "./utils";
@@ -58,12 +58,12 @@ export class StorageEventManagerObsidian extends StorageEventManager {
if (file instanceof TFile) { if (file instanceof TFile) {
this.appendWatchEvent([ this.appendWatchEvent([
{ type: "CREATE", file }, { type: "CREATE", file },
{ type: "DELETE", file: { path: oldFile, mtime: file.stat.mtime, ctime: file.stat.ctime, size: file.stat.size, deleted: true } } { type: "DELETE", file: { path: oldFile as FilePath, mtime: file.stat.mtime, ctime: file.stat.ctime, size: file.stat.size, deleted: true } },
], ctx); ], ctx);
} }
} }
// Watch raw events (Internal API) // Watch raw events (Internal API)
watchVaultRawEvents(path: string) { watchVaultRawEvents(path: FilePath) {
if (!this.plugin.settings.syncInternalFiles) return; if (!this.plugin.settings.syncInternalFiles) return;
if (!this.plugin.settings.watchInternalFileChanges) return; if (!this.plugin.settings.watchInternalFileChanges) return;
if (!path.startsWith(app.vault.configDir)) return; if (!path.startsWith(app.vault.configDir)) return;

Submodule src/lib updated: f644c8dfc3...f5db618612

View File

@@ -8,6 +8,17 @@ Since v0.18.0, they can be obfuscated. so it is no longer possible to decipher t
We can configure the `Path Obfuscation` in the `Remote database configuration` pane. We can configure the `Path Obfuscation` in the `Remote database configuration` pane.
Note: **When changing this configuration, we need to rebuild both of the local and the remote databases**. Note: **When changing this configuration, we need to rebuild both of the local and the remote databases**.
#### Minors
- 0.18.1
- Fixed:
- Some messages are fixed (Typo)
- File type detection now works fine!
- 0.18.2
- Improved:
- The setting pane has been refined.
- We can enable `hidden files sync` with several initial behaviours; `Merge`, `Fetch` remote, and `Overwrite` remote.
- No longer `Touch hidden files`.
### 0.17.0 ### 0.17.0
- 0.17.0 has no surfaced changes but the design of saving chunks has been changed. They have compatibility but changing files after upgrading makes different chunks than before 0.16.x. - 0.17.0 has no surfaced changes but the design of saving chunks has been changed. They have compatibility but changing files after upgrading makes different chunks than before 0.16.x.
Please rebuild databases once if you have been worried about storage usage. Please rebuild databases once if you have been worried about storage usage.