diff options
| author | Craig Jennings <c@cjennings.net> | 2025-11-01 18:44:55 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-11-01 18:44:55 -0500 |
| commit | 8be2753f052fa8e3e66dd9d82c69b569147b568f (patch) | |
| tree | 60290928ed6121d62e2f4c99bd4e82d66cf20e83 /modules | |
| parent | 0c66466d8d559ccb7dad6457988f6ecef894227e (diff) | |
chore: Update todo.org with new inbox items and music-config improvements
todo.org updates:
- Added 2 new inbox items about printing keybindings
- Reordered Method 1 tasks by priority
- Removed blank lines (linter formatting)
music-config.el improvements:
- Added validation checks to cj/music--valid-file-p and cj/music--valid-directory-p
- Fixed regex escaping in cj/music--m3u-file-tracks
- Added new cj/music--append-track-to-m3u-file function for testing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'modules')
| -rw-r--r-- | modules/music-config.el | 69 |
1 files changed, 63 insertions, 6 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) |
