@codebend3r/che 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +424 -0
  3. package/bin/che.py +869 -0
  4. package/bin/commands.py +872 -0
  5. package/bin/drives/eject-all-drives.py +203 -0
  6. package/bin/drives/mount-all-drives.py +140 -0
  7. package/bin/drives/ping-nas.py +242 -0
  8. package/bin/files/compress-folders.py +188 -0
  9. package/bin/files/delete-by-ext.py +108 -0
  10. package/bin/files/delete-empty-folders.py +136 -0
  11. package/bin/files/delete-smb-files.py +105 -0
  12. package/bin/files/files-under-size.py +132 -0
  13. package/bin/files/find-largest-files.py +102 -0
  14. package/bin/files/make-alpha-dir.py +63 -0
  15. package/bin/git/all-actions.py +545 -0
  16. package/bin/git/checkout-my-branches.py +147 -0
  17. package/bin/git/clean-stale-branches.py +161 -0
  18. package/bin/git/prune-worktrees.py +143 -0
  19. package/bin/git/sync-all-branches.py +368 -0
  20. package/bin/git/update-local-branches.py +195 -0
  21. package/bin/install.py +1515 -0
  22. package/bin/shellgen.py +782 -0
  23. package/bin/system/btop-launch.py +114 -0
  24. package/bin/system/update-brew.py +146 -0
  25. package/bin/tui.py +648 -0
  26. package/bin/utils.py +836 -0
  27. package/bin/video/delete-duplicate-videos.py +313 -0
  28. package/bin/video/detect-green-magenta-videos.py +268 -0
  29. package/bin/video/find-movie-by-year.py +84 -0
  30. package/bin/video/find-video-mkv-issues.py +242 -0
  31. package/bin/video/fix-codecs.py +209 -0
  32. package/bin/video/largest-tv-shows.py +175 -0
  33. package/bin/video/remove-metadata.py +126 -0
  34. package/bin/video/rename-video-file.py +329 -0
  35. package/bin/video/scan-videos-audio-language.py +110 -0
  36. package/bin/video/show-codecs.py +111 -0
  37. package/bin/video/validate-video-files.py +114 -0
  38. package/bin/video/video-list.py +120 -0
  39. package/package.json +54 -0
  40. package/shell/che.bash +382 -0
  41. package/shell/che.fish +282 -0
  42. package/shell/che.zsh +389 -0
  43. package/shell/completions/_che +146 -0
  44. package/shell/completions/che.bash +107 -0
  45. package/shell/completions/che.fish +371 -0
package/shell/che.fish ADDED
@@ -0,0 +1,282 @@
1
+ # che 1.0.0 - generated file, do not edit.
2
+ #
3
+ # Generated from bin/commands.py by `che install`.
4
+ # To change a wrapper, edit that manifest and run `che install` again.
5
+
6
+ if not set -q CHE_HOME
7
+ set -l __che_dir (dirname (status --current-filename))/..
8
+ if type -q path
9
+ set -gx CHE_HOME (path resolve $__che_dir)
10
+ else
11
+ set -gx CHE_HOME $__che_dir
12
+ end
13
+ set -e __che_dir
14
+ end
15
+ set -gx CHE_BIN "$CHE_HOME/bin"
16
+ if not set -q CHE_PYTHON
17
+ set -gx CHE_PYTHON python3
18
+ end
19
+ set -gx SHELL_FUNCTIONS_BIN "$CHE_BIN"
20
+
21
+ # Play a wrapper's completion sound, then return the status it was given.
22
+ function che_notify --description 'che: completion sound'
23
+ set -l code 0
24
+ if set -q argv[2]
25
+ set code $argv[2]
26
+ end
27
+ if test "$CHE_SOUNDS" != "0"
28
+ if type -q "playsound-$argv[1]"
29
+ "playsound-$argv[1]" >/dev/null 2>&1
30
+ end
31
+ end
32
+ return $code
33
+ end
34
+
35
+ function che --description 'che: shell helpers dispatcher'
36
+ if test (count $argv) -eq 0
37
+ "$CHE_PYTHON" "$CHE_BIN/che.py"
38
+ else
39
+ "$CHE_PYTHON" "$CHE_BIN/che.py" $argv
40
+ che_notify 7 $status
41
+ end
42
+ end
43
+
44
+ # --------------------------------------------------------------------------
45
+ # Git - Branch hygiene, worktrees and GitHub Actions
46
+ # --------------------------------------------------------------------------
47
+
48
+ function all-actions --description 'GitHub Actions status for every open PR you authored'
49
+ "$CHE_PYTHON" "$CHE_BIN/git/all-actions.py" $argv
50
+ end
51
+
52
+ function all-actions-watch --description 'Same as all-actions, refreshed on an interval'
53
+ "$CHE_PYTHON" "$CHE_BIN/git/all-actions.py" --watch $argv
54
+ end
55
+
56
+ function checkout-my-branches --description 'Check out recent remote branches you authored that aren\'t local yet'
57
+ "$CHE_PYTHON" "$CHE_BIN/git/checkout-my-branches.py" $argv
58
+ che_notify 7 $status
59
+ end
60
+
61
+ function clean-stale-branches --description 'Delete local branches whose upstream is gone'
62
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/git/clean-stale-branches.py" $argv
63
+ che_notify 4 $status
64
+ end
65
+
66
+ function clean-stale-branches-dr --description 'Delete local branches whose upstream is gone (preview only)'
67
+ env DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/git/clean-stale-branches.py" $argv
68
+ che_notify 7 $status
69
+ end
70
+
71
+ function prune-worktrees --description 'Remove every linked worktree, then prune stale admin records'
72
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/git/prune-worktrees.py" $argv
73
+ che_notify 4 $status
74
+ end
75
+
76
+ function prune-worktrees-dr --description 'Remove every linked worktree, then prune stale admin records (preview only)'
77
+ env DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/git/prune-worktrees.py" $argv
78
+ che_notify 7 $status
79
+ end
80
+
81
+ function sync-all-branches --description 'Push every worktree, collapse the pushed ones, then tidy branches'
82
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/git/sync-all-branches.py" $argv
83
+ che_notify 7 $status
84
+ end
85
+
86
+ function sync-all-branches-dr --description 'Push every worktree, collapse the pushed ones, then tidy branches (preview only)'
87
+ env DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/git/sync-all-branches.py" $argv
88
+ che_notify 7 $status
89
+ end
90
+
91
+ function update-from-origin --description 'Same as sync-all-branches but never pushes: fetch, collapse, rebase'
92
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/git/sync-all-branches.py" --no-push $argv
93
+ che_notify 7 $status
94
+ end
95
+
96
+ function update-local-branches --description 'Rebase every local branch that has an upstream onto origin'
97
+ "$CHE_PYTHON" "$CHE_BIN/git/update-local-branches.py" $argv
98
+ che_notify 5 $status
99
+ end
100
+
101
+ # --------------------------------------------------------------------------
102
+ # Video - Codecs, renaming, dedupe and metadata
103
+ # --------------------------------------------------------------------------
104
+
105
+ function show-codecs --description 'Report media files outside the Direct Play codec/container set'
106
+ "$CHE_PYTHON" "$CHE_BIN/video/show-codecs.py" $argv
107
+ che_notify 6 $status
108
+ end
109
+
110
+ function fix-codecs --description 'Re-encode media to h265/aac mp4 so it Direct Plays'
111
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/video/fix-codecs.py" $argv
112
+ che_notify 7 $status
113
+ end
114
+
115
+ function fix-codecs-dr --description 'Re-encode media to h265/aac mp4 so it Direct Plays (preview only)'
116
+ env DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/video/fix-codecs.py" $argv
117
+ che_notify 7 $status
118
+ end
119
+
120
+ function find-video-mkv-issues --description 'Scan MKV files and estimate Plex direct-play compatibility'
121
+ "$CHE_PYTHON" "$CHE_BIN/video/find-video-mkv-issues.py" $argv
122
+ che_notify 4 $status
123
+ end
124
+
125
+ function validate-video-files --description 'Check .mp4/.mkv files decode by playing one frame with mpv'
126
+ "$CHE_PYTHON" "$CHE_BIN/video/validate-video-files.py" $argv
127
+ che_notify 2 $status
128
+ end
129
+
130
+ function scan-videos-audio-language --description 'Print the audio language tags of every video under a path'
131
+ "$CHE_PYTHON" "$CHE_BIN/video/scan-videos-audio-language.py" $argv
132
+ che_notify 3 $status
133
+ end
134
+
135
+ function remove-metadata --description 'Strip all metadata from video files recursively'
136
+ "$CHE_PYTHON" "$CHE_BIN/video/remove-metadata.py" $argv
137
+ che_notify 4 $status
138
+ end
139
+
140
+ function rename-video-file --description 'Title-case video filenames (and optionally their folders)'
141
+ "$CHE_PYTHON" "$CHE_BIN/video/rename-video-file.py" $argv
142
+ che_notify 5 $status
143
+ end
144
+
145
+ function delete-duplicate-videos --description 'Delete duplicate MKV/MP4 files under a root directory'
146
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/video/delete-duplicate-videos.py" $argv
147
+ che_notify 6 $status
148
+ end
149
+
150
+ function delete-duplicate-videos-dr --description 'Delete duplicate MKV/MP4 files under a root directory (preview only)'
151
+ env DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/video/delete-duplicate-videos.py" $argv
152
+ che_notify 6 $status
153
+ end
154
+
155
+ function video-list --description 'List .mp4/.mkv files under a path with human-readable sizes'
156
+ "$CHE_PYTHON" "$CHE_BIN/video/video-list.py" $argv
157
+ che_notify 5 $status
158
+ end
159
+
160
+ function detect-green-magenta-videos --description 'Detect videos with the green/magenta chroma artifact'
161
+ "$CHE_PYTHON" "$CHE_BIN/video/detect-green-magenta-videos.py" $argv
162
+ che_notify 3 $status
163
+ end
164
+
165
+ function find-movie-by-year --description 'Find movie folders whose name ends with "(YYYY)"'
166
+ "$CHE_PYTHON" "$CHE_BIN/video/find-movie-by-year.py" $argv
167
+ che_notify 2 $status
168
+ end
169
+
170
+ function largest-tv-shows --description 'Rank TV show folders in a library by total size on disk'
171
+ "$CHE_PYTHON" "$CHE_BIN/video/largest-tv-shows.py" $argv
172
+ che_notify 2 $status
173
+ end
174
+
175
+ # --------------------------------------------------------------------------
176
+ # Files - Delete, compress and inspect by size or extension
177
+ # --------------------------------------------------------------------------
178
+
179
+ function delete-by-ext --description 'Delete files under a path matching a set of extensions'
180
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/files/delete-by-ext.py" $argv
181
+ che_notify 6 $status
182
+ end
183
+
184
+ function delete-by-ext-dr --description 'Delete files under a path matching a set of extensions (preview only)'
185
+ env DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/files/delete-by-ext.py" $argv
186
+ che_notify 7 $status
187
+ end
188
+
189
+ function delete-empty-folders --description 'Delete truly-empty directories under a path, cascading upwards'
190
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/files/delete-empty-folders.py" $argv
191
+ che_notify 6 $status
192
+ end
193
+
194
+ function delete-empty-folders-dr --description 'Delete truly-empty directories under a path, cascading upwards (preview only)'
195
+ env DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/files/delete-empty-folders.py" $argv
196
+ che_notify 7 $status
197
+ end
198
+
199
+ function delete-smb-files --description 'Delete .smbdelete* files left behind by an SMB share'
200
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/files/delete-smb-files.py" $argv
201
+ che_notify 6 $status
202
+ end
203
+
204
+ function delete-smb-files-dr --description 'Delete .smbdelete* files left behind by an SMB share (preview only)'
205
+ env DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/files/delete-smb-files.py" $argv
206
+ che_notify 7 $status
207
+ end
208
+
209
+ function files-under-size --description 'Find video files at or under a size threshold'
210
+ env DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/files/files-under-size.py" $argv
211
+ che_notify 4 $status
212
+ end
213
+
214
+ function files-under-size-dr --description 'Find video files at or under a size threshold (preview only)'
215
+ env DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/files/files-under-size.py" $argv
216
+ che_notify 7 $status
217
+ end
218
+
219
+ function find-largest-files --description 'List the largest files under a path, biggest first'
220
+ "$CHE_PYTHON" "$CHE_BIN/files/find-largest-files.py" $argv
221
+ che_notify 2 $status
222
+ end
223
+
224
+ function make-alpha-dir --description 'Create \'#\' and A-Z bucket folders under a parent directory'
225
+ "$CHE_PYTHON" "$CHE_BIN/files/make-alpha-dir.py" $argv
226
+ che_notify 4 $status
227
+ end
228
+
229
+ function compress-folders --description 'Zip every immediate subfolder of a path at max compression'
230
+ "$CHE_PYTHON" "$CHE_BIN/files/compress-folders.py" $argv
231
+ che_notify 7 $status
232
+ end
233
+
234
+ function list-permission --description 'Show ownership and mode of a volume under /Volumes'
235
+ command ls -ld "/Volumes/$argv[1]"
236
+ che_notify 7 $status
237
+ end
238
+
239
+ # --------------------------------------------------------------------------
240
+ # Drives - Mount, eject and keep-alive for the NAS volumes
241
+ # --------------------------------------------------------------------------
242
+
243
+ function mount-all-drives --description 'Mount all NAS drives over SMB via AppleScript'
244
+ "$CHE_PYTHON" "$CHE_BIN/drives/mount-all-drives.py" $argv
245
+ che_notify 7 $status
246
+ end
247
+
248
+ function eject-all-drives --description 'Eject all NAS volumes from /Volumes'
249
+ "$CHE_PYTHON" "$CHE_BIN/drives/eject-all-drives.py" $argv
250
+ che_notify 7 $status
251
+ end
252
+
253
+ function eject-all-drives-dr --description 'Eject all NAS volumes from /Volumes (preview only)'
254
+ "$CHE_PYTHON" "$CHE_BIN/drives/eject-all-drives.py" --dry-run $argv
255
+ che_notify 7 $status
256
+ end
257
+
258
+ function ping-nas --description 'Keep-alive pinger that remounts a NAS drive that dropped off'
259
+ "$CHE_PYTHON" "$CHE_BIN/drives/ping-nas.py" $argv
260
+ end
261
+
262
+ # --------------------------------------------------------------------------
263
+ # System - Homebrew, monitoring and the machine itself
264
+ # --------------------------------------------------------------------------
265
+
266
+ function update-brew --description 'Update Homebrew, upgrade formulae and casks, then clean up'
267
+ "$CHE_PYTHON" "$CHE_BIN/system/update-brew.py" $argv
268
+ che_notify 7 $status
269
+ end
270
+
271
+ function update-brew-dr --description 'Update Homebrew, upgrade formulae and casks, then clean up (preview only)'
272
+ "$CHE_PYTHON" "$CHE_BIN/system/update-brew.py" --dry-run $argv
273
+ che_notify 7 $status
274
+ end
275
+
276
+ function btop --description 'Launch btop with a gruvbox theme matching the macOS appearance'
277
+ "$CHE_PYTHON" "$CHE_BIN/system/btop-launch.py" $argv
278
+ end
279
+
280
+ if test -r "$CHE_HOME/shell/completions/che.fish"
281
+ source "$CHE_HOME/shell/completions/che.fish"
282
+ end
package/shell/che.zsh ADDED
@@ -0,0 +1,389 @@
1
+ # che 1.0.0 - generated file, do not edit.
2
+ #
3
+ # Generated from bin/commands.py by `che install`.
4
+ # To change a wrapper, edit that manifest and run `che install` again.
5
+ #
6
+ # Source this file (or let the `che install` block in your rc file do it):
7
+ #
8
+ # . ~/Developer/git/che/shell/che.zsh
9
+ #
10
+ # Every wrapper is a shell function, so `type <name>` shows what it runs and
11
+ # `che doctor` can tell you when something it needs is missing.
12
+
13
+ # CHE_HOME is normally exported by the installed rc block. Fall back to this
14
+ # file's own location so a bare `source` of it also works.
15
+ if [ -z "${CHE_HOME:-}" ]; then
16
+ CHE_HOME="${${(%):-%x}:A:h:h}"
17
+ fi
18
+
19
+ # Exported, not just set: every wrapper expands "$CHE_BIN", and a shell that
20
+ # replays the *functions* without re-reading this file - a `typeset -f` dump, an
21
+ # environment snapshot, any tool that rebuilds a shell from its exported
22
+ # environment - keeps the functions and drops a plain variable, leaving the
23
+ # wrappers to run "/che.py". CHE_HOME comes in exported by the rc block; this
24
+ # keeps everything derived from it exported too.
25
+ export CHE_BIN="${CHE_HOME}/bin"
26
+
27
+ # Any python3 on PATH by default. `che install` records the interpreter it
28
+ # verified (3.12+) in the rc block, which wins over this.
29
+ : "${CHE_PYTHON:=python3}"
30
+ export CHE_PYTHON
31
+
32
+ # Legacy name from the hand-maintained wrappers, kept so an older snippet in
33
+ # ~/.zshrc that still refers to it keeps resolving.
34
+ export SHELL_FUNCTIONS_BIN="${CHE_BIN}"
35
+
36
+ # Play the completion sound for a wrapper, then return the status it was given.
37
+ #
38
+ # playsound-N is defined outside this repo. Every wrapper used to call it
39
+ # unconditionally, which printed "command not found" on a machine that never
40
+ # had it; here a missing playsound is simply skipped. Set CHE_SOUNDS=0 to
41
+ # silence them all.
42
+ che_notify() {
43
+ if [ "${CHE_SOUNDS:-1}" = "1" ] && command -v "playsound-$1" >/dev/null 2>&1; then
44
+ "playsound-$1" >/dev/null 2>&1
45
+ fi
46
+ return "${2:-0}"
47
+ }
48
+
49
+ # The dispatcher. `che` with no arguments opens the interactive menu.
50
+ che() {
51
+ if [ "$#" -eq 0 ]; then
52
+ "$CHE_PYTHON" "$CHE_BIN/che.py"
53
+ else
54
+ "$CHE_PYTHON" "$CHE_BIN/che.py" "$@"
55
+ che_notify 7 $?
56
+ fi
57
+ }
58
+
59
+ # An alias sharing a name with a wrapper below is fatal, not cosmetic: zsh
60
+ # expands aliases while *parsing*, so `remove-metadata() {` becomes
61
+ # `find ./ -type f …() {` and the parse error aborts the rest of this file -
62
+ # taking every wrapper after it with it.
63
+ #
64
+ # So stash any colliding alias, define the functions, then put it back.
65
+ # Clearing it outright would silently replace a command the user aliased on
66
+ # purpose; restoring it keeps their alias winning at the prompt, exactly as it
67
+ # did before che was installed. `che doctor` reports the collision, and
68
+ # `\name` (backslash) reaches the wrapper without expanding the alias.
69
+ typeset -ga _che_stashed_aliases=()
70
+
71
+ _che_stash_aliases() {
72
+ local name
73
+ for name in "$@"; do
74
+ if [[ -n ${aliases[$name]-} ]]; then
75
+ _che_stashed_aliases+=("${name}=${aliases[$name]}")
76
+ unalias -- "$name"
77
+ fi
78
+ done
79
+ }
80
+
81
+ _che_restore_aliases() {
82
+ local entry
83
+ for entry in "${_che_stashed_aliases[@]}"; do
84
+ alias -- "$entry"
85
+ done
86
+ _che_stashed_aliases=()
87
+ }
88
+
89
+ _che_stash_aliases che all-actions all-actions-watch checkout-my-branches \
90
+ clean-stale-branches clean-stale-branches-dr prune-worktrees \
91
+ prune-worktrees-dr sync-all-branches sync-all-branches-dr \
92
+ update-from-origin update-local-branches show-codecs fix-codecs \
93
+ fix-codecs-dr find-video-mkv-issues validate-video-files \
94
+ scan-videos-audio-language remove-metadata rename-video-file \
95
+ delete-duplicate-videos delete-duplicate-videos-dr video-list \
96
+ detect-green-magenta-videos find-movie-by-year largest-tv-shows \
97
+ delete-by-ext delete-by-ext-dr delete-empty-folders \
98
+ delete-empty-folders-dr delete-smb-files delete-smb-files-dr \
99
+ files-under-size files-under-size-dr find-largest-files make-alpha-dir \
100
+ compress-folders list-permission mount-all-drives eject-all-drives \
101
+ eject-all-drives-dr ping-nas update-brew update-brew-dr btop
102
+
103
+ # --------------------------------------------------------------------------
104
+ # Git - Branch hygiene, worktrees and GitHub Actions
105
+ # --------------------------------------------------------------------------
106
+
107
+ # GitHub Actions status for every open PR you authored
108
+ all-actions() {
109
+ "$CHE_PYTHON" "$CHE_BIN/git/all-actions.py" "$@"
110
+ }
111
+
112
+ # Same as all-actions, refreshed on an interval
113
+ all-actions-watch() {
114
+ "$CHE_PYTHON" "$CHE_BIN/git/all-actions.py" --watch "$@"
115
+ }
116
+
117
+ # Check out recent remote branches you authored that aren't local yet
118
+ checkout-my-branches() {
119
+ "$CHE_PYTHON" "$CHE_BIN/git/checkout-my-branches.py" "$@"
120
+ che_notify 7 $?
121
+ }
122
+
123
+ # Delete local branches whose upstream is gone
124
+ clean-stale-branches() {
125
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/git/clean-stale-branches.py" "$@"
126
+ che_notify 4 $?
127
+ }
128
+
129
+ # Delete local branches whose upstream is gone (preview only)
130
+ clean-stale-branches-dr() {
131
+ DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/git/clean-stale-branches.py" "$@"
132
+ che_notify 7 $?
133
+ }
134
+
135
+ # Remove every linked worktree, then prune stale admin records
136
+ prune-worktrees() {
137
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/git/prune-worktrees.py" "$@"
138
+ che_notify 4 $?
139
+ }
140
+
141
+ # Remove every linked worktree, then prune stale admin records (preview only)
142
+ prune-worktrees-dr() {
143
+ DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/git/prune-worktrees.py" "$@"
144
+ che_notify 7 $?
145
+ }
146
+
147
+ # Push every worktree, collapse the pushed ones, then tidy branches
148
+ sync-all-branches() {
149
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/git/sync-all-branches.py" "$@"
150
+ che_notify 7 $?
151
+ }
152
+
153
+ # Push every worktree, collapse the pushed ones, then tidy branches (preview only)
154
+ sync-all-branches-dr() {
155
+ DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/git/sync-all-branches.py" "$@"
156
+ che_notify 7 $?
157
+ }
158
+
159
+ # Same as sync-all-branches but never pushes: fetch, collapse, rebase
160
+ update-from-origin() {
161
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/git/sync-all-branches.py" --no-push "$@"
162
+ che_notify 7 $?
163
+ }
164
+
165
+ # Rebase every local branch that has an upstream onto origin
166
+ update-local-branches() {
167
+ "$CHE_PYTHON" "$CHE_BIN/git/update-local-branches.py" "$@"
168
+ che_notify 5 $?
169
+ }
170
+
171
+ # --------------------------------------------------------------------------
172
+ # Video - Codecs, renaming, dedupe and metadata
173
+ # --------------------------------------------------------------------------
174
+
175
+ # Report media files outside the Direct Play codec/container set
176
+ show-codecs() {
177
+ "$CHE_PYTHON" "$CHE_BIN/video/show-codecs.py" "$@"
178
+ che_notify 6 $?
179
+ }
180
+
181
+ # Re-encode media to h265/aac mp4 so it Direct Plays
182
+ fix-codecs() {
183
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/video/fix-codecs.py" "$@"
184
+ che_notify 7 $?
185
+ }
186
+
187
+ # Re-encode media to h265/aac mp4 so it Direct Plays (preview only)
188
+ fix-codecs-dr() {
189
+ DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/video/fix-codecs.py" "$@"
190
+ che_notify 7 $?
191
+ }
192
+
193
+ # Scan MKV files and estimate Plex direct-play compatibility
194
+ find-video-mkv-issues() {
195
+ "$CHE_PYTHON" "$CHE_BIN/video/find-video-mkv-issues.py" "$@"
196
+ che_notify 4 $?
197
+ }
198
+
199
+ # Check .mp4/.mkv files decode by playing one frame with mpv
200
+ validate-video-files() {
201
+ "$CHE_PYTHON" "$CHE_BIN/video/validate-video-files.py" "$@"
202
+ che_notify 2 $?
203
+ }
204
+
205
+ # Print the audio language tags of every video under a path
206
+ scan-videos-audio-language() {
207
+ "$CHE_PYTHON" "$CHE_BIN/video/scan-videos-audio-language.py" "$@"
208
+ che_notify 3 $?
209
+ }
210
+
211
+ # Strip all metadata from video files recursively
212
+ remove-metadata() {
213
+ "$CHE_PYTHON" "$CHE_BIN/video/remove-metadata.py" "$@"
214
+ che_notify 4 $?
215
+ }
216
+
217
+ # Title-case video filenames (and optionally their folders)
218
+ rename-video-file() {
219
+ "$CHE_PYTHON" "$CHE_BIN/video/rename-video-file.py" "$@"
220
+ che_notify 5 $?
221
+ }
222
+
223
+ # Delete duplicate MKV/MP4 files under a root directory
224
+ delete-duplicate-videos() {
225
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/video/delete-duplicate-videos.py" "$@"
226
+ che_notify 6 $?
227
+ }
228
+
229
+ # Delete duplicate MKV/MP4 files under a root directory (preview only)
230
+ delete-duplicate-videos-dr() {
231
+ DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/video/delete-duplicate-videos.py" "$@"
232
+ che_notify 6 $?
233
+ }
234
+
235
+ # List .mp4/.mkv files under a path with human-readable sizes
236
+ video-list() {
237
+ "$CHE_PYTHON" "$CHE_BIN/video/video-list.py" "$@"
238
+ che_notify 5 $?
239
+ }
240
+
241
+ # Detect videos with the green/magenta chroma artifact
242
+ detect-green-magenta-videos() {
243
+ "$CHE_PYTHON" "$CHE_BIN/video/detect-green-magenta-videos.py" "$@"
244
+ che_notify 3 $?
245
+ }
246
+
247
+ # Find movie folders whose name ends with "(YYYY)"
248
+ find-movie-by-year() {
249
+ "$CHE_PYTHON" "$CHE_BIN/video/find-movie-by-year.py" "$@"
250
+ che_notify 2 $?
251
+ }
252
+
253
+ # Rank TV show folders in a library by total size on disk
254
+ largest-tv-shows() {
255
+ "$CHE_PYTHON" "$CHE_BIN/video/largest-tv-shows.py" "$@"
256
+ che_notify 2 $?
257
+ }
258
+
259
+ # --------------------------------------------------------------------------
260
+ # Files - Delete, compress and inspect by size or extension
261
+ # --------------------------------------------------------------------------
262
+
263
+ # Delete files under a path matching a set of extensions
264
+ delete-by-ext() {
265
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/files/delete-by-ext.py" "$@"
266
+ che_notify 6 $?
267
+ }
268
+
269
+ # Delete files under a path matching a set of extensions (preview only)
270
+ delete-by-ext-dr() {
271
+ DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/files/delete-by-ext.py" "$@"
272
+ che_notify 7 $?
273
+ }
274
+
275
+ # Delete truly-empty directories under a path, cascading upwards
276
+ delete-empty-folders() {
277
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/files/delete-empty-folders.py" "$@"
278
+ che_notify 6 $?
279
+ }
280
+
281
+ # Delete truly-empty directories under a path, cascading upwards (preview only)
282
+ delete-empty-folders-dr() {
283
+ DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/files/delete-empty-folders.py" "$@"
284
+ che_notify 7 $?
285
+ }
286
+
287
+ # Delete .smbdelete* files left behind by an SMB share
288
+ delete-smb-files() {
289
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/files/delete-smb-files.py" "$@"
290
+ che_notify 6 $?
291
+ }
292
+
293
+ # Delete .smbdelete* files left behind by an SMB share (preview only)
294
+ delete-smb-files-dr() {
295
+ DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/files/delete-smb-files.py" "$@"
296
+ che_notify 7 $?
297
+ }
298
+
299
+ # Find video files at or under a size threshold
300
+ files-under-size() {
301
+ DRY_RUN=false "$CHE_PYTHON" "$CHE_BIN/files/files-under-size.py" "$@"
302
+ che_notify 4 $?
303
+ }
304
+
305
+ # Find video files at or under a size threshold (preview only)
306
+ files-under-size-dr() {
307
+ DRY_RUN=true "$CHE_PYTHON" "$CHE_BIN/files/files-under-size.py" "$@"
308
+ che_notify 7 $?
309
+ }
310
+
311
+ # List the largest files under a path, biggest first
312
+ find-largest-files() {
313
+ "$CHE_PYTHON" "$CHE_BIN/files/find-largest-files.py" "$@"
314
+ che_notify 2 $?
315
+ }
316
+
317
+ # Create '#' and A-Z bucket folders under a parent directory
318
+ make-alpha-dir() {
319
+ "$CHE_PYTHON" "$CHE_BIN/files/make-alpha-dir.py" "$@"
320
+ che_notify 4 $?
321
+ }
322
+
323
+ # Zip every immediate subfolder of a path at max compression
324
+ compress-folders() {
325
+ "$CHE_PYTHON" "$CHE_BIN/files/compress-folders.py" "$@"
326
+ che_notify 7 $?
327
+ }
328
+
329
+ # Show ownership and mode of a volume under /Volumes
330
+ list-permission() {
331
+ command ls -ld "/Volumes/$1"
332
+ che_notify 7 $?
333
+ }
334
+
335
+ # --------------------------------------------------------------------------
336
+ # Drives - Mount, eject and keep-alive for the NAS volumes
337
+ # --------------------------------------------------------------------------
338
+
339
+ # Mount all NAS drives over SMB via AppleScript
340
+ mount-all-drives() {
341
+ "$CHE_PYTHON" "$CHE_BIN/drives/mount-all-drives.py" "$@"
342
+ che_notify 7 $?
343
+ }
344
+
345
+ # Eject all NAS volumes from /Volumes
346
+ eject-all-drives() {
347
+ "$CHE_PYTHON" "$CHE_BIN/drives/eject-all-drives.py" "$@"
348
+ che_notify 7 $?
349
+ }
350
+
351
+ # Eject all NAS volumes from /Volumes (preview only)
352
+ eject-all-drives-dr() {
353
+ "$CHE_PYTHON" "$CHE_BIN/drives/eject-all-drives.py" --dry-run "$@"
354
+ che_notify 7 $?
355
+ }
356
+
357
+ # Keep-alive pinger that remounts a NAS drive that dropped off
358
+ ping-nas() {
359
+ "$CHE_PYTHON" "$CHE_BIN/drives/ping-nas.py" "$@"
360
+ }
361
+
362
+ # --------------------------------------------------------------------------
363
+ # System - Homebrew, monitoring and the machine itself
364
+ # --------------------------------------------------------------------------
365
+
366
+ # Update Homebrew, upgrade formulae and casks, then clean up
367
+ update-brew() {
368
+ "$CHE_PYTHON" "$CHE_BIN/system/update-brew.py" "$@"
369
+ che_notify 7 $?
370
+ }
371
+
372
+ # Update Homebrew, upgrade formulae and casks, then clean up (preview only)
373
+ update-brew-dr() {
374
+ "$CHE_PYTHON" "$CHE_BIN/system/update-brew.py" --dry-run "$@"
375
+ che_notify 7 $?
376
+ }
377
+
378
+ # Launch btop with a gruvbox theme matching the macOS appearance
379
+ btop() {
380
+ "$CHE_PYTHON" "$CHE_BIN/system/btop-launch.py" "$@"
381
+ }
382
+
383
+ _che_restore_aliases
384
+
385
+ # Completions. compdef only exists once compinit has run; when it has not,
386
+ # skip quietly rather than erroring at shell start.
387
+ if whence -w compdef >/dev/null 2>&1 && [ -r "${CHE_HOME}/shell/completions/_che" ]; then
388
+ . "${CHE_HOME}/shell/completions/_che"
389
+ fi