;;; emacspeak-librivox.el --- Speech-enabled  LIBRIVOX API client
;;; $Id: emacspeak-librivox.el 4797 2007-07-16 23:31:22Z tv.raman.tv $
;;; $Author: tv.raman.tv $
;;; Description:  Speech-enable LIBRIVOX An Emacs Interface to Free Audio Books
;;; Keywords: Emacspeak,  Audio Desktop librivox
;;{{{  LCD Archive entry:

;;; LCD Archive Entry:
;;; emacspeak| T. V. Raman |raman@cs.cornell.edu
;;; A speech interface to Emacs |
;;; $Date: 2007-05-03 18:13:44 -0700 (Thu, 03 May 2007) $ |
;;;  $Revision: 4532 $ |
;;; Location undetermined
;;;

;;}}}
;;{{{  Copyright:

;;;Copyright (C) 1995 -- 2011, T. V. Raman
;;; Copyright (c) 1994, 1995 by Digital Equipment Corporation.
;;; All Rights Reserved.
;;;
;;; 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, 675 Mass Ave, Cambridge, MA 02139, USA.

;;}}}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;{{{  introduction

;;; Commentary:
;;; LIBRIVOX == http://wwwlibrivox.org Free Audio Books
;;; It provides a simple Web  API http://wiki.librivox.org/index.php/LibriVoxAPI
;;; This module implements an Emacspeak Librivox client.

;;; Code:

;;}}}
;;{{{  Required modules

(require 'cl)
(declaim  (optimize  (safety 0) (speed 3)))
(require 'emacspeak-preamble)
(require 'emacspeak-table-ui)
(require 'derived)

;;}}}
;;{{{ Customizations

(defgroup emacspeak-librivox nil
  "Librivox Access on the Complete Audio Desktop."
  :group 'emacspeak)

(defcustom emacspeak-librivox-directory
  (expand-file-name "librivox"
                    emacspeak-resource-directory)
  "Location where we cache  librivox data.")

(defvar emacspeak-librivox-catalog-location
  (expand-file-name "catalog.csv" emacspeak-librivox-directory)
  "Location where we cache the Librivox catalog.")
;;}}}
;;{{{ Variables:

(defvar emacspeak-librivox-curl-program (executable-find "curl")
  "Curl executable.")

(defvar emacspeak-librivox-curl-common-options
  " --silent "
  "Common Curl options for Librivox. ")

(defvar emacspeak-librivox-api-base
  "http://librivox.org/newcatalog/"
  "Base REST end-point for Librivox API  access.")
(defvar emacspeak-librivox-buffer-name
  "*Librivox Interaction*"
  "Name of Librivox interaction buffer.")

;;}}}
;;{{{ Helpers:

(defun emacspeak-librivox-fetch-catalog ()
  "Fetch catalog to our cache location."
  (interactive)
  (declare (special emacspeak-librivox-api-base
                    emacspeak-librivox-catalog-location))
  (let ((dir  (file-name-directory emacspeak-librivox-catalog-location)))
    (unless (file-exists-p dir)
      (make-directory dir 'parents)))
  (shell-command
   (format "%s %s %s > %s 2>/dev/null"
           emacspeak-librivox-curl-program
           emacspeak-librivox-curl-common-options
           "https://catalog.librivox.org/csv.php"
           emacspeak-librivox-catalog-location))
  (emacspeak-auditory-icon 'task-done))

;;}}}
(provide 'emacspeak-librivox)
;;{{{ Librivox Mode:

(define-derived-mode emacspeak-librivox-mode emacspeak-table-mode
  "Librivox Library Of Free Audio Books"
  "A Librivox front-end for the Emacspeak Audio Desktop."
  (progn
    (declare (special emacspeak-table-speak-row-filter))
    (setq tab-width 12)
    (setq emacspeak-table-speak-row-filter
          '(0 " by " 5))))

(define-prefix-command 'emacspeak-librivox-searcher)

(defun emacspeak-librivox-setup-keys ()
  "Set up Librivox keys."
  (declare (special emacspeak-librivox-mode-map
                    emacspeak-librivox-searcher))
  (loop for binding in
        '(
          ("\C-m" emacspeak-librivox-open-rss)
          ("S" emacspeak-librivox-searcher)
          ([C-return] emacspeak-librivox-play)
          ("P" emacspeak-librivox-play)
          ("u" emacspeak-librivox-open-url)
          ("F" emacspeak-librivox-fetch-catalog)
          )
        do
        (emacspeak-keymap-update emacspeak-librivox-mode-map  binding))
  (loop for key in
        '(
          ("a" emacspeak-librivox-search-author)
          ("t" emacspeak-librivox-search-title)
          ("g" emacspeak-librivox-search-genre))
        do
        (emacspeak-keymap-update emacspeak-librivox-searcher key)))

(emacspeak-librivox-setup-keys)
;;;###autoload
(defun emacspeak-librivox ()
  "Librivox Library Of Free Audio Books."
  (interactive)
  (declare (special emacspeak-librivox-buffer-name
                    emacspeak-librivox-catalog-location))
  (let ((inhibit-read-only t))
    (unless (file-exists-p emacspeak-librivox-catalog-location)
      (message "Retrieving Librivox catalog, might take a minute.")
      (emacspeak-librivox-fetch-catalog))
    (unless (file-exists-p emacspeak-librivox-catalog-location)
      (error "Cannot find Librivox Catalog."))
    (cond
     ((null (get-buffer emacspeak-librivox-buffer-name))
      (emacspeak-table-find-csv-file
       emacspeak-librivox-catalog-location)
      (rename-buffer emacspeak-librivox-buffer-name)
      (emacspeak-librivox-mode))
     (t (switch-to-buffer emacspeak-librivox-buffer-name)))
    (emacspeak-auditory-icon 'open-object)
    (message "Librivox Interaction")))

;;}}}
;;{{{ User Actions:
(defvar  emacspeak-librivox-fields
  '(
    ("ProjectName" . 0)
    ("LibrivoxURL" . 1)
    ("RssURL" . 2)
    ("Category" . 3)
    ("Genre" . 4)
    ("Author1" . 5)
    ("Author2" . 6)
    ("Author3" . 7)
    ("Author4" . 8)
    ("Translator" . 9)
    ("Language" . 10)
    ("Type" . 11))
  "Association list of Librivox Catalog fields.")

(defsubst emacspeak-librivox-field-position (name)
  "Return column for specified field."
  (declare (special emacspeak-librivox-fields))
  (cdr (assoc name  emacspeak-librivox-fields)))

;;;###autoload
(defun emacspeak-librivox-open-rss ()
  "Open RSS  link for current Librivox book."
  (interactive)
  (declare (special emacspeak-table))
  (unless
      (and (eq major-mode 'emacspeak-librivox-mode)
           (boundp 'emacspeak-table)
           emacspeak-table)
    (error "Not in a valid Emacspeak table."))
  (let ((rss (emacspeak-table-this-element
              emacspeak-table
              (emacspeak-table-current-row emacspeak-table)
              (emacspeak-librivox-field-position "RssURL"))))
    (emacspeak-webutils-rss-display rss)))

;;;###autoload
(defun emacspeak-librivox-open-url ()
  "Open Librivox URL  for current Librivox book."
  (interactive)
  (declare (special emacspeak-table))
  (unless
      (and (eq major-mode 'emacspeak-librivox-mode)
           (boundp 'emacspeak-table)
           emacspeak-table)
    (error "Not in a valid Emacspeak table."))
  (let ((url (emacspeak-table-this-element
              emacspeak-table
              (emacspeak-table-current-row emacspeak-table)
              (emacspeak-librivox-field-position "LibrivoxURL"))))
    (browse-url url)))

(defsubst emacspeak-librivox-m3u-filename (rss)
  "Construct M3U  filename given the RSS URL."
  (expand-file-name
   (format  "%s.m3u"
            (substring
             (file-name-nondirectory rss)
             0 -4))
   emacspeak-librivox-directory))

;;;###autoload
(defun emacspeak-librivox-play ()
  "Play current book as a playlist."
  (interactive)
  (declare (special emacspeak-table emacspeak-xslt-program
                    emacspeak-librivox-directory))
  (unless (and (eq major-mode 'emacspeak-librivox-mode)
               (boundp 'emacspeak-table)
               emacspeak-table)
    (error "Not in a valid Emacspeak table."))
  (let* ((rss (emacspeak-table-this-element
               emacspeak-table
               (emacspeak-table-current-row emacspeak-table)
               (emacspeak-librivox-field-position "RssURL")))
         (m3u-file (emacspeak-librivox-m3u-filename rss)))
    (unless (file-exists-p m3u-file)
      (message "Retrieving playlist.")
      (shell-command
       (format
        "%s %s %s > %s"
        emacspeak-xslt-program
        (expand-file-name "rss2m3u.xsl" emacspeak-xslt-directory)
        rss
        m3u-file))
      (emacspeak-auditory-icon 'task-done))
    (message "Playing book.")
    (emacspeak-auditory-icon 'progress)
    (emacspeak-m-player m3u-file 'playlist)))

;;;###autoload

(defun emacspeak-librivox-search-author (pattern)
  "Search in catalog for Author 1."
  (interactive "sAuthor 1")
  (declare (special emacspeak-table))
  (let*((column (emacspeak-librivox-field-position "Author1"))
        (row
         (emacspeak-table-find-match-in-column
          emacspeak-table column pattern 'string-match)))
    (emacspeak-table-goto row column)
    (call-interactively  emacspeak-table-speak-element)))
;;;###autoload
(defun emacspeak-librivox-search-title (pattern)
  "Search in catalog for title."
  (interactive "sTitle")
  (declare (special emacspeak-table))
  (let*((column (emacspeak-librivox-field-position "ProjectName"))
        (row
         (emacspeak-table-find-match-in-column
          emacspeak-table column pattern 'string-match)))
    (emacspeak-table-goto row column)
    (call-interactively  emacspeak-table-speak-element)))

;;;###autoload
(defun emacspeak-librivox-search-genre (pattern)
  "Search in catalog for genre."
  (interactive "sGenre")
  (declare (special emacspeak-table))
  (let*((column (emacspeak-librivox-field-position "ProjectName"))
        (row
         (emacspeak-table-find-match-in-column
          emacspeak-table column pattern 'string-match)))
    (emacspeak-table-goto row column)
    (call-interactively  emacspeak-table-speak-element)))

;;}}}
;;{{{ end of file

;;; local variables:
;;; folded-file: t
;;; byte-compile-dynamic: nil
;;; end:

;;}}}
