aboutsummaryrefslogtreecommitdiff
(defpackage #:autotag
  (:use #:cl
        #:alexandria
        #:split-sequence
        #:just-getopt-parser)
  (:export #:main))

(in-package #:autotag)

(cl-interpol:enable-interpol-syntax)

(defparameter *quiet-p* nil
  "Should autotag be quiet?")

(defparameter *order-p* nil
  "Apply tags in the same order as in release? (ignore name matching
  or keywords)")

(defparameter *rename-p* t
  "Should files be renamed by default?")

(defparameter *use-id-p* nil
  "")

(defparameter *print-tags-p* nil
  "")

(defvar *album-name* nil)
(defvar *album-artist* nil)
(defvar *npathnames* nil)

(defvar *database* "https://musicbrainz.org/ws/2/")

(defparameter *valid-cmd-line-args*
  '(;;
    (:artist "artist" :required)
    ;;
    (:album #\A :required)
    (:album "album" :required)
    ;;
    (:order #\o)
    (:order "order")
    ;;
    (:id #\i)
    (:id "id")
    ;;
    (:use-id #\u :required)
    (:use-id "use-id" :required)
    ;;
    (:print-tags #\p)
    (:print-tags "print-tags")
    ;;
    (:dont-rename #\n)
    (:dont-rename "dont-rename")
    ;;
    (:quiet #\q)
    (:quiet "quiet")
    ;;
    (:help #\h)
    (:help "help")))

(defun chomp (string)
  (if (and (string/= string "") (eql (last-elt string) #\Newline))
      (subseq string 0 (1- (length string)))
      string))

(defun dmenu (options &key (printer #'princ-to-string) (lines 10) prompt text-output)
  (let ((text-options (mapcar printer options)))
    (multiple-value-bind (output error-output exit-status)
	(trivial-shell:shell-command
	 (format nil "dmenu~@[ -l '~A'~]~@[ -p \"~A\"~]"
		 lines
		 (str:replace-using '("\"" "\\\"" "$" "\\$") prompt))
	 :input (format nil "~{~A~%~}" text-options))
      (declare (ignore error-output exit-status))
      (if text-output
	  (chomp output)
	  (let ((position (position (chomp output) text-options :test #'string=)))
	    (and position (elt options position)))))))

(defun info (control-string &rest format-arguments)
  (unless *quiet-p*
    (apply #'format t control-string format-arguments)))

;; Tags used by this autotagger, each track has one instance of this
;; struct
(defstruct tag
  (title "")
  (album "")
  (artist "")
  (date "")
  (track "")
  (keywords nil))

(defparameter *ogg-tags*
  `((:title  "TITLE"       ,#'tag-title  ,#'(setf tag-title))
    (:artist "ARTIST"      ,#'tag-artist ,#'(setf tag-artist))
    (:album  "ALBUM"       ,#'tag-album  ,#'(setf tag-album))
    (:date   "DATE"        ,#'tag-date   ,#'(setf tag-date))
    (:track  "TRACKNUMBER" ,#'tag-track  ,#'(setf tag-track))))

;; (defun read-ogg-tags (pathname)
;;   (flet ((ogg-field-reader (line)
;; 	   (unless (string= line "")
;; 	     (let* ((fields (split-sequence #\= line))
;; 		    (name (car (rassoc (car fields) *ogg-tags*
;; 				       :test #'string= :key #'first)))
;; 		    (value (apply #'concatenate 'string (cdr fields))))
;; 	       (list name value)))))
;;     (read-tags (format nil "vorbiscomment -l ~A" pathname) #'ogg-field-reader)))

(defun print-tag (tag)
  (dolist (field *ogg-tags*)
    (format t "~A=~A" (elt field 1) (funcall (elt field 2) tag))))

(defun write-ogg-tags (pathname tag)
  (trivial-shell:shell-command
   (with-open-stream (stream (make-string-output-stream))
     (format stream "vorbiscomment -w \"~A\"" pathname)
     (loop :for (tag-name ogg-name reader writer) :in *ogg-tags*
	   :for value = (funcall reader tag)
	   :do
	      (format stream " -t \"~A=~A\"" ogg-name value))
     (get-output-stream-string stream))))

(defun help ()
  (format t #?|~&Usage: autotag [OPTION...] [DIRECTORY... \| FILE...]

autotag - Uses metadata from musicbrainz.org to fill song files with
the tags of a given album.

Examples:

Assume that the directory working on has the same name as <ALBUM>
  autotag --artist=<ARTIST> -A <ALBUM>

Options:
      --artist=ARTIST    improve search results by providing an artist
  -A, --album=ALBUM      name of the album the files belong to
  -i, --id               get album id
  -u, --use-id=ID        use ID as release id
  -p, --print-tags       write tags to stdout instead of file
  -o, --order            ignore file names and assume that files are in order
                         (useful for ripped CDs)
  -q, --quiet            do not output operations being done
  -h, --help             show help options

autotag
Copyright (C) 2020 Thomas Albers Raviola <thomas@thomaslabs.org>
This program comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it
under the terms of the GNU General Public License.
|)
  (uiop:quit))