Gnus development mailing list
 help / color / mirror / Atom feed
From: wmperry@aventail.com (William M. Perry)
Cc: Wes Hardaker <wjhardaker@ucdavis.edu>, ding@gnus.org
Subject: Re: all you MP3 people out there... a fix for lars!
Date: 30 Oct 1998 13:10:26 -0500	[thread overview]
Message-ID: <86zpad53al.fsf@kramer-fast.bp.aventail.com> (raw)
In-Reply-To: wmperry@aventail.com's message of "30 Oct 1998 11:10:23 -0500"

[-- Attachment #1: Type: text/plain, Size: 1439 bytes --]

wmperry@aventail.com (William M. Perry) writes:

> Darren Stalder <darren@u.washington.edu> writes:
> 
> > -----BEGIN PGP SIGNED MESSAGE-----
> > 
> > Wes Hardaker, in an immanent manifestation of deity, wrote:
> > >Willing?  Heck, thats what my "cdrec" script is currently written in.
> > >Was there another alternative???
> > 
> > Okay.  I've gotten the impression on ding that if it ain't e-lisp, it
> > ain't nuthin.  I need to make a recent change to cd->query and then I
> > can send a snapshot to anyone that wants it.
> 
>   And for those that really do think if it aint-e-lisp, its crap, here's a
> first pass at cddb.el.  It can use cdda2wav or cdparanoia to get the TOC
> from the CD, computes the discid, and can either look up the CD in a local
> CDDB database, or do it remotely.  It even works through my socks.el stuff, 
> wheee. :)
> 
>   Try evaluating (cddb-get-entry) when you have a CD in the drive.  It will
> pull back the XMCD style info.  Next on the list is actually parsing this
> out.  I want to eventually have a suite of elisp scripts that will just
> take a CD, convert the whole damn thing to mp3, and create an appropriate
> .XAM file (the xaudio album format).
> 
>   Or perhaps just go all the way and write an MP3 player in elisp and make
> the playlists, etc, controllable via custom, and album files in a nice easy 
> to (read ...) format.
> 
>   Any interest? :)

  Duh, here's the attachment.

-bp


[-- Attachment #2: cddb.el --]
[-- Type: text/plain, Size: 16831 bytes --]

;;; cddb.el --- CD DataBase interface
;; Author: William M. Perry <wmperry@aventail.com>
;; Created: October 29, 1998
;; Version: 0.1
;; Keywords: multimedia

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Copyright (c) 1998 Free Software Foundation, Inc.
;;;
;;; This file is not part of GNU Emacs, but the same permissions apply.
;;;
;;; GNU Emacs is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2, or (at your option)
;;; any later version.
;;;
;;; GNU Emacs is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Emacs; see the file COPYING.  If not, write to the
;;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;;; Boston, MA 02111-1307, USA.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defconst cddb-version "0.1"
  "Version # of CDDB package.")

(defvar cddb-cdparanoia-program "cdparanoia"
  "*Where to find the cdparanoia program.")

(defvar cddb-cdda2wav-program "cdda2wav"
  "*Where to find the cdda2wav program.")

(defvar cddb-cdda2wav-arguments '("-N" "-t" "1" "-d" "1")
  "*Arguments to pass to cdda2wav to get CD track information.")

(defvar cddb-cd-query-function nil
  "*Function to call to get CD track information for a device.")
  
(defvar cddb-hosts '("/opt/kde/share/apps/kscd/cddb/"
		     "/usr/local/share/apps/kscd/cddb/"		     
		     "/usr/share/apps/kscd/cddb/"
		     ("cddb.moonsoft.com" 8880)
		     ("cddb.sonic.net" 888)
		     ("sunsite.unc.edu" 8880)
		     ("www.cddb.com" 8880)
		     ("cddb.netads.com" 8880)
		     ("cddb.kagomi.com" 8880)
		     ("cddb.celestial.com" 888))
  "*A list of CDDB database locations to search.
Entries in this list can be strings to specify a local directory
structure to search, or a list of (hostname portnumber) to specify a
remote CDDB server.
")

(defvar cddb-possible-probe-programs
  '((cddb-cdparanoia-program . cddb-cdparanoia-get-info)
    (cddb-cdda2wav-program . cddb-cdda2wav-get-info))
  "*Possible programs to probe for CD characteristics.
This is used if you have not explicitly set `cddb-cd-query-function'.

It is an assoc list.  The car of each entry is a symbol name that
holds the program name.  The cdr of each entry is a function name that
should be used as the value of `cddb-cd-query-function' if the program
is found.")


(defsubst cddb-sum-digit (i)
  (let ((sum 0))
    (mapc (lambda (char)
	    (setq sum (+ sum (string-to-int (char-to-string char)))))
	  (int-to-string i))
    sum))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Various ways to extract the information we need to do CDDB queries using
;;; external programs.
;;;
;;; Please feel free to add more!  Just please email them to the maintainer
;;; so other people can enjoy them as well.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun cddb-cdparanoia-get-info (&optional device)
  "Extractinformation about a cd using cdparanoia.
Returns a list of (DISCID TRACKS LENGTH TRACKINFO)

DISCID - the CDDB discid of the cd
TRACKS - the # of tracks on the cd
LENGTH - the total running time of the cd
TRACKINFO - a list of the frame offsets of each track
"
  (save-excursion
    (set-buffer (get-buffer-create " *cdparanoia*"))
    (erase-buffer)
    (let ((exit-status (if device
			   (call-process cddb-cdparanoia-program nil t nil "-d" device "-Q")
			 (call-process cddb-cdparanoia-program nil t nil "-Q")))
	  (track-info nil)
	  (track-time-info nil)
	  (sums 0)
	  (tracks 0)
	  (length 0)
	  (last-offset 0))
      (if (/= exit-status 0)
	  (error "Could not get execute `%s'" cddb-cdparanoia-program))
      (goto-char (point-min))
      (while (re-search-forward "\\([0-9]+\\)[^0-9]+[0-9][0-9]:[0-9][0-9]\\.[0-9][0-9].\\s-+\\([0-9]+\\)" nil t)
	(push (string-to-int (match-string 2)) track-info)
	(setq last-offset (+ (string-to-int (match-string 1)) (string-to-int (match-string 2)))))
      (setq track-info (reverse track-info)
	    tracks (length track-info)
	    track-time-info (mapcar (lambda (offset) (+ 2 (/ offset 75))) track-info)
	    length (- (+ 2 (/ last-offset 75)) (car track-time-info))
	    sums (apply (quote +) (mapcar (quote cddb-sum-digit) track-time-info)))
      (list (format "%02x%04x%02x" sums length tracks)
	    tracks
	    length
	    track-info))))

(defun cddb-cdda2wav-get-info (&optional device)
  "Extractinformation about a cd using cdda2wav.
Returns a list of (DISCID TRACKS LENGTH TRACKINFO)

DISCID - the CDDB discid of the cd
TRACKS - the # of tracks on the cd
LENGTH - the total running time of the cd
TRACKINFO - a list of the frame offsets of each track
"

  (save-excursion
    (set-buffer (get-buffer-create " *cdda2wav*"))
    (erase-buffer)
    (let* ((exit-status (if device
			    (apply 'call-process
				   cddb-cdda2wav-program nil t nil 
				   "-D" device
				   cddb-cdda2wav-arguments)
			  (apply 'call-process
				 cddb-cdda2wav-program nil t nil
				 cddb-cdda2wav-arguments)))
	  (track-info nil)
	  (track-time-info nil)
	  (sums 0)
	  (tracks 0)
	  (length 0)
	  (last-offset 0))
      (if (/= exit-status 0)
	  (error "Could not get execute `%s'" cddb-cdda2wav-program))
      (goto-char (point-min))
      (if (not (re-search-forward "starting sectors.*" nil t))
	  (error "Could not understand output of `%s'" cddb-cdda2wav-program))
      (delete-region (point-min) (match-end 0))
      (while (re-search-forward "\\s-*\\([^\(]+\\)\(\\s-*\\([0-9]+\\)\),?" nil t)
	(push (string-to-int (match-string 2)) track-info))
      (setq last-offset (pop track-info))
      (setq track-info (reverse track-info)
	    tracks (length track-info)
	    track-time-info (mapcar (lambda (offset) (+ 2 (/ offset 75))) track-info)
	    length (- (+ 2 (/ last-offset 75)) (car track-time-info))
	    sums (apply (quote +) (mapcar (quote cddb-sum-digit) track-time-info)))
      (list (format "%02x%04x%02x" sums length tracks)
	    tracks
	    length
	    track-info))))    

\f
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; XMCD file parsing
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defsubst cddb-parse-valid-p ()
  (save-excursion
    (goto-char (point-min))
    (looking-at "# xmcd")))

(defun cddb-parse-get-item (name)
  (goto-char (point-min))
  (let ((regexp (format "^\\s-*%s=\\(.*\\)" name))
	(value ""))
    (while (re-search-forward regexp nil t)
      (setq value (concat value (match-string 1))))
    value))

(defun cddb-parse-info (&optional buffer)
  (save-excursion
    (set-buffer (or buffer (current-buffer)))
    (if (not (cddb-parse-valid-p))
	nil
      )))
	
\f
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Local searching
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun cddb-local-search-internal (discid dir)
  (let ((subdirs (directory-files dir nil nil nil 5))
	(subdir nil)
	(matches nil)
	(entry nil))
    (if (file-exists-p (expand-file-name discid dir))
	(push (expand-file-name discid dir) matches))
    (while subdirs
      (setq subdir (pop subdirs))
      (if (or (string= "." subdir) (string= ".." subdir))
	  nil
	(setq entry (cddb-local-search-internal discid (expand-file-name subdir dir)))
	(if entry
	    (setq matches (append matches entry)))))
    matches))

(defun cddb-local-search (info dir)
  (if (not (file-exists-p dir))
      nil
    (message "Searching locally: %s" dir)
    (let ((matches (cddb-local-search-internal (car info) dir))
	  (discid nil)
	  (filename nil)
	  (title nil))
      (if (not matches)
	  nil
	(save-excursion
	  (set-buffer (get-buffer-create " *cddb*"))
	  (while matches
	    (erase-buffer)
	    (insert-file-contents (car matches))
	    (push (car matches) filename)
	    (push (cddb-parse-get-item "DISCID") discid)
	    (push (cddb-parse-get-item "DTITLE") title)
	    (pop matches)))

	(if (= (length discid) 1)
	    (setq discid (car discid)
		  title (car title)
		  filename (car filename)))

	(if (listp discid)
	    (let ((choices nil)
		  (completion-ignore-case t))
	      (while title
		(push (cons (pop title) (cons (pop filename) (pop discid))) choices))
	      (setq title (completing-read "Multiple matches, choose: " choices nil t))
	      (if (not title)
		  (error "Must choose an item from the list."))
	      (setq filename (car (car (assoc title choices)))
		    discid (cdr (car (assoc title choices))))))
	(save-excursion
	  (set-buffer (get-buffer-create " *cddb*"))
	  (erase-buffer)
	  (insert-file-contents filename)
	  (buffer-string))))))

\f
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Remote searching
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun cddb-build-query-string (info)
  (format "cddb query %s %d %s %d\r\n"
	  (nth 0 info)
	  (nth 1 info)
	  (mapconcat 'int-to-string (nth 3 info) " ")
	  (nth 2 info)))

(defsubst cddb-process-live-p (process)
  (memq (process-status process) '(run open)))
	
(defsubst cddb-get-multiple-data (process)
  (goto-char (point-min))
  (while (not (re-search-forward "^\\.\r?$" nil t))
    (if (not (cddb-process-live-p process))
	(error "CDDB Connection unexpectedly closed."))
    (accept-process-output process))
  (replace-match "")
  (prog1
      (buffer-substring (point-min) (match-beginning 0))
    (delete-region (point-min) (match-beginning 0))))

(defun cddb-get-response (process &optional error-string)
  "Handles getting a response from a CDDB server.
Return value is a list of (CODE DATA EXTENDEDDATA)

CODE is the numerical response code
DATA is the single-line textual response from the server
EXTENDEDDATA is the extra data from the server (for x1x responses)
"
  (let ((response-code nil)
	(response-data nil)
	(extended-data nil)
	(major-code nil)
	(minor-code nil))
    (goto-char (point-min))
    (while (not (re-search-forward "^\\([0-9]+\\)\\s-+\\([^\r\n]*\\)\r*\n" nil t))
      (if (not (cddb-process-live-p process))
	  (error (or error-string "Error: %s") "Connection unexpectedly closed."))
      (accept-process-output process)
      (goto-char (point-min)))

    (setq response-code (string-to-int (match-string 1))
	  response-data (match-string 2)
	  major-code (/ response-code 100)
	  minor-code (/ (- (% response-code 100) (% (% response-code 100) 10)) 10))
    (replace-match "")
    (case major-code
      (1 nil)				; Informative message
      (2 nil)				; Command OK
      (3 nil)				; Command OK so far, continue
      (4				; Command Ok, but cannot be performed
       (error (or error-string "Cannot perform command: %s") response-data))
      (5				; Command unimplemented, incorrect, or program error
       (error (or error-string "Error: %s") response-data))
      (otherwise			; Unknown major response code!
       (error "Unknown major response code: %d" response-code)))

    (case minor-code
      (0 nil)				; Ready for further commands
      (1				; More server-to-client output follows
       (setq extended-data (cddb-get-multiple-data process)))
      (2 nil)				; More client-to-server input follows
      (3 nil)				; Connection will close
      (otherwise nil))			; Unknown minor code is ok?

    (list response-code response-data extended-data)))

(defun cddb-remote-search (query host port)
  "Perform a search on a remote HOST for a given cddb query QUERY"
  (message "Searching remotely: %s:%d" host port)
  (save-excursion
    (set-buffer (get-buffer-create " *cddb*"))
    (erase-buffer)
    (let ((process (open-network-stream "cddb" (current-buffer) host port))
	  (response nil)
	  (category nil)
	  (discid nil)
	  (title nil)
	  (entry nil))

      ;; This is just so we don't see the annoying (to me)
      ;; 'Process foo exited abnormally with code ###'
      ;; at the end of every connection.
      (set-process-sentinel process 'ignore)

      ;; Slurp in the opening banner
      (cddb-get-response process "Cannot connect to server: %s")

      ;; Send/receive the hello
      (process-send-string process
			   (format "cddb hello %s %s cddb.el %s\r\n"
				   (user-real-login-name)
				   (system-name)
				   cddb-version))
      (cddb-get-response process)

      ;; Actually send the query
      (process-send-string process query)
      (setq response (cddb-get-response process "Could not send query to server: %s"))

      (let ((query-response-regexp "\\(\\w+\\)\\s-+\\(........\\)\\s-\\(.*\\)"))
	(case (car response)
	  (200
	   ;; Found exact match
	   (if (string-match query-response-regexp (nth 1 response))
	       (setq category (match-string 1 (nth 1 response))
		     discid (match-string 2 (nth 1 response))
		     title (match-string 3 (nth 1 response)))))
	  (202
	   ;; No matches found
	   )
	  ((210 211)
	   ;; 210 = Found multiple exact matches, list follows
	   ;; 211 = Found inexact matches, list follows
	   (setq response (split-string "\r?\n" (nth 2 response)))
	   (while response
	     (if (string-match query-response-regexp (car response))
		 (progn
		   (push (match-string 1 (car response)) category)
		   (push (match-string 2 (car response)) discid)
		   (push (match-string 3 (car response)) title))
	       (error "Can not understand query response: %s" (car response)))
	     (pop response)))))
      (if (listp category)
	  (let ((choices nil)
		(completion-ignore-case t))
	    (while title
	      (push (cons (pop title) (cons (pop category) (pop discid))) choices))
	    (setq title (completing-read "Multiple matches, choose: " choices nil t))
	    (if (not title)
		(error "Must choose an item from the list."))
	    (setq category (car (car (assoc title choices)))
		  discid (cdr (car (assoc title choices))))))
      (if (not category)
	  ;; Didn't find _any_ matches, or the user didn't like what they found
	  ;; let's move on to the next server.
	  nil
	(process-send-string process (format "cddb read %s %s\r\n" category discid))
	(setq response (cddb-get-response process "Error during `cddb read': %s")
	      entry (nth 2 response)))
      (process-send-string process "quit\r\n")
      (cddb-get-response process "Problem disconnecting from server: %s")
      (if (and entry (string-match "\r\n" entry))
	  (progn
	    ;; Has \r\n crap, clean it up
	    (erase-buffer)
	    (insert entry)
	    (goto-char (point-min))
	    (while (re-search-forward "\r\n" nil t)
	      (replace-match "\n"))
	    (setq entry (buffer-string))
	    (erase-buffer)))
      entry)))

\f
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Main entry points
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun cddb-init ()
  (if (not cddb-cd-query-function)
      (let ((paths nil)
	    (possible-probes cddb-possible-probe-programs)
	    (probe nil)
	    (program-name nil))

	(while possible-probes
	  (setq probe (car possible-probes)
		possible-probes (cdr possible-probes)
		paths exec-path
		program-name (symbol-value (car probe)))
	  (while paths
	    (if (and (file-exists-p (expand-file-name program-name (car paths)))
		     (file-executable-p (expand-file-name program-name (car paths))))
		(setq paths nil
		      possible-probes nil
		      cddb-cd-query-function (cdr probe)))
	    (setq paths (cdr paths))))))
  (if (not cddb-cd-query-function)
      (save-window-excursion
	(set-buffer (get-buffer-create "*CDDB Error*"))
	(erase-buffer)
	(insert "*** ERROR ***\n\n"
		"An error occured while trying to determine how to get CD track "
		"information on your system.\n\n"
		"None of the following programs was found on your system.  "
		"Please customize one of the variables and try again.")
	(fill-region (point-min) (point-max))
	(insert "\n")
	(mapcar (lambda (v)
		  (insert (symbol-name (car v)) " -- " (symbol-value (car v)) "\n"))
		cddb-possible-probe-programs)
	(display-buffer (current-buffer))
	(read-string "An error occured - press RETURN to continue...")
	(error "Aborting"))))

(defun cddb-get-entry (&optional device)
  (cddb-init)
  (let* ((current-host nil)
	 (hosts cddb-hosts)
	 (info (funcall cddb-cd-query-function device))
	 (entry nil)
	 (query (cddb-build-query-string info)))
    (while (and hosts (not entry))
      (setq current-host (car hosts)
	    hosts (cdr hosts))
      (if (stringp current-host)
	  (setq entry (cddb-local-search info current-host))
	(setq entry (apply 'cddb-remote-search query current-host))))
    entry))

  reply	other threads:[~1998-10-30 18:10 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
1998-10-28 18:16 William M. Perry
1998-10-28 22:24 ` Wes Hardaker
1998-10-29  1:04   ` William M. Perry
1998-10-29  1:28     ` Wes Hardaker
1998-10-29  2:11       ` Harald Meland
1998-10-29  5:05         ` Wes Hardaker
1998-10-29  5:20         ` David Hedbor
1998-10-29  7:21         ` Lars Magne Ingebrigtsen
1998-10-29 18:00           ` Wes Hardaker
1998-10-31 13:32             ` Lars Magne Ingebrigtsen
1998-11-02 18:59               ` Wes Hardaker
1998-11-04  5:51               ` Matt Simmons
1998-11-10 16:22                 ` Justin Sheehy
     [not found]                   ` <x7g1br8nh3.fsf@peorth.gweep.net>
1998-11-10 21:35                     ` David Hedbor
1998-11-12 21:34                       ` Brian Edmonds
1998-10-29  2:17   ` Mark R. Boyns
1998-10-29 11:15   ` Lars Magne Ingebrigtsen
1998-10-29 17:56     ` Wes Hardaker
1998-10-29 21:09     ` Darren Stalder
1998-10-31 13:31       ` Lars Magne Ingebrigtsen
1998-11-01 16:18         ` William M. Perry
     [not found]         ` <x7btmsejes.fsf@peorth.gweep.net>
1998-11-01 17:22           ` Brian Edmonds
1998-11-01 20:46             ` William M. Perry
1998-11-07 13:41               ` Lars Magne Ingebrigtsen
1998-10-29 12:57   ` Darren/Torin/Who Ever...
1998-10-29 17:57     ` Wes Hardaker
1998-10-29 20:55       ` Darren Stalder
1998-10-30 16:10         ` William M. Perry
1998-10-30 18:10           ` William M. Perry [this message]
1998-11-04  8:08             ` Matt Simmons
1998-11-04 10:42               ` William M. Perry
1998-11-05  8:05                 ` Matt Simmons
1998-11-05 13:15                   ` William M. Perry
1998-11-07 13:54                     ` Lars Magne Ingebrigtsen
1998-11-09 16:05                       ` Wes Hardaker
1998-11-10  4:51                         ` Lars Magne Ingebrigtsen
1998-11-11  7:10                       ` Matt Simmons

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86zpad53al.fsf@kramer-fast.bp.aventail.com \
    --to=wmperry@aventail.com \
    --cc=ding@gnus.org \
    --cc=wjhardaker@ucdavis.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).