diff --git a/README b/README index 918597a..02d1cc5 100644 --- a/README +++ b/README @@ -1,4 +1,20 @@ -This is a distribution of mp_suggest, an organization tool for MP3 libraries +This is a distribution of mp_suggest, an organization tool for MP3 +libraries. It was written primarily as an experiment in learning Hy. +Hy is a lisp-like programming language that runs on top of the Python VM +and exploits Python's access to the running AST in order to build +working Python programs. + +Writing mp_suggest was an interesting exercise in returning to Lisp +after all these years. I find that I really enjoyed it (although, +honestly, Hy's debugging facilities leave a lot to be desired). The +style used inside mp_suggest is most definitely not Lispy; looking +through it, with its persistent use of cheap anonymous functions and +closures and its function-level metaprogramming, I guess the best +language I could compare it to is Coffeescript. I like Coffeescript a +lot, but I don't get many opportunities to use it professionally, but +the sensibilities of Coffeescript (especially Reginald Braithwaite's +Ristrettology and his other books on functional programming) heavily +influenced the design decisions I made in mp_suggest. * Licensing diff --git a/mp_suggest.hy b/mp_suggest.hy index ee014c3..90ad35b 100755 --- a/mp_suggest.hy +++ b/mp_suggest.hy @@ -17,35 +17,39 @@ (defn print-version [] (print (.format "mp_suggest (hy version {})" *version*)) - (sys.exit 0)) + (print "Copyright (c) 2008, 2014 Elf M. Sternberg ") + (sys.exit)) (defn print-help [] (print "Usage:") (ap-each optlist (print (.format " -{} --{} {}" (get it 0) (get it 1) (get it 3)))) - (sys.exit 0)) + (sys.exit)) ; Given a set of command-line arguments, compare that to a mapped ; version of the optlist and return a canonicalized dictionary of all ; the arguments that have been set. -(defn find-opt-reducer [acc it] - (let [[(, o a) it] - [optmap (ap-reduce (do (assoc acc (+ "-" (. it [0])) (. it [1])) acc) optlist {})]] - (if (.has_key optmap o) (let [[full (car (get optmap o))] - [hasargs (car (cdr (get optmap o)))]] - (assoc acc full (if hasargs a true)))) - acc)) +(defn make-opt-assoc [prefix pos] + (fn [acc it] (assoc acc (+ prefix (get it pos)) (get it 1)) acc)) + +(defn make-options-rationalizer [optlist] + (let [ + [short-opt-assoc (make-opt-assoc "-" 0)] + [long-opt-assoc (make-opt-assoc "--" 1)] + [fullset + (ap-reduce (-> (short-opt-assoc acc it) + (long-opt-assoc it)) optlist {})]] + (fn [acc it] (do (assoc acc (get fullset (get it 0)) (get it 1)) acc)))) ; Assuming the directory name looked like "Artist - Album", return the ; two names separately. If only one name is here, assume a compilation ; or mixtape album and default to "VA" (Various Artists). (defn artist-album [] - (let [[aa (.split (. (.split (.getcwd os) "/") [-1]) " - ")] - [sp (fn [i] (.strip i))]] + (let [[aa (-> (.getcwd os) (.split "/") (get -1) (.split " - "))]] (if (= (len aa) 1) (, "VA" (get aa 0)) - (, (sp (get aa 0)) (sp (get aa 1)))))) + (, (.strip (get aa 0)) (.strip (get aa 1)))))) ; A long list of substitutions intended to turn a filename into a ; human-readable strategy. This operation is the result of weeks @@ -64,7 +68,6 @@ ; filesystem access is a point of failure, but this is mostly ; reliable. - (defn tag-deriver [usefilenames] (fn [mp3s] (defn derive-tag [pos mp3] @@ -146,23 +149,39 @@ [artist (if (.has_key opts "artist") (get opts "artist") - (sfix loc_artist))]] + (sfix loc_artist))] + + [format-string + (string.join ["id3v2 -T \"{}\"" + "--album \"{}\"" + "--artist \"{}\"" + "--genre \"{}\"" + "--song \"{}\"" + "\"{}\""] " ")]] - (print genre album artist))) + (ap-each mp3s + (print (.format format-string (get it 5) album artist genre (get it 4) (get it 0)))))) (defmain [&rest args] (try (let [[optstringsshort - (string.join (ap-map (. it [0]) optlist) ":")] + (string.join (ap-map (+ (. it [0]) (cond [(. it [2]) ":"] [true ""])) optlist) "")] + [optstringslong (list (ap-map (+ (. it [1]) (cond [(. it [2]) "="] [true ""])) optlist))] + [(, opt arg) (getopt.getopt (slice args 1) optstringsshort optstringslong)] + + [rationalize-options + (make-options-rationalizer optlist)] + [options - (ap-reduce (find-opt-reducer acc it) opt {})]] - (cond [(.has_key options "h") (print-help)] - [(.has_key options "v") (print-version)] - [true (suggest options)])))) + (ap-reduce (rationalize-options acc it) opt {})]] + + (cond [(.has_key options "help") (print-help)] + [(.has_key options "version") (print-version)] + [true (suggest options)])))) diff --git a/mp_suggest_man.tex b/mp_suggest_man.tex index ba2a219..7845f93 100644 --- a/mp_suggest_man.tex +++ b/mp_suggest_man.tex @@ -62,6 +62,35 @@ commands can be tweaked, and all of them override the techniques This is probably completely idiosyncratic to the way I do things in my own music archive, but as an exercise in writing Hy, it was fun. +\section{Options} +%%%%%%%%%%%%%%%%% +\begin{Description} +\item[\OptArg{--genre}{genre}] Set the genre manually from the command + line. This overrides any settings found in the ID3 tag. A short form + of this tag is ``-g'' + +\item[\OptArg{--album}{album}] Set the album manually from the command + line. This overrides any settings found in the ID3 tag. A short form + of this tag is ``-a'' + +\item[\OptArg{--artist}{artist}] Set the artist manually from the command + line. This overrides any settings found in the ID3 tag. A short form + of this tag is ``-r'' + +\item[\Opt{--usedir}] Use the directory name for artist and album + information. This overrides anything found in the ID3 tags. A short + form of this tag is ``-n''. + +\item[\Opt{--usefilename}] Use the filename as the song title. This + overrides anything found in the ID3 tags. A short form of this tag is + ``-t''. + +\item[\Opt{--help}] Print a short help message. A short form of this + tag is ``-h'' + +\item[\Opt{--version}] Print the version information. A short form of this + tag is ``-v'' + \section{Requirements} %%%%%%%%%%%%%%%%%%%%%%