Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

First pass at track bindings, utils tests.

  • Loading branch information...
commit bbdf4eb5fc8fdf1a5f51bc13e41e8303a77238fa 1 parent e499be5
@redline6561 authored
View
6 cl-echonest.asd
@@ -4,13 +4,15 @@
:version "0.1"
:license "BSD"
:author "Brit Butler <redline6561@gmail.com>"
- :depends-on (#:drakma #:md5 #:st-json #:flexi-streams)
+ :depends-on (#:drakma #:st-json #:flexi-streams
+ #:alexandria #:split-sequence #:md5)
:components ((:module src
:serial t
:components ((:file "package")
(:file "config")
(:file "errors")
- (:file "utils"))))
+ (:file "utils")
+ (:file "track"))))
:in-order-to ((test-op (load-op cl-echonest-tests)))
:perform (test-op :after (op c)
(funcall (intern "RUN!" :cl-echonest-tests))))
View
2  src/errors.lisp
@@ -19,4 +19,4 @@ From http://api.echonest.com/docs/v4/index.html#response-codes")
(defun response-message (respcode)
"Retrieve the message corresponding to the number RESPCODE."
- (rest (nth (1- respcode) *response-codes*)))
+ (rest (nth (1+ respcode) *response-codes*)))
View
4 src/package.lisp
@@ -1,6 +1,6 @@
(defpackage #:cl-echonest
(:documentation "Homepage: <a href=\"http://github.com/redline6561/cl-echonest\">Github</a>")
(:use #:cl)
- (:import-from #:flexi-streams
- #:string-to-octets))
+ (:import-from #:flexi-streams #:string-to-octets)
+ (:import-from #:split-sequence #:split-sequence))
View
46 src/track.lisp
@@ -0,0 +1,46 @@
+(in-package :cl-echonest)
+
+;; TODO: Make this not suck.
+;; Track methods on pathnames should return track instances.
+
+(defclass track ()
+ ;; Define some helpers, get-file, get-analysis
+ ((filepath)
+ (id-or-md5)
+ (analysis)))
+
+(defun get-analysis (track-json)
+ (let* ((url (getjso* "audio_summary.analysis_url" track-json))
+ (params-idx (position #\? url))
+ ;; KLUDGE: drakma::dissect-query and hunchentoot::url-decode are
+ ;; needed just to pass the signature correctly :-/ WTF
+ (params (mapcar (lambda (pair)
+ (cons (car pair) (url-decode (cdr pair))))
+ (drakma::dissect-query (subseq url (1+ params-idx))))))
+ (st-json:read-json (drakma:http-request (subseq url 0 params-idx)
+ :parameters params))))
+
+(defmethod analyze ((file pathname))
+ (let ((result (echonest-call "track" "analyze"
+ `(("md5" . ,(md5sum file))
+ ("bucket" . "audio_summary"))
+ :method :post)))
+ (getjso* "track" result)))
+
+(defmethod profile ((file pathname))
+ (let ((result (echonest-call "track" "profile"
+ `(("md5" . ,(md5sum file))
+ ("bucket" . "audio_summary")))))
+ (getjso* "track" result)))
+
+;; TODO: Why does this die mid-upload? Am I doing something wrong to get dropped?
+(defmethod upload ((file pathname) &optional (wait "false"))
+ (let ((result (echonest-call "track" "upload"
+ `(("wait" . ,wait)
+ ,@(when (string= "true" wait)
+ '(("bucket" . "audio_summary")))
+ ("url" . ,(pathname-name file))
+ ("filetype" . ,(pathname-type file))
+ ("track" . ,file))
+ :method :post :form-data t)))
+ (getjso* "track" result)))
View
66 src/utils.lisp
@@ -4,22 +4,76 @@
"Given a BYTE-VECTOR, coerce it to a list and print it in hexadecimal."
(format nil "~(~{~2,'0X~}~)" (coerce byte-vector 'list)))
+(defgeneric md5sum (object)
+ (:documentation "Creates an MD5 byte-array of STRING and prints it as lower-case hexadecimal."))
+
(defmethod md5sum ((string string))
- "Creates an MD5 byte-array of STRING and prints it as lower-case hexadecimal."
(let ((string (string-to-octets string :external-format :utf-8)))
(format-md5 (md5:md5sum-sequence string))))
(defmethod md5sum ((file pathname))
- "Creates an MD5 byte-array of FILE and prints it as lower-case hexadecimal."
(format-md5 (md5:md5sum-file file)))
-(defun echonest-call (type name params &key (method :get) (format "json"))
+(defun echonest-call (type name params &key (method :get) (format "json")
+ form-data)
(let ((url (format nil "~a/~a/~a" *api-url* type name)))
(multiple-value-bind (response status headers)
- (drakma:http-request url :method method
+ (drakma:http-request url :method method :form-data form-data
:parameters (append `(("format" . ,format)
("api_key" . ,*api-key*))
params))
- (if (= status 200)
- response
+ (if (and (= status 200) (search "\"code\": 0" response))
+ (st-json:getjso "response" (st-json:read-json response))
(error 'echonest-error :message response)))))
+
+;; Copying this here until it or similar gets merged into st-json...
+(defmacro getjso* (keys jso)
+ (let ((last (position #\. keys :from-end t)))
+ (if last
+ `(st-json:getjso ,(subseq keys (1+ last))
+ (getjso* ,(subseq keys 0 last) ,jso))
+ `(st-json:getjso ,keys ,jso))))
+
+;; Lovingly stolen from hunchentoot
+(defun %url-decode (string)
+ "Decodes a URL-encoded STRING"
+ (when (zerop (length string))
+ (return-from %url-decode ""))
+ (let ((vector (make-array (length string) :element-type 'octet :fill-pointer 0))
+ (i 0)
+ unicodep)
+ (loop
+ (unless (< i (length string))
+ (return))
+ (let ((char (aref string i)))
+ (labels ((decode-hex (length)
+ (prog1
+ (parse-integer string :start i :end (+ i length) :radix 16)
+ (incf i length)))
+ (push-integer (integer)
+ (vector-push integer vector))
+ (peek ()
+ (aref string i))
+ (advance ()
+ (setq char (peek))
+ (incf i)))
+ (cond
+ ((char= #\% char)
+ (advance)
+ (cond
+ ((char= #\u (peek))
+ (unless unicodep
+ (setq unicodep t)
+ (upgrade-vector vector '(integer 0 65535)))
+ (advance)
+ (push-integer (decode-hex 4)))
+ (t
+ (push-integer (decode-hex 2)))))
+ (t
+ (push-integer (char-code (case char
+ ((#\+) #\Space)
+ (otherwise char))))
+ (advance))))))
+ (cond (unicodep
+ (upgrade-vector vector 'character :converter #'code-char))
+ (t (flexi-streams:octets-to-string vector)))))
View
23 tests/tests.lisp
@@ -1,5 +1,24 @@
(in-package :cl-echonest-tests)
-(def-suite :cl-echonest)
-(in-suite :cl-echonest)
+(def-suite :cl-echonest-private)
+(in-suite :cl-echonest-private)
+(test response-message-test
+ (is (string= "Success" (cl-echonest::response-message 0))))
+
+(test format-md5-test
+ (flet ((hex-p (char)
+ (let ((int (char-code char)))
+ (or (and (> int 47) (< int 58))
+ (and (> int 96) (< int 103))))))
+ (let ((result (cl-echonest::format-md5 (loop for i from 0 to 255
+ collecting i))))
+ (is (stringp result))
+ (is (every #'hex-p result)))))
+
+; TODO: md5sum-utf8-test, echonest-call-test
+
+(def-suite :cl-echonest-public)
+(def-suite :cl-echonest-public)
+
+; TODO: track methods
Please sign in to comment.
Something went wrong with that request. Please try again.