diff --git a/.emacs.d/desktop.el b/.emacs.d/desktop.el index 9e6bc17..ae29ae8 100644 --- a/.emacs.d/desktop.el +++ b/.emacs.d/desktop.el @@ -381,14 +381,14 @@ DIR is either 'left or 'right." _t_: Terminal (Alacritty) _b_: Browser (Firefox) _s_: Rocket.Chat -_e_: Element -_d_: Discord +_d_: DBeaver +_c_: Chromium " ("t" (lambda () (interactive) (my/run-in-background "alacritty"))) ("b" (lambda () (interactive) (my/run-in-background "firefox"))) - ("s" (lambda () (interactive) (my/run-in-background "flatpak run chat.rocket.RocketChat"))) - ("e" (lambda () (interactive) (my/run-in-background "flatpak run im.riot.Riot"))) - ("d" (lambda () (interactive) (my/run-in-background "flatpak run com.discordapp.Discord")))) + ("s" (lambda () (interactive) (my/run-in-background "rocketchat-desktop"))) + ("d" (lambda () (interactive) (my/run-in-background "dbeaver"))) + ("c" (lambda () (interactive) (my/run-in-background "chromium")))) (defun my/exwm-lock () (interactive) @@ -594,10 +594,10 @@ _d_: Discord (my/exwm-set-wallpaper) (my/exwm-run-shepherd) - (my/exwm-run-systemd) (my/run-in-background "gpgconf --reload gpg-agent") (my/exwm-run-polybar) (setenv "DBUS_SESSION_BUS_ADDRESS" "unix:path=/run/user/1000/bus") + (my/exwm-run-systemd) (when (my/is-arch) (my/run-in-background "set_layout"))) diff --git a/.emacs.d/modules/sqrt-emms.el b/.emacs.d/modules/sqrt-emms.el index 95d2b47..732011b 100644 --- a/.emacs.d/modules/sqrt-emms.el +++ b/.emacs.d/modules/sqrt-emms.el @@ -82,6 +82,8 @@ ;; I have everything I need in polybar (emms-mode-line-mode -1) (emms-playing-time-display-mode -1) + (delq 'emms-mark-mode evil-emacs-state-modes) + (delq 'emms-browser-mode evil-emacs-state-modes) (defun emms-info-mpd-process (track info) (dolist (data info) (let ((name (car data)) diff --git a/.emacs.d/modules/sqrt-index.el b/.emacs.d/modules/sqrt-index.el index d4c6bb8..41d085c 100644 --- a/.emacs.d/modules/sqrt-index.el +++ b/.emacs.d/modules/sqrt-index.el @@ -375,8 +375,11 @@ REMOTE = '' FOLDERS = json.loads('') OPTIONS = json.loads('') +for folder, i in zip(FOLDERS, range(len(FOLDERS))): + folder['id'] = i -def rclone_make_command(local_path, remote_path, remote): + +def rclone_make_command(local_path, remote_path, remote, extra_args=[]): return [ 'rclone', 'bisync', @@ -390,7 +393,8 @@ def rclone_make_command(local_path, remote_path, remote): 'NEVER', '--use-json-log', '--stats', - '9999m' + '9999m', + *extra_args ] @@ -418,15 +422,26 @@ def process_output(output): except Exception: print(line) -def rclone_run(folder): +def process_command_for_print(command): + res = [] + for c in command: + if ' ' in c: + res.append(f'\\'{c}\\'') + else: + res.append(c) + return ' '.join(res) + + +def rclone_run(folder, extra_args=[]): command = rclone_make_command( - folder['local-path'], folder['remote-path'], folder['remote'] + folder['local-path'], folder['remote-path'], folder['remote'], extra_args ) try: + print(str(folder['id']) + '. ' + process_command_for_print(command)) result = subprocess.run(command, check=True, capture_output=True, text=True) except subprocess.CalledProcessError as e: print(f'=== Error syncing {folder['local-path']} ===') - print(f'Command: {' '.join(command)}') + print(f'Command: {process_command_for_print(command)}') print(f'--- STDOUT ---') process_output(e.stdout) print(f'--- STDERR ---') @@ -434,7 +449,6 @@ def rclone_run(folder): return {'success': False, 'stats': {}} return {'success': True, 'stats': parse_rclone_stats(result.stderr)} - def notify(summary, body, level='normal', expire_time=5000): subprocess.run(['notify-send', '-u', level, '-t', str(expire_time), summary, body]) @@ -447,17 +461,16 @@ def sizeof_fmt(num, suffix='B'): return f'{num:.1f}Yi{suffix}' -def rclone_run_all(folders): +def rclone_run_all(folders, extra_args=[]): error_folders = [] total_bytes = 0 total_transfers = 0 total_deleted = 0 total_renamed = 0 for folder in folders: - print(f'Running rclone for {folder}') - res = rclone_run(folder) + res = rclone_run(folder, extra_args) if not res['success']: - error_folders.append(folder['local-path']) + error_folders.append(folder) else: total_bytes += res.get('stats', {}).get('bytes', 0) total_transfers += res.get('stats', {}).get('transfers', 0) @@ -469,21 +482,44 @@ def rclone_run_all(folders): if total_transfers > 0: msg += f'''Transferred {total_transfers} files ({sizeof_fmt(total_bytes)})\n''' if total_deleted > 0: - msg += f'''Deleted {total_transfers} files\n''' + msg += f'''Deleted {total_deleted} files\n''' if total_renamed > 0: msg += f'''Renamed {total_renamed} files\n''' if len(error_folders) > 0: msg += '''\nSync errors for the following folders:''' for folder in error_folders: - msg += '''\n- ''' + folder + msg += '''\n- ''' + str(folder['id']) + '. ' + folder['local-path'] level = 'critical' if len(msg) > 0: notify(f'rclone sync {REMOTE}', msg, level=level) +def parse_arguments(): + if len(sys.argv) < 2: + return None, [] + + id_arg = sys.argv[1] + folder_ids = [int(x.strip()) for x in id_arg.split(',')] + + extra_args = sys.argv[2:] + + return folder_ids, extra_args + + if __name__ == '__main__': - rclone_run_all(FOLDERS) + folder_ids, extra_args = parse_arguments() + + if folder_ids is None: + selected_folders = FOLDERS + else: + selected_folders = [f for f in FOLDERS if f['id'] in folder_ids] + found_ids = {f['id'] for f in selected_folders} + missing_ids = set(folder_ids) - found_ids + if missing_ids: + print(f'Warning: folder IDs not found: {sorted(missing_ids)}') + + rclone_run_all(selected_folders, extra_args) ")) (setq script (thread-last script @@ -562,6 +598,23 @@ The return value is a list of commands as defined by commands)) (nreverse commands))) +(defun my/index-rclone-reset () + "Delete all rclone test files." + (interactive) + (let* ((tree (my/index--tree-retrive)) + (folders (my/index--rclone-get-folders tree)) + files-to-delete) + (dolist (folder folders) + (let ((test-file-path + (concat + (alist-get :local-path folder) + (format ".rclone-test-%s" (alist-get :remote folder))))) + (when (file-exists-p test-file-path) + (push test-file-path files-to-delete)))) + (when (y-or-n-p (format "Delete %d files" (seq-length files-to-delete))) + (dolist (file files-to-delete) + (delete-file file))))) + (defun my/index--git-commands (tree) "Get commands to clone the yet uncloned git repos in TREE. diff --git a/Desktop.org b/Desktop.org index b389a5e..1b5314f 100644 --- a/Desktop.org +++ b/Desktop.org @@ -965,14 +965,14 @@ A +transient+ hydra for shortcuts for the most frequent apps. _t_: Terminal (Alacritty) _b_: Browser (Firefox) _s_: Rocket.Chat -_e_: Element -_d_: Discord +_d_: DBeaver +_c_: Chromium " ("t" (lambda () (interactive) (my/run-in-background "alacritty"))) ("b" (lambda () (interactive) (my/run-in-background "firefox"))) - ("s" (lambda () (interactive) (my/run-in-background "flatpak run chat.rocket.RocketChat"))) - ("e" (lambda () (interactive) (my/run-in-background "flatpak run im.riot.Riot"))) - ("d" (lambda () (interactive) (my/run-in-background "flatpak run com.discordapp.Discord")))) + ("s" (lambda () (interactive) (my/run-in-background "rocketchat-desktop"))) + ("d" (lambda () (interactive) (my/run-in-background "dbeaver"))) + ("c" (lambda () (interactive) (my/run-in-background "chromium")))) #+end_src *** Locking up Run i3lock. @@ -1219,10 +1219,10 @@ And the EXWM config itself. (my/exwm-set-wallpaper) (my/exwm-run-shepherd) - (my/exwm-run-systemd) (my/run-in-background "gpgconf --reload gpg-agent") (my/exwm-run-polybar) (setenv "DBUS_SESSION_BUS_ADDRESS" "unix:path=/run/user/1000/bus") + (my/exwm-run-systemd) (when (my/is-arch) (my/run-in-background "set_layout"))) @@ -4239,6 +4239,7 @@ This section generates manifests for various desktop software that I'm using. | office | okular | | office | obs-studio | | office | rocketchat-desktop | + ** LaTeX | Category | Arch dependency | Disabled | |----------+--------------------------+----------| @@ -4540,6 +4541,7 @@ Other desktop programs I use are listed below. | desktop-misc | noto-fonts-emoji | | | desktop-misc | remmina | | | desktop-misc | android-file-transfer | | +| desktop-misc | veracrypt | | #+NAME: packages #+begin_src emacs-lisp :tangle no diff --git a/Emacs.org b/Emacs.org index 7288344..63f358d 100644 --- a/Emacs.org +++ b/Emacs.org @@ -10433,6 +10433,8 @@ References: ;; I have everything I need in polybar (emms-mode-line-mode -1) (emms-playing-time-display-mode -1) + (delq 'emms-mark-mode evil-emacs-state-modes) + (delq 'emms-browser-mode evil-emacs-state-modes) <>) #+end_src **** MPD @@ -12870,8 +12872,11 @@ REMOTE = '' FOLDERS = json.loads('') OPTIONS = json.loads('') +for folder, i in zip(FOLDERS, range(len(FOLDERS))): + folder['id'] = i -def rclone_make_command(local_path, remote_path, remote): + +def rclone_make_command(local_path, remote_path, remote, extra_args=[]): return [ 'rclone', 'bisync', @@ -12885,7 +12890,8 @@ def rclone_make_command(local_path, remote_path, remote): 'NEVER', '--use-json-log', '--stats', - '9999m' + '9999m', + ,*extra_args ] @@ -12913,15 +12919,26 @@ def process_output(output): except Exception: print(line) -def rclone_run(folder): +def process_command_for_print(command): + res = [] + for c in command: + if ' ' in c: + res.append(f'\\'{c}\\'') + else: + res.append(c) + return ' '.join(res) + + +def rclone_run(folder, extra_args=[]): command = rclone_make_command( - folder['local-path'], folder['remote-path'], folder['remote'] + folder['local-path'], folder['remote-path'], folder['remote'], extra_args ) try: + print(str(folder['id']) + '. ' + process_command_for_print(command)) result = subprocess.run(command, check=True, capture_output=True, text=True) except subprocess.CalledProcessError as e: print(f'=== Error syncing {folder['local-path']} ===') - print(f'Command: {' '.join(command)}') + print(f'Command: {process_command_for_print(command)}') print(f'--- STDOUT ---') process_output(e.stdout) print(f'--- STDERR ---') @@ -12929,7 +12946,6 @@ def rclone_run(folder): return {'success': False, 'stats': {}} return {'success': True, 'stats': parse_rclone_stats(result.stderr)} - def notify(summary, body, level='normal', expire_time=5000): subprocess.run(['notify-send', '-u', level, '-t', str(expire_time), summary, body]) @@ -12942,17 +12958,16 @@ def sizeof_fmt(num, suffix='B'): return f'{num:.1f}Yi{suffix}' -def rclone_run_all(folders): +def rclone_run_all(folders, extra_args=[]): error_folders = [] total_bytes = 0 total_transfers = 0 total_deleted = 0 total_renamed = 0 for folder in folders: - print(f'Running rclone for {folder}') - res = rclone_run(folder) + res = rclone_run(folder, extra_args) if not res['success']: - error_folders.append(folder['local-path']) + error_folders.append(folder) else: total_bytes += res.get('stats', {}).get('bytes', 0) total_transfers += res.get('stats', {}).get('transfers', 0) @@ -12964,21 +12979,44 @@ def rclone_run_all(folders): if total_transfers > 0: msg += f'''Transferred {total_transfers} files ({sizeof_fmt(total_bytes)})\n''' if total_deleted > 0: - msg += f'''Deleted {total_transfers} files\n''' + msg += f'''Deleted {total_deleted} files\n''' if total_renamed > 0: msg += f'''Renamed {total_renamed} files\n''' if len(error_folders) > 0: msg += '''\nSync errors for the following folders:''' for folder in error_folders: - msg += '''\n- ''' + folder + msg += '''\n- ''' + str(folder['id']) + '. ' + folder['local-path'] level = 'critical' if len(msg) > 0: notify(f'rclone sync {REMOTE}', msg, level=level) +def parse_arguments(): + if len(sys.argv) < 2: + return None, [] + + id_arg = sys.argv[1] + folder_ids = [int(x.strip()) for x in id_arg.split(',')] + + extra_args = sys.argv[2:] + + return folder_ids, extra_args + + if __name__ == '__main__': - rclone_run_all(FOLDERS) + folder_ids, extra_args = parse_arguments() + + if folder_ids is None: + selected_folders = FOLDERS + else: + selected_folders = [f for f in FOLDERS if f['id'] in folder_ids] + found_ids = {f['id'] for f in selected_folders} + missing_ids = set(folder_ids) - found_ids + if missing_ids: + print(f'Warning: folder IDs not found: {sorted(missing_ids)}') + + rclone_run_all(selected_folders, extra_args) #+end_src A function that templates the script above: @@ -13073,6 +13111,26 @@ The return value is a list of commands as defined by (nreverse commands))) #+end_src +Also, a command to remove all =.rclone-test-*= files. This is necessary, e.g. after a change in the filter file. +#+begin_src emacs-lisp +(defun my/index-rclone-reset () + "Delete all rclone test files." + (interactive) + (let* ((tree (my/index--tree-retrive)) + (folders (my/index--rclone-get-folders tree)) + files-to-delete) + (dolist (folder folders) + (let ((test-file-path + (concat + (alist-get :local-path folder) + (format ".rclone-test-%s" (alist-get :remote folder))))) + (when (file-exists-p test-file-path) + (push test-file-path files-to-delete)))) + (when (y-or-n-p (format "Delete %d files" (seq-length files-to-delete))) + (dolist (file files-to-delete) + (delete-file file))))) +#+end_src + **** Git repos To sync git, we just need to clone the required git repos. Removing the repos is handled by the folder sync commands.