From d87cd1c3112c9e916fc71fd41721726709cae7c0 Mon Sep 17 00:00:00 2001 From: Ye Wenbin Date: Wed, 6 Dec 2006 15:35:00 +0000 Subject: emms-mp3tag support ogg, add more documentation. fix some error darcs-hash:20061206153528-94065-1a7c6b96e2ae32e1f02b97bc884be6540021f21f.gz --- emms-history.el | 33 ++++---- emms-i18n.el | 22 +++++- emms-info-ogginfo.el | 2 +- emms-lyrics.el | 4 +- emms-mark.el | 12 ++- emms-mp3tag.el | 218 ++++++++++++++++++++++++++++++++------------------- 6 files changed, 187 insertions(+), 104 deletions(-) diff --git a/emms-history.el b/emms-history.el index 3c1720a..cd74681 100644 --- a/emms-history.el +++ b/emms-history.el @@ -23,8 +23,7 @@ ;;; Commentary: ;; Save playlists when exit emacs. -;; Next time use M-x emms-playlist-load-saved-list to load saved -;; playlist +;; Next time use M-x emms-history-load to load saved playlist ;; Put this file into your load-path and the following into your ~/.emacs: ;; (require 'emms-history) @@ -36,10 +35,14 @@ (eval-when-compile (require 'cl)) -(defvar emms-history-saved-list-file "~/.emacs.d/.emms-history") +(defvar emms-history-file "~/.emacs.d/.emms-history" + "File to save playlists") -(defun emms-history-save-on-exit () - (when (stringp emms-history-saved-list-file) +(defun emms-history-save () + "Save all playlists that open in this emacs session when exit. Use +`emms-history-load' to load saved playlists." + (interactive) + (when (stringp emms-history-file) (let ((oldbuf emms-playlist-buffer) emms-playlist-buffer playlists) (save-excursion @@ -71,17 +74,17 @@ (emms-repeat-playlist . ,emms-repeat-playlist)) (current-buffer)) (insert "\n)") - (write-file emms-history-saved-list-file)))))) + (write-file emms-history-file)))))) -(add-hook 'kill-emacs-hook 'emms-history-save-on-exit) +(add-hook 'kill-emacs-hook 'emms-history-save) -(defun emms-history-load-saved-list () +(defun emms-history-load () (interactive) - (when (and (stringp emms-history-saved-list-file) - (file-exists-p emms-history-saved-list-file)) + (when (and (stringp emms-history-file) + (file-exists-p emms-history-file)) (let (history buf) (with-temp-buffer - (insert-file-contents emms-history-saved-list-file) + (insert-file-contents emms-history-file) (setq history (read (current-buffer))) (dolist (playlist (cadr history)) (with-current-buffer (emms-playlist-new (car playlist)) @@ -90,12 +93,12 @@ (setq buf (current-buffer))) (mapc 'emms-playlist-insert-track (nth 2 playlist)) - (condition-case nil - (emms-playlist-select (cadr playlist)) - (error nil)))) + (ignore-errors + (emms-playlist-select (cadr playlist))))) (setq emms-playlist-buffer buf) (dolist (method (nth 2 history)) (set (car method) (cdr method))) - (emms-start))))) + (ignore-errors + (emms-start)))))) ;;; emms-history.el ends here diff --git a/emms-i18n.el b/emms-i18n.el index 6aa9f6c..26ccbb4 100644 --- a/emms-i18n.el +++ b/emms-i18n.el @@ -1,4 +1,4 @@ -;;; emms-i18n.el --- +;;; emms-i18n.el --- Function for handling coding system ;; Copyright 2006 Ye Wenbin ;; @@ -63,7 +63,7 @@ (defun emms-iconv-buffer (from to &optional buf) (save-excursion (and buf (set-buffer buf)) - (emms-iconv-region-1 (point-min) (point-max) from to))) + (emms-iconv-region (point-min) (point-max) from to))) (defun emms-set-default-coding-system (read-coding write-coding) "Set `emms-default-coding-system'" @@ -81,17 +81,31 @@ (cdr default-process-coding-system)))))) (defun emms-call-process-to-string (program &rest args) + "This function run program and return the program result. If the CAR +part of `emms-default-coding-system' is non-nil, the program result will +be decode use the CAR part of emms-default-coding-system. Otherwise, +use `emms-coding-dectect-functions' to detect the coding system of the +result. If the emms-coding-dectect-functions failed, use +`emms-detect-coding-function' to detect coding system. If all the +coding system is nil or in `emms-nerver-used-coding-system', decode +the result using `emms-coding-system-for-read'. + +The result arguments ARGS is a list of string which pass to `call-process'." (with-temp-buffer (let ((default-process-coding-system (copy-tree default-process-coding-system)) (process-coding-system-alist nil) exit) (setcar default-process-coding-system (car emms-default-coding-system)) (setq exit (apply 'call-process (append (list program nil t nil) args))) (when (and (zerop exit) (null (car emms-default-coding-system))) - (setq coding (emms-detect-buffer-coding-system)) - (decode-coding-region (point-min) (point-max) coding)) + (decode-coding-region (point-min) (point-max) (emms-detect-buffer-coding-system))) (and (zerop exit) (buffer-string))))) +;; Is this function useful? (defun emms-call-process (program &rest args) + "Run the program like `call-process'. If +the cdr part `emms-default-coding-system' is non-nil, the string in +ARGS will be encode by the CDR part of `emms-default-coding-system', +otherwise, it is pass all parameter to `call-process'." (with-temp-buffer (if (cdr emms-default-coding-system) (let ((default-process-coding-system emms-default-coding-system) diff --git a/emms-info-ogginfo.el b/emms-info-ogginfo.el index 9de34a9..d8ed2c7 100644 --- a/emms-info-ogginfo.el +++ b/emms-info-ogginfo.el @@ -71,7 +71,7 @@ This is a useful element for `emms-info-functions'." (goto-char (point-min)) (when (re-search-forward "^.*\\.\\.\\.$" (point-max) t) (while (zerop (forward-line 1)) - (when (looking-at "^\t\\(.*\\)=\\(.*\\)$") + (when (looking-at "^\t\\(.*?\\)=\\(.*\\)$") ; recognize the first '=' (let ((a (match-string 1)) (b (match-string 2))) (when (and (< 0 (length a)) diff --git a/emms-lyrics.el b/emms-lyrics.el index 54a7417..8013c9d 100644 --- a/emms-lyrics.el +++ b/emms-lyrics.el @@ -131,7 +131,9 @@ FILE should be under the same directory as the music file, or under 'type)) (unless (file-exists-p file) (setq file (emms-lyrics-find-lyric file))) - (setq file (funcall emms-lyrics-find-lyric-function file))) + (setq file + (and (functionp emms-lyrics-find-lyric-function) + (funcall emms-lyrics-find-lyric-function file)))) (when (and file (not (string= file "")) (file-exists-p file)) (with-temp-buffer (let ((coding-system-for-read emms-lyrics-coding-system)) diff --git a/emms-mark.el b/emms-mark.el index d4d917e..9d71cef 100644 --- a/emms-mark.el +++ b/emms-mark.el @@ -152,6 +152,7 @@ description function.") (forward-line 1))))) (defsubst emms-mark-has-markedp () + "Predicate whether the playlist has marked line" (save-excursion (goto-char (point-min)) (re-search-forward (format "^[%c]" emms-mark-char) nil t))) @@ -160,7 +161,13 @@ description function.") ;;{{{ functions to operate marked tracks (defun emms-mark-do-with-marked-track (func &optional move) - "If your function don't move forward, set move to non-nil." + "Call FUNC on every marked line in current playlist. The FUNC take +no argument, so if need the track in marked line, should use +`emms-playlist-track-at' to get it. The FUNC can also modify the +playlist buffer, such as delete the line. It is import for who want to +use this function, this function didn't move forward. So if FUNC don't +move forward, set the third parameter MOVE to non-nil. Otherwise, the +function will never exit the loop." (let ((regexp (format "^[%c]" emms-mark-char)) (newfunc func)) (if move @@ -172,6 +179,9 @@ description function.") (funcall newfunc))))) (defun emms-mark-mapcar-marked-track (func &optional move) + "This function does the same thing as +`emms-mark-do-with-marked-track', the only difference is this function +collect the result of FUNC." (let ((regexp (format "^[%c]" emms-mark-char)) result (newfunc func)) (if move diff --git a/emms-mp3tag.el b/emms-mp3tag.el index 3e3fd98..1e8be5f 100644 --- a/emms-mp3tag.el +++ b/emms-mp3tag.el @@ -1,4 +1,4 @@ -;;; emms-mp3tag.el --- +;;; emms-mp3tag.el --- Edit track tags. ;; Copyright 2006 Ye Wenbin ;; @@ -45,29 +45,33 @@ (info-tracknumber . "n") (info-year . "y") (info-genre . "g") - (info-note . "c"))) + (info-date . "d") + (info-note . "c")) +"A list to setup format.") (defvar emms-mp3tag-edit-buffer "*EMMS-TAGS*" "Buffer name to edit mp3 tags") (defvar emms-mp3tag-log-buffer "*EMMS-LOG*" "Buffer name of mp3tag edit log") -(defvar emms-mp3tag-format - `(("default" - ,(format "%%m\n%s\n\n" - (mapconcat - (lambda (tag) - (concat (propertize (format "%-16s = " (symbol-name (car tag))) - 'read-only t 'rear-nonsticky t) - "%" (cdr tag))) - (append '((name . "f")) emms-mp3tag-tags) "\n")) - emms-mp3tag-default-parser)) - "The contents of this variable should look like: - ((NAME FORMATER PARSER) ...) - -The NAME is use to select proper format, and FORMATER should be a -string or a function. When it is a string, it can use specification -as: +(defun emms-mp3tag-make-format (tags) + (format "%%m\n%-16s = %%f\n%s\n\n" "name" + (mapconcat + (lambda (tag) + (concat (propertize (format "%-16s = " (symbol-name tag)) + 'read-only t 'rear-nonsticky t 'face 'bold) + "%" (cdr (assoc tag emms-mp3tag-tags)))) + tags "\n"))) + +(defvar emms-mp3tag-formats + (let* ((tags (mapcar 'car emms-mp3tag-tags)) + (default (emms-mp3tag-make-format (remove 'info-date tags)))) + `(("mp3" . ,default) + ("ogg" . ,(emms-mp3tag-make-format (remove 'info-year tags))) + ("default" . ,default))) + "Format to insert the track. The CAR part is the extension of the +track name, and the CDR part is the format template. The format +specification is like: m -- Track description f -- Track name a -- Track info-artist @@ -78,27 +82,79 @@ as: g -- Track info-genre c -- Track info-note -When it is a function, it recept a parameter, the track, and should -return a string that to insert to `emms-mp3tag-edit-buffer'. +You can add new specification in `emms-mp3tag-tags' and use +`emms-mp3tag-make-format' to help create a new format. -The PARSER is a function to collect all track info in -`emms-mp3tag-edit-buffer'. It should return the new tracks. If -the track tag changed, it should add a new property tag-modified -and set to non-nil. If the track name change, it should set new -newname to the new file name. +The CDR part also can be a function, which accept one parameter, the +track, and should return a string to insert to `emms-mp3tag-edit-buffer'. ") -(defvar emms-mp3tag-selected-format - (assoc "default" emms-mp3tag-format)) - -(defun emms-mp3tag-select-format (format) - (interactive - (list (completing-read "Set edit format to: " - emms-mp3tag-format nil t))) - (setq emms-mp3tag-select-format (assoc "default" emms-mp3tag-format))) +(defvar emms-mp3tag-get-format-function 'emms-mp3tag-get-format + "Function to decide which format to use for format the track.") + +(defvar emms-mp3tag-parse-function 'emms-mp3tag-default-parser + "Function to parse tags in `emms-mp3tag-edit-buffer'. It should find +all modified tags, and return all the tracks. The tracks which tag has +been modified should set a property 'tag-modified to t, and if the +track name have been change, the function should set a new property +'newname instead set the 'name directly. + +See also `emms-mp3tag-default-parser'.") + +(defvar emms-mp3tag-tagfile-functions + '(("mp3" "mp3info" + ((info-artist . "a") + (info-title . "t") + (info-album . "l") + (info-tracknumber . "n") + (info-year . "y") + (info-genre . "g") + (info-note . "c"))) + ("ogg" . emms-mp3tag-tag-ogg)) + "A List for change tag in files. If the extern program set tag by +command line options one by one such as mp3info, the list should like: + (EXTENSION PROGRAM COMMAND_LINE_OPTIONS) + +Otherwise, a function that accept a parameter, the track, should be +given. + +See also `emms-mp3tag-tag-file' and `emms-mp3tag-tag-ogg'. +") -(defun emms-mp3tag-format-track (format track) - (if (stringp format) +(defun emms-mp3tag-tag-ogg (track) + (call-process "vorbiscomment" nil nil nil + "-w" "-t" + (mapconcat + (lambda (tag) + (concat tag "=" + (emms-track-get track (intern (concat "info-" tag))))) + '("artist" "title" "album" "tracknumber" "date" "genre" "note") + "\n\t") + (emms-track-name track))) + +(defun emms-mp3tag-tag-file (track program tags) + "Change tag in FILE use PROGRAM. The TAGS is given in `emms-mp3tag-tagfile-functions'." + (let (args val) + (mapc (lambda (tag) + (when (> (length (setq val (emms-track-get track (car tag)))) 0) + (setq args (append args (list (concat "-" (cdr tag)) val))))) + tags) + (apply 'call-process program + nil nil nil + filename args))) + +(defun emms-mp3tag-get-format (track) + (let ((format + (assoc (file-name-extension (emms-track-name track)) + emms-mp3tag-formats))) + (if format + (cdr format) + (cdr (assoc "default" emms-mp3tag-formats))))) + +(defun emms-mp3tag-format-track (track) + (let ((format (funcall emms-mp3tag-get-format-function track))) + (if (functionp format) + (funcall format track) (format-spec format (apply 'format-spec-make @@ -107,12 +163,10 @@ newname to the new file name. 'emms-track (copy-sequence track)) ?f (emms-track-name track) (apply 'append - (mapcar - (lambda (pair) - (list (aref (cdr pair) 0) - (or (emms-track-get track (car pair)) ""))) - emms-mp3tag-tags)))) - (funcall format track))) + (mapcar (lambda (tag) + (list (string-to-char (cdr tag)) + (or (emms-track-get track (car tag)) ""))) + emms-mp3tag-tags))))))) (defun emms-mp3tag-track-at (&optional pos) (let ((track (emms-playlist-track-at pos)) @@ -129,19 +183,9 @@ newname to the new file name. (set-buffer (get-buffer-create buf)) (erase-buffer)))) -;; (defun emms-mp3tag-insert-track (track) -;; (cond ((null track) nil) -;; ((not (eq (emms-track-get track 'type) 'file)) -;; (emms-mp3tag-log "Track %s is not a local file!" (emms-track-name track))) -;; ((not (file-writable-p (emms-track-name track))) -;; (emms-mp3tag-log "The file %s is not writable" (emms-track-name track))) -;; (t (insert (emms-mp3tag-format-track -;; (cadr emms-mp3tag-selected-format) track))))) - (defsubst emms-mp3tag-insert-track (track) (and track - (insert (emms-mp3tag-format-track - (cadr emms-mp3tag-selected-format) track)))) + (insert (emms-mp3tag-format-track track)))) (defun emms-mp3tag-insert-tracks (tracks) (save-excursion @@ -149,7 +193,7 @@ newname to the new file name. (emms-mp3tag-erase-buffer emms-mp3tag-edit-buffer) (set-buffer (get-buffer emms-mp3tag-edit-buffer)) (mapc 'emms-mp3tag-insert-track tracks) - (emms-mp3tag-mode 1) + (emms-mp3tag-mode) (pop-to-buffer (current-buffer)) (goto-char (point-min)) (emms-mp3tag-display-log-buffer-maybe))) @@ -168,6 +212,7 @@ newname to the new file name. (emms-mp3tag-insert-tracks tracks)))) (defun emms-mp3tag-edit () + "Edit tags of track at point or marked tracks" (interactive) (if (emms-mark-has-markedp) (emms-mp3tag-edit-marked-tracks) @@ -183,28 +228,32 @@ newname to the new file name. (define-key map [backtab] 'emms-mp3tag-prev-field) (define-key map "\C-c\C-n" 'emms-mp3tag-next-track) (define-key map "\C-c\C-p" 'emms-mp3tag-prev-track) - (define-key map "\C-c\C-c" 'emms-mp3tag-submit) + (define-key map "\C-c\C-c" 'emms-mp3tag-submit-and-exit) + (define-key map "\C-c\C-s" 'emms-mp3tag-submit) (define-key map "\C-c\C-r" 'emms-mp3tag-replace-all) map)) (define-key emms-playlist-mode-map "E" 'emms-mp3tag-edit) -(define-minor-mode emms-mp3tag-mode - "A minor mode to edit mp3tag. -\\{emms-mp3tag-mode-map}" - :lighter " MP3Tag" - :keymap emms-mp3tag-mode-map) +(define-derived-mode emms-mp3tag-mode text-mode "Mp3tag" + "Major mode to edit track tags. +\\{emms-mp3tag-mode-map}") (defun emms-mp3tag-replace-all (name value) + "Replace all track's NAME to VALUE. If turn transient-mark-mode on, +you can apply the command to a selected region." (interactive (list (completing-read "Replace tag: " emms-mp3tag-tags nil t) (read-from-minibuffer "Set tag to: "))) (save-excursion - (goto-char (point-min)) - (while (re-search-forward (concat "^" (regexp-quote name)) nil t) - (skip-chars-forward " \t=") - (delete-region (point) (line-end-position)) - (insert value)))) + (save-restriction + (if (and mark-active transient-mark-mode) + (narrow-to-region (region-beginning) (region-end))) + (goto-char (point-min)) + (while (re-search-forward (concat "^" (regexp-quote name)) nil t) + (skip-chars-forward " \t=") + (delete-region (point) (line-end-position)) + (insert value))))) (defun emms-mp3tag-next-field (arg) (interactive "p") @@ -247,10 +296,12 @@ newname to the new file name. (error "No next track")) (goto-char next))) -(defun emms-mp3tag-submit () - (interactive) - (let ((tracks (funcall (nth 2 emms-mp3tag-selected-format))) - filename exit old pos args val need-sync) +(defun emms-mp3tag-submit (arg) + "Make modified tags take affect. With prefiex argument, bury tag +edit buffer." + (interactive "P") + (let ((tracks (funcall emms-mp3tag-parse-function)) + filename func exit old pos val need-sync) (if (not (and tracks (y-or-n-p "Submit changes? "))) (message "Nothing have to do!") (emms-mp3tag-erase-buffer emms-mp3tag-log-buffer) @@ -275,22 +326,20 @@ newname to the new file name. (funcall emms-cache-modified-function) (funcall emms-cache-set-function filename 'file old)) ;; set tags to original track - (setq args nil) (dolist (tag emms-mp3tag-tags) (when (setq val (emms-track-get track (car tag))) - (emms-track-set old (car tag) val) - (setq args (append args (list (concat "-" (cdr tag)) val))))) + (emms-track-set old (car tag) val))) ;; use mp3info to change tag in mp3 file - (if (and (eq (emms-track-get track 'type) 'file) - (file-writable-p (emms-track-name track)) - (string-match "\\.mp3\\'" filename)) - (if (zerop (setq exit - (apply 'call-process emms-info-mp3info-program-name - nil nil nil - filename args))) - ;; for `emms-cache-sync' not call `emms-info-functions' again - (emms-track-get track 'info-mtime (butlast (current-time))) - (emms-mp3tag-log "Change tags of %s failed with exit value %d" filename exit))) + (when (and (eq (emms-track-get track 'type) 'file) + (file-writable-p (emms-track-name track)) + (setq func (assoc (file-name-extension filename) emms-mp3tag-tagfile-functions))) + (setq exit + (if (functionp (cdr func)) + (funcall (cdr func) track) + (emms-mp3tag-tag-file track (cadr func) (nth 2 func)))) + (if (zerop exit) + (emms-track-get track 'info-mtime (butlast (current-time))) + (emms-mp3tag-log "Change tags of %s failed with exit value %d" filename exit))) ;; update track in playlist (when (and (setq pos (emms-track-get track 'position)) (marker-position pos)) @@ -302,7 +351,12 @@ newname to the new file name. (if (and need-sync (y-or-n-p "You have change some track names, sync the cache? ")) (emms-cache-sync)) (emms-mp3tag-display-log-buffer-maybe) - (message "Set all mp3 tag done!")))) + (message "Set all mp3 tag done!"))) + (if arg (bury-buffer))) + +(defun emms-mp3tag-submit-and-exit () + (interactive) + (emms-mp3tag-submit t)) (defun emms-mp3tag-default-parser () (let (next tracks track key val) -- cgit v1.2.3