diff options
| -rw-r--r-- | modules/music-config.el | 69 | ||||
| -rw-r--r-- | todo.org | 30 |
2 files changed, 81 insertions, 18 deletions
diff --git a/modules/music-config.el b/modules/music-config.el index 902fbd9c..2e9b3252 100644 --- a/modules/music-config.el +++ b/modules/music-config.el @@ -44,14 +44,16 @@ (defun cj/music--valid-file-p (file) "Return non-nil if FILE has an accepted music extension (case-insensitive)." - (when-let ((ext (file-name-extension file))) - (member (downcase ext) cj/music-file-extensions))) + (when (and file (stringp file)) + (when-let ((ext (file-name-extension file))) + (member (downcase ext) cj/music-file-extensions)))) (defun cj/music--valid-directory-p (dir) "Return non-nil if DIR is a non-hidden directory." - (and (file-directory-p dir) - (not (string-prefix-p "." (file-name-nondirectory - (directory-file-name dir)))))) + (when (and dir (stringp dir) (not (string-empty-p dir))) + (and (file-directory-p dir) + (not (string-prefix-p "." (file-name-nondirectory + (directory-file-name dir))))))) (defun cj/music--collect-entries-recursive (root) "Return sorted relative paths of all subdirs and music files under ROOT. @@ -105,7 +107,7 @@ Directories are suffixed with /; files are plain. Hidden dirs/files skipped." (let ((line (string-trim (match-string 0)))) (unless (string-empty-p line) (push (if (or (file-name-absolute-p line) - (string-match-p "\`\(https?\|mms\)://" line)) + (string-match-p "\\`\\(https?\\|mms\\)://" line)) line (expand-file-name line dir)) tracks)))) @@ -189,6 +191,60 @@ Directories (trailing /) are added recursively; files added singly." ;;; Commands: playlist management (load/save/clear/reload/edit) +(defun cj/music--append-track-to-m3u-file (track-path m3u-file) + "Append TRACK-PATH to M3U-FILE. Signals error on failure. +Pure function for testing - no user interaction. +TRACK-PATH should be an absolute path. +M3U-FILE should be an existing, writable M3U file path." + (unless (file-exists-p m3u-file) + (error "M3U file does not exist: %s" m3u-file)) + (unless (file-writable-p m3u-file) + (error "M3U file is not writable: %s" m3u-file)) + + ;; Determine if we need a leading newline + (let ((needs-prefix-newline nil) + (file-size (file-attribute-size (file-attributes m3u-file)))) + (when (> file-size 0) + ;; Read the last character of the file to check if it ends with newline + (with-temp-buffer + (insert-file-contents m3u-file nil (max 0 (1- file-size)) file-size) + (setq needs-prefix-newline (not (= (char-after (point-min)) ?\n))))) + + ;; Append the track with proper newline handling + (with-temp-buffer + (when needs-prefix-newline + (insert "\n")) + (insert track-path "\n") + (write-region (point-min) (point-max) m3u-file t 0))) + t) + + +(defun cj/music-append-track-to-playlist () + "Append track at point to a selected M3U playlist file. +Prompts for M3U file selection with completion. Allows cancellation." + (interactive) + (unless (derived-mode-p 'emms-playlist-mode) + (user-error "This command must be run in the EMMS playlist buffer")) + (let ((track (emms-playlist-track-at (point)))) + (unless track + (user-error "No track at point")) + (let* ((track-path (emms-track-name track)) + (m3u-files (cj/music--get-m3u-files))) + (when (null m3u-files) + (user-error "No M3U files found in %s" cj/music-m3u-root)) + (let* ((choices (append (mapcar #'car m3u-files) '("(Cancel)"))) + (choice (completing-read "Append track to playlist: " choices nil t))) + (if (string= choice "(Cancel)") + (message "Cancelled") + (let ((m3u-file (cdr (assoc choice m3u-files)))) + (condition-case err + (progn + (cj/music--append-track-to-m3u-file track-path m3u-file) + (message "Added '%s' to %s" + (file-name-nondirectory track-path) + choice)) + (error (message "Failed to append track: %s" (error-message-string err)))))))))) + (defun cj/music-playlist-load () "Load an M3U playlist from cj/music-m3u-root. @@ -441,6 +497,7 @@ Dirs added recursively." ("q" . emms-playlist-mode-bury-buffer) ("a" . cj/music-fuzzy-select-and-add) ;; Manipulation + ("A" . cj/music-append-track-to-playlist) ("C" . cj/music-playlist-clear) ("L" . cj/music-playlist-load) ("E" . cj/music-playlist-edit) @@ -18,7 +18,6 @@ V2MOM is located at: [[file:docs/emacs-config-v2mom.org][emacs-config-v2mom.org] Research/ideas that don't serve vision: [[file:docs/someday-maybe.org][someday-maybe.org]] * Method 1: Make Using Emacs Frictionless - ** DONE [#A] Remove network check from startup (saves 1+ seconds) CLOSED: [2025-10-31 Fri] @@ -45,21 +44,16 @@ Updated README.org with Development/Testing section All integration tests pass (5/5) ✅ -** TODO [#B] Fix go-ts-mode-map keybinding error (void-variable) - -Error: "Debugger entered--Lisp error: (void-variable go-ts-mode-map)" -Location: modules/prog-go.el - trying to bind keys before mode loads. -Fix: Wrap keybinding in with-eval-after-load or check if variable is bound. - ** DONE [#A] Implement cj/diff-buffer-with-file (compare buffer with saved version) CLOSED: [2025-10-31 Fri] Bound to C-; b D. Weekly need satisfied. -** TODO [#B] Optimize org-agenda performance using built-in profiler +** TODO [#B] Fix go-ts-mode-map keybinding error (void-variable) -THE BOTTLENECK. Currently 30+ seconds, target < 5 seconds. -Use M-x profiler-start before Method 3 debug-profiling.el is built. +Error: "Debugger entered--Lisp error: (void-variable go-ts-mode-map)" +Location: modules/prog-go.el - trying to bind keys before mode loads. +Fix: Wrap keybinding in with-eval-after-load or check if variable is bound. ** TODO [#B] Fix org-noter (reading/annotation workflow currently "so painful") @@ -73,15 +67,20 @@ Use constantly, needs to work reliably. Daily workflow improvement. -** TODO [#C] Fix grammar checker performance (currently disabled) +** TODO [#B] Optimize org-agenda performance using built-in profiler -Currently disabled because it breaks flow when writing. +THE BOTTLENECK. Currently 30+ seconds, target < 5 seconds. +Use M-x profiler-start before Method 3 debug-profiling.el is built. ** TODO [#B] Optimize org-capture target building performance 15-20 seconds every time capturing a task (12+ times/day). Major daily bottleneck - minutes lost waiting, plus context switching cost. +** TODO [#C] Fix grammar checker performance (currently disabled) + +Currently disabled because it breaks flow when writing. + ** TODO [#D] Fix EMMS keybinding inconsistency with other buffers EMMS keybindings conflict with standard buffer keybindings, causing mistypes. @@ -185,3 +184,10 @@ Review this inbox, cancel stale items, keep < 20 active. Track in calendar. Can't research next thing until current thing is implemented. * Emacs Config Inbox +** TODO [#A] Irritant: Print should be keybound to capital P +and perhaps print-screen (however Emacs references that key) + +lowercase p should be about getting path, not doing something dramatic like sending a document to the LPR without confirmation + +** TODO [#A] Irritant: send buffer to printer must have confirmation +it should allow to skip the confirmation with C-u. |
