mirror of
https://github.com/al0cam/AutoMover.git
synced 2026-07-22 05:41:25 +00:00
feat: added auto time based moving, ribbon and cmd access to manual
This commit is contained in:
parent
8bd4d68467
commit
6f4cc32364
7 changed files with 148 additions and 18 deletions
|
|
@ -150,10 +150,11 @@ Thank you!
|
|||
- [x] Add excluded folder support
|
||||
- [x] Add excluded file support
|
||||
- [x] Add regex support for excluded folders and files (must support language accents like ñ, á, š, đ, こ, 猫, etc.)
|
||||
- [x] Add time based execution of rule sorting
|
||||
- [x] Exposing the move files button to the left toolbar
|
||||
- [x] Exposing the move files to the commands accessible via command palette
|
||||
- [ ] Add a file like .gitignore which contains all the moving rules (i am assuming the list can grow quite big for some people)
|
||||
- [ ] Add import and export of rules
|
||||
- [ ] Add time based execution of rule sorting
|
||||
- [ ] Exposing the move files button to the left toolbar
|
||||
- [ ] Auto tagging of moved files with the destination folder name (last folder in the path)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ export interface AutoMoverSettings {
|
|||
// moveOnSave: boolean;
|
||||
movingRules: MovingRule[];
|
||||
exclusionRules: ExclusionRule[];
|
||||
automaticMoving: boolean;
|
||||
timer: number | null; // in miliseconds
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: Partial<AutoMoverSettings> = {
|
||||
|
|
@ -14,6 +16,8 @@ export const DEFAULT_SETTINGS: Partial<AutoMoverSettings> = {
|
|||
// moveOnSave: true,
|
||||
movingRules: [],
|
||||
exclusionRules: [],
|
||||
automaticMoving: false,
|
||||
timer: null,
|
||||
};
|
||||
|
||||
function loadSettings(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import type AutoMoverPlugin from "main";
|
|||
import { ExclusionRule } from "Models/ExclusionRule";
|
||||
import { MovingRule } from "Models/MovingRule";
|
||||
import * as obsidian from "obsidian";
|
||||
import timerUtil from "Utils/TimerUtil";
|
||||
|
||||
export class SettingsTab extends obsidian.PluginSettingTab {
|
||||
plugin: AutoMoverPlugin;
|
||||
|
|
@ -38,6 +39,44 @@ export class SettingsTab extends obsidian.PluginSettingTab {
|
|||
});
|
||||
});
|
||||
|
||||
const automaticMovingContainer = containerEl.createDiv({});
|
||||
|
||||
new obsidian.Setting(automaticMovingContainer)
|
||||
.setName("Automatic moving")
|
||||
.setDesc(
|
||||
`Execute a timed event that goes through all the files and moves them according to the rules specified below.
|
||||
The formatting is hh:mm:ss.
|
||||
If the timer is set to 0, the automatic moving will do nothing.`,
|
||||
)
|
||||
.setClass("timer-setting")
|
||||
.addToggle((cb) =>
|
||||
cb
|
||||
.setValue(this.plugin.settings.automaticMoving)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.automaticMoving = value;
|
||||
await this.plugin.saveData(this.plugin.settings);
|
||||
this.app.workspace.trigger(
|
||||
"AutoMover:automatic-moving-update",
|
||||
this.plugin.settings,
|
||||
);
|
||||
this.display();
|
||||
}),
|
||||
)
|
||||
.addText((cb) =>
|
||||
cb
|
||||
.setDisabled(!this.plugin.settings.automaticMoving)
|
||||
.setValue(timerUtil.formatTime(this.plugin.settings.timer))
|
||||
.setPlaceholder("hh:mm:ss")
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.timer = timerUtil.parseTimeToMs(value);
|
||||
await this.plugin.saveData(this.plugin.settings);
|
||||
this.app.workspace.trigger(
|
||||
"AutoMover:automatic-moving-update",
|
||||
this.plugin.settings,
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
// there is no default event to move on save, therefore, i'd need to define a new one
|
||||
// running move on change could be an option, but it would produce an overhead in performance because of constant checking for changes
|
||||
//
|
||||
|
|
|
|||
|
|
@ -20,12 +20,8 @@ class ExclusionMatcherUtil {
|
|||
* @returns true if the file path is excluded, false otherwise
|
||||
*/
|
||||
isFilePathExcluded(file: TFile, exclusionRules: ExclusionRule[]): boolean {
|
||||
console.log("Exclusion rules: ", exclusionRules);
|
||||
for (const rule of exclusionRules) {
|
||||
const regex = new RegExp(rule.regex);
|
||||
console.log("Regex: ", regex);
|
||||
console.log("File path: ", file.path);
|
||||
console.log("Result: ", regex.test(file.path));
|
||||
if (regex.test(file.path)) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
44
Utils/TimerUtil.ts
Normal file
44
Utils/TimerUtil.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
class TimerUtil {
|
||||
private static instance: TimerUtil;
|
||||
private timer: NodeJS.Timeout | null = null;
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static getInstance(): TimerUtil {
|
||||
if (!TimerUtil.instance) {
|
||||
TimerUtil.instance = new TimerUtil();
|
||||
}
|
||||
return TimerUtil.instance;
|
||||
}
|
||||
|
||||
formatTime(ms: number | null): string {
|
||||
if (ms == null) return "00:00:00";
|
||||
const totalSeconds = Math.floor(ms / 1000);
|
||||
const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, "0");
|
||||
const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(
|
||||
2,
|
||||
"0",
|
||||
);
|
||||
const seconds = String(totalSeconds % 60).padStart(2, "0");
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
parseTimeToMs(timeStr: string): number {
|
||||
const [h, m, s] = timeStr.split(":").map(Number);
|
||||
if ([h, m, s].some((n) => Number.isNaN(n))) return 0;
|
||||
return ((h ?? 0) * 3600 + (m ?? 0) * 60 + (s ?? 0)) * 1000;
|
||||
}
|
||||
|
||||
startTimer(callback: () => void, interval: number) {
|
||||
if (this.timer) {
|
||||
// console.log("Clearing existing timer");
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
|
||||
// console.log("Starting timer with interval: ", interval);
|
||||
this.timer = setTimeout(callback, interval);
|
||||
}
|
||||
}
|
||||
|
||||
const timerUtil = TimerUtil.getInstance();
|
||||
export default timerUtil;
|
||||
57
main.ts
57
main.ts
|
|
@ -4,6 +4,8 @@ import movingUtil from "Utils/MovingUtil";
|
|||
import ruleMatcherUtil from "Utils/RuleMatcherUtil";
|
||||
import * as obsidian from "obsidian";
|
||||
import exclusionMatcherUtil from "Utils/ExclusionMatcherUtil";
|
||||
import timerUtil from "Utils/TimerUtil";
|
||||
import { addIcon } from "obsidian";
|
||||
|
||||
export default class AutoMoverPlugin extends obsidian.Plugin {
|
||||
settings: Settings.AutoMoverSettings;
|
||||
|
|
@ -26,15 +28,47 @@ export default class AutoMoverPlugin extends obsidian.Plugin {
|
|||
if (!this.areThereRulesToApply()) return;
|
||||
if (!this.areRulesValid()) return;
|
||||
|
||||
if (this.settings.moveOnOpen) {
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("file-open", (file: obsidian.TFile) => {
|
||||
if (file == null || file.path == null) return;
|
||||
if (this.isFileExcluded(file)) return;
|
||||
this.matchAndMoveFile(file);
|
||||
}),
|
||||
);
|
||||
}
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("file-open", (file: obsidian.TFile) => {
|
||||
if (!this.settings.moveOnOpen) return;
|
||||
if (file == null || file.path == null) return;
|
||||
if (this.isFileExcluded(file)) return;
|
||||
this.matchAndMoveFile(file);
|
||||
}),
|
||||
);
|
||||
|
||||
this.registerEvent(
|
||||
// since i am defining my own event, ts-lint is crying about it but it works in the end
|
||||
this.app.workspace.on("AutoMover:automatic-moving-update", () => {
|
||||
// console.log("Automatic moving update");
|
||||
this.automaticMoving();
|
||||
}),
|
||||
);
|
||||
|
||||
this.addCommand({
|
||||
id: "AutoMover:move-files",
|
||||
name: "Move files",
|
||||
callback: () => {
|
||||
this.goThroughAllFiles();
|
||||
},
|
||||
});
|
||||
|
||||
this.addRibbonIcon("file-input", "AutoMover: Move files", () => {
|
||||
this.goThroughAllFiles();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reocurring function that will be called by the timer
|
||||
*
|
||||
* @returns void
|
||||
*/
|
||||
automaticMoving() {
|
||||
// console.log("Automatic moving run");
|
||||
if (!this.settings.automaticMoving) return;
|
||||
if (this.settings.timer == null || this.settings.timer <= 0) return;
|
||||
this.goThroughAllFiles();
|
||||
timerUtil.startTimer(this.automaticMoving.bind(this), this.settings.timer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -43,11 +77,10 @@ export default class AutoMoverPlugin extends obsidian.Plugin {
|
|||
* @returns void
|
||||
*/
|
||||
goThroughAllFiles() {
|
||||
// console.log("Going through all files");
|
||||
const files = this.app.vault.getFiles();
|
||||
for (const file of files) {
|
||||
if (file == null || file.path == null) continue;
|
||||
console.log("Going through file: ", file.path);
|
||||
console.log("File excluded: ", this.isFileExcluded(file));
|
||||
if (this.isFileExcluded(file)) continue;
|
||||
this.matchAndMoveFile(file);
|
||||
}
|
||||
|
|
@ -77,7 +110,7 @@ export default class AutoMoverPlugin extends obsidian.Plugin {
|
|||
* @returns void
|
||||
*/
|
||||
matchAndMoveFile(file: obsidian.TFile): void {
|
||||
console.log("Moving file: ", file.path);
|
||||
// console.log("Moving file: ", file.path);
|
||||
const rule = ruleMatcherUtil.getMatchingRule(
|
||||
file,
|
||||
this.settings.movingRules,
|
||||
|
|
|
|||
13
styles.css
13
styles.css
|
|
@ -63,3 +63,16 @@ If your plugin does not need CSS, delete this file.
|
|||
.rule_button_duplicate:hover {
|
||||
background-color: var(--color-blue) !important;
|
||||
}
|
||||
|
||||
/* Style for timer-setting input field */
|
||||
.setting-item.timer-setting input[type="text"] {
|
||||
width: auto;
|
||||
min-width: 8ch;
|
||||
/* Minimum for hh:mm:ss */
|
||||
max-width: 16ch;
|
||||
/* Allows up to ~9999:59:59 */
|
||||
/* padding: 2px 6px; */
|
||||
font-family: monospace;
|
||||
text-align: center;
|
||||
/* box-sizing: content-box; */
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue