;; monk-ripper.el --- Provide ripper mode ;; Gonna be a part of monk.el ;; Copyright (c) 2001 S.Namba ;; All right reserved. ;; Started: Fri Jan 5 17:10:08 2001 (defconst monk-ripper-version "0.10") (require 'monk) (require 'monk-cdaudio) (require 'dired) ;; ;; Object of this file is to create this sort of `track' ;; in *monk*ripper* buffer (Notice there's no 'file prop): ;; ;; ((outfile . "~/ripped/Artist - Album/Song Title") ;; (options) ;; (type . rip) ;; (track-number . 2) ;; (track-offset . 15640) ;; (title . "Song Title") ;; (player . "cdda2wav") ;; (track-opt . "-t%d") ;; (offset-opt . "-o%d") ;; (duration-opt . "-d%df") ;; (post-filter) ;; (frames . 20780) ;; (fps . 75) ;; (time . 277.05) ;; (cent . 207.8) ;; (fps . 75.0)) ;; ;; These are in monk.el ;; (defvar monk-mp3-extension ".mp3") ;; (defvar monk-mp3-directory "/tmp") ;; (defvar monk-ripper-command "monk-ripper") ;; (defvar monk-ripper-options nil) ;; (defconst monk-mp3-junk-buffer "*monk*mp3*junk*" "For junk script output.") (defvar monk-ripper-mode-map (let ((monk-ripper-mode-map (copy-keymap monk-mode-map))) (define-key monk-ripper-mode-map "\C-c\C-c" 'monk-ripper-go-for-it) monk-ripper-mode-map) "Keymap used in `monk-ripper-mode'.") (defvar monk-ripper-buffer-name "*monk*ripper*" "Buffer name for MonK the ripper.") (defvar monk-ripper-mode-hook nil) (defun monk-ripper-go-for-it () "Start ripping from the first track." (interactive) (goto-char (point-min)) (monk-play-this-track)) ;;;###autoload (defun monk-ripper-mode () "A ripper mode used in *monk*ripper* buffer. Available key bindings -- almost the same as `monk-mode-map': \\{monk-ripper-mode-map}" (interactive) ;; This kill disables all local variable, so advancing to next ;; track is disabled. What is the demerit without ;; this (kill-all-local-variables) ? -- perhaps no harm for MonK ;(or (eq major-mode 'monk-mode) (kill-all-local-variables) ; we do not switch between `monk-mode' ;) (setq monk-repeat nil) ; Stop at the end. (setq truncate-lines monk-truncate-lines) (use-local-map monk-ripper-mode-map) (setq major-mode 'monk-ripper-mode) (setq mode-name "MonKRipper") ;; (and window-system (font-lock-mode t)) ; Cheap. (when window-system (monk-font-lock-setup)) (setq buffer-read-only t) (setq goal-column 36) ; Cheap. (monk-face-setup) ; New. Unconditional. (let ((dir (monk-hack-trailing-/ (monk-generate-album-directory)))) (or (and (file-exists-p dir) (file-accessible-directory-p dir)) (make-directory dir t)) ; generate error here if without perm. ;; Should `cd' there ! ;; "standard output.cddb", "standard output.cdindex" may be written there. ;; (setq default-directory ;; Seems like this is not good enough,... but, ;; ... oh, `monk-hack-trailing-/' was wrong ? (cd (expand-file-name dir))) (run-hooks 'monk-ripper-mode-hook)) ;;;###autoload (defun monk-ripper () "Copy current *monk* buffer to new buffer for ripping." (interactive) (or (eq major-mode 'monk-mode) (eq major-mode 'monk-edit-mode) (error "Can't do that job, you should be in *monk* buffer, pal")) ;; Rough. May have to check if there ain't rational titles (?). ;; Some cdrom drive and media combination may generate titles. ;; Or did I wrote MonK so that no title is available without cddb ? ;; If so, this is good enough. (or monk-use-cddb-server (error "Can't do that job, track title is not available, guys")) (let ((tracks (buffer-substring (point-min) (point-max))) (buf (generate-new-buffer monk-ripper-buffer-name))) (switch-to-buffer-other-window buf) (erase-buffer) (insert tracks) (goto-char (point-min)) (monk-ripper-convert-tracks) (monk-ripper-mode) (current-buffer))) ; as `monk-subr' does ;; `monk-play-take-control' Does these: ;;(setq ;; monk-fps monk-cdaudio-fps ;; monk-current-track track ;; ;; offset supplied 0 is okay for ripper (at least now) ;; monk-current-start-sec (/ offset monk-fps) ;; monk-current-track-length (monk-disc-info-get-length monk-current-track) ;; monk-current-cent (/ monk-current-track-length 100.0) ;; monk-current-loop-name "" ;; monk-current-track-name track) ;; `monk-ripper-convert-tracks' -- Not complete yet: ;; The tracks are assumed to be `cda type. ;; Must handle other type -- Just to ignore may be good enough for now. (defun monk-ripper-convert-tracks () "Convert 'cda tracks into 'rip in ripper buffer." (let ((dir (expand-file-name (monk-hack-trailing-/ (monk-generate-album-directory)))) (tlist (monk-cddb-get-title-list)) num tr title) (monk-walk-over-tracks (cond ;; copy-list() otherwise we destroy the original MonK buffer. ((setq tr (copy-list (monk-get-track-property))) (setq num (monk-assq-get-value 'track-number tr) ;; title in `tlist' may be fiddled new one. title (nth (1- num) tlist) ;; For testing monk-album-artist-title.el ;; tr (monk-prop-set-val tr (cons 'title title)) tr (monk-prop-set-val tr '(type . rip)) tr (monk-prop-set-val tr (cons 'options monk-ripper-options)) tr (monk-prop-set-val tr (cons 'post-filter nil)) tr (monk-prop-set-val ; surely this can add prop. tr (cons 'outfile (concat dir "/" ;; title ;; Sometimes cddb TITLE includes "/", so ;; `monk-generate-album-directory' like hack (when title ; else concat() nil (dired-replace-in-string "\/" "--" title)) monk-mp3-extension)))) ;; NO! This is not what we do ;; (forward-line 0) (monk-put-track-property tr) (end-of-line) (delete-region (save-excursion (forward-line 0) (point)) (point)) (monk-insert-track tr "rip:" (format "%5s.00" (monk-sec2cdtime (monk-assq-get-value 'time tr))) (monk-assq-get-value 'track-offset tr) (monk-assq-get-value 'frames tr) ;; (setq ttl (format "\"%s\"" (monk-assq-get-value 'title tr)) nil)))))) ;;; ;;; Replacement functions for monk.el ;;; ;; Should be merged in monk.el later (defun monk-generate-command (track offset &optional duration) "Setup `playing' command line to be passed to `start-process-shell-command'." (cond ;; ((integerp track) ; Integer track deprecated. ;; (monk-cda-generate-command track offset duration)) ;; Added for the ripper: ((and (listp track) ; ripper prop (eq 'rip (monk-assq-get-value 'type track))) (monk-generate-command-ripper track offset)) ((listp track) ; other generic tracks, may return nil (monk-generate-command-generic track offset duration)) (t (error "BUG:Track data %s broken." track)))) ;;;###autoload (defun monk-generate-command-ripper (track offset &optional duration) "Generates command line as list out of object TRACK. See `monk-generate-command-ripper' and `monk-generate-command' for detail." (let* ((ofsopt (monk-assq-get-value 'offset-opt track)) (duropt (monk-assq-get-value 'duration-opt track)) (trkopt (monk-assq-get-value 'track-opt track)) (num (monk-assq-get-value 'track-number track)) (player monk-ripper-command) (options (monk-assq-get-value 'options track)) ;; `dired-shell-stuff-it' does not take care of / in dname/fname ;; This handles error(): (dir (expand-file-name (monk-hack-trailing-/ (monk-generate-album-directory)))) (fname (monk-assq-get-value 'outfile track)) ofs dur command) ;; It Should be already there (by `monk-ripper-mode') ;; (or (file-accessible-directory-p dir) ;; (make-directory dir)) ; let the system handle error (or (and num trkopt) (error "Track must have `track-opt' property track: %s" track)) (setq ofs (and ofsopt (format ofsopt offset)) dur (cond (duropt (format duropt (or duration 0)))) command player trkopt (and num (format trkopt num)) options (and options (if (listp options) options (list options)))) (when (and fname (file-readable-p fname) (null (y-or-n-p (format "File %s already exists. Overwrite ? " fname)))) (error "Aborted")) (when (and fname (file-exists-p fname) (null (file-writable-p fname))) (error "MonK can't write file %s" fname)) (cond ((and command fname num) (append ;; (list (dired-shell-stuff-it command (list fname) nil)) ;; Now player is invoked without shell. (list command fname) options ; generated as list (list trkopt ofs dur))) (t (message "Unknown error, maybe broken track data: %s" track) nil))))