New patches: [Support for Last.fm address@hidden This patch adds a new file which enables EMMS to send the songs you listened to to last.fm. I volunteer to maintain and enhance the file. Please tell me what I have to do therefore. (Sign papers, etc.) Tassilo Horn ] { addfile ./emms-lastfm.el hunk ./emms-lastfm.el 1 +;;; emms-lastfm.el --- add your listened songs to your profile at last.fm + +;; Copyright (C) 2006 Tassilo Horn + +;; Author: Tassilo Horn +;; Keywords: emms, mp3, mpeg, multimedia + +;; This file is part of EMMS. + +;; EMMS 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. + +;; EMMS 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 EMMS; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. + +;;; Commentary: + +;; This code sends information about what music you are playing to +;; last.fm. See and +;; . + +;; It requires http-get.el and http-post.el which can be get from +;; http://emacswiki.org/cgi-bin/wiki.pl?SimpleWikiEditMode. (You have to get it +;; straight from savannah CVS.) + +;; Then tell emacs where those files are: +;; (add-to-list 'load-path "~/elisp/http-emacs") + +;;; Sample configuration: + +;; (setq emms-lastfm-username "my-user-name" +;; emms-lastfm-password "very-secret!") + +;;; Usage: + +;; To activate the last.fm emms plugin, run: +;; M-x emms-lastfm-activate + +;; To deactivate the last.fm emms plugin, run: +;; C-u -1 M-x emms-lastfm-activate + +;; ----------------------------------------------------------------------- + +;; TODO: Have a look at `emms-playing-time'. Stopping the timer may not be +;; needed. +;; TODO: Implement a better error handling and give better infos to the user. + +(require 'http-get) +(require 'http-post) + +(defvar emms-lastfm-username "" + "Your last.fm username") +(defvar emms-lastfm-password "" + "Your last.fm password") + +(defconst emms-lastfm-server "http://post.audioscrobbler.com/" + "The last.fm server responsible for the handshaking procedure.") +;; TODO: get an id for emms (There should be no release without a valid +;; id. Email to address@hidden was sent, waiting for reply with our id. +(defconst emms-lastfm-client-id "tst") +(defconst emms-lastfm-client-version 1.0) + +;; used internally +(defvar emms-lastfm-process nil) +(defvar emms-lastfm-md5-challenge nil) +(defvar emms-lastfm-submit-url nil) +(defvar emms-lastfm-current-track nil) +(defvar emms-lastfm-timer nil) + +(defun emms-lastfm-new-track-function () + (setq emms-lastfm-current-track + (emms-playlist-current-selected-track)) + ;; Tracks should be submitted, if they played 240 secs or half of their + ;; length, whichever comes first. + (let ((secs + (/ (cdr (assoc 'info-playing-time + emms-lastfm-current-track)) + 2))) + (when (> secs 240) + (setq secs 240)) + (unless (< secs 15) ;; Skip titles shorter than 30 seconds + (setq emms-lastfm-timer + (run-with-timer secs nil 'emms-lastfm-submit-track))))) + +(defun emms-lastfm-cancel-timer () + (when emms-lastfm-timer + (cancel-timer emms-lastfm-timer) + (setq emms-lastfm-timer nil))) + +(defun emms-lastfm-activate (&optional ARG) + "Submit the tracks you listened to to http://www.last.fm, if +ARG is positive. If ARG is negative or zero submission of the +tracks will be stopped. This applies to the current track, too." + (interactive "p") + (if (> ARG 0) + (progn + (add-hook 'emms-player-started-hook + 'emms-lastfm-handshake-if-needed) + (add-hook 'emms-player-started-hook + 'emms-lastfm-new-track-function) + (add-hook 'emms-player-stopped-hook + 'emms-lastfm-cancel-timer) + (add-hook 'emms-player-paused-hook + 'emms-lastfm-cancel-timer) + (message "EMMS Last.fm plugin activated.")) + (remove-hook 'emms-player-started-hook + 'emms-lastfm-handshake-if-needed) + (remove-hook 'emms-player-started-hook + 'emms-lastfm-new-track-function) + (remove-hook 'emms-player-stopped-hook + 'emms-lastfm-cancel-timer) + (remove-hook 'emms-player-paused-hook + 'emms-lastfm-cancel-timer) + (cancel-timer emms-lastfm-timer) + (setq emms-lastfm-md5-challenge nil + emms-lastfm-submit-url nil + emms-lastfm-process nil + emms-lastfm-current-track nil) + (message "EMMS Last.fm plugin deactivated."))) + + +(defun read-line () + (buffer-substring-no-properties (line-beginning-position) + (line-end-position))) + +(defun emms-lastfm-handshake-if-needed () + (when (not (and emms-lastfm-md5-challenge + emms-lastfm-submit-url)) + (emms-lastfm-handshake))) + +(defun emms-lastfm-handshake () + "Handshakes with the last.fm server." + (interactive) + (accept-process-output + (setq emms-lastfm-process + (http-get (concat emms-lastfm-server "?hs=true&p=1.1" + "&c=" emms-lastfm-client-id + "&v=" (number-to-string + emms-lastfm-client-version) + "&u=" emms-lastfm-username) + '(("Connection" . "close")) + 'emms-lastfm-handshake-sentinel + 1.0)))) + +(defun emms-lastfm-handshake-sentinel (proc msg) + "Parses the server reponse and act accordingly." + (save-excursion + (set-buffer (process-buffer proc)) + (goto-char (point-min)) + (if (not (string-match "\\(UPTODATE\\|UPDATE\\)" (read-line))) + (progn + (cond ((string-match "FAILED" (read-line)) + (error "Handshake failed")) + ((string-match "BADUSER" (read-line)) + (error "Wrong username")))) + (goto-line 2) + (setq emms-lastfm-md5-challenge (read-line)) + (next-line) + (setq emms-lastfm-submit-url (read-line))) + (erase-buffer))) + +(defun emms-lastfm-submit-track () + "Submits the current track (`emms-lastfm-current-track') to +last.fm." + (let* ((artist (emms-track-get emms-lastfm-current-track 'info-artist)) + (title (emms-track-get emms-lastfm-current-track 'info-title)) + (album (emms-track-get emms-lastfm-current-track 'info-album)) + (musicbrainz-id "") + (track-length (number-to-string + (emms-track-get emms-lastfm-current-track + 'info-playing-time))) + (date (format-time-string "%Y-%m-%d %H:%M:%S" (current-time) t))) + (accept-process-output + (setq emms-lastfm-process + (http-post emms-lastfm-submit-url + `(("u" . ,emms-lastfm-username) + ("s" . ,(md5 (concat (md5 emms-lastfm-password) + emms-lastfm-md5-challenge))) + ("a[0]" . ,artist) + ("t[0]" . ,title) + ("b[0]" . ,album) + ("m[0]" . ,musicbrainz-id) + ("l[0]" . ,track-length) + ("i[0]" . ,date)) + 'utf-8 + '(("Connection" . "close")) + 'emms-lastfm-submission-sentinel + 1.0))))) + +(defun emms-lastfm-submission-sentinel (proc msg) + "Is called after a track submission to last.fm was made." + (save-excursion + (set-buffer (process-buffer proc)) + (goto-char (point-min)) + (if (string= "OK" (read-line)) + (message "\"%s\" submitted..." + (emms-track-description emms-lastfm-current-track)) + ;; TODO: Inform the user what went wrong. + (error "Song couldn't be submitted")) + (erase-buffer))) + + +(provide 'emms-lastfm) +;;; emms-lastfm.el ends here + } [Inclusion of emms-lastfm in emms-devel setup script. Some changes to address@hidden emms-lastfm.el Adds emms-lastfm to the `emms-devel' setup script in emms-setup.el. It's only required, not activated. `emms-lastfm-activate' tells the user to set his username and password, if it's not already set. replaced `error's with `message's at noncritical warnings. ] { hunk ./emms-lastfm.el 104 - (if (> ARG 0) - (progn - (add-hook 'emms-player-started-hook - 'emms-lastfm-handshake-if-needed) - (add-hook 'emms-player-started-hook - 'emms-lastfm-new-track-function) - (add-hook 'emms-player-stopped-hook - 'emms-lastfm-cancel-timer) - (add-hook 'emms-player-paused-hook - 'emms-lastfm-cancel-timer) - (message "EMMS Last.fm plugin activated.")) - (remove-hook 'emms-player-started-hook - 'emms-lastfm-handshake-if-needed) - (remove-hook 'emms-player-started-hook - 'emms-lastfm-new-track-function) - (remove-hook 'emms-player-stopped-hook - 'emms-lastfm-cancel-timer) - (remove-hook 'emms-player-paused-hook - 'emms-lastfm-cancel-timer) - (cancel-timer emms-lastfm-timer) - (setq emms-lastfm-md5-challenge nil - emms-lastfm-submit-url nil - emms-lastfm-process nil - emms-lastfm-current-track nil) - (message "EMMS Last.fm plugin deactivated."))) + (if (not (and emms-lastfm-username emms-lastfm-password)) + (message "%s" (concat "In order to activate the last.fm plugin you first " + "have to set both `emms-lastfm-username' and " + "`emms-lastfm-password'.")) + (if (> ARG 0) + (progn + (add-hook 'emms-player-started-hook + 'emms-lastfm-handshake-if-needed) + (add-hook 'emms-player-started-hook + 'emms-lastfm-new-track-function) + (add-hook 'emms-player-stopped-hook + 'emms-lastfm-cancel-timer) + (add-hook 'emms-player-paused-hook + 'emms-lastfm-cancel-timer) + (message "EMMS Last.fm plugin activated.")) + (remove-hook 'emms-player-started-hook + 'emms-lastfm-handshake-if-needed) + (remove-hook 'emms-player-started-hook + 'emms-lastfm-new-track-function) + (remove-hook 'emms-player-stopped-hook + 'emms-lastfm-cancel-timer) + (remove-hook 'emms-player-paused-hook + 'emms-lastfm-cancel-timer) + (cancel-timer emms-lastfm-timer) + (setq emms-lastfm-md5-challenge nil + emms-lastfm-submit-url nil + emms-lastfm-process nil + emms-lastfm-current-track nil) + (message "EMMS Last.fm plugin deactivated.")))) hunk ./emms-lastfm.el 146 - (interactive) hunk ./emms-lastfm.el 165 - (error "Handshake failed")) + (message "Handshake failed")) hunk ./emms-lastfm.el 167 - (error "Wrong username")))) + (message "Wrong username")))) hunk ./emms-lastfm.el 211 - (error "Song couldn't be submitted")) + (message "Song couldn't be submitted")) hunk ./emms-setup.el 127 + (require 'emms-lastfm) } Context: [fix faulty emms-playlist-mode keybinding, fix due to William and Damien. address@hidden [Added seeking to the playlist keymap, and updated the manual. address@hidden [emms-player-mpd: Only display error if we are certain that url.el is not up-to-date Michael Olson **20061004032213] [seek-for-alsaplayer address@hidden Add relative seek support for alsaplayer ] [midi-files-via-timidity address@hidden A simple-player definition for timidity ] [emms-playing-time.el: Minor cleanups. address@hidden [emms-lyrics.el: Minor Cleanups. address@hidden [pause-for-alsaplayer address@hidden Get pause/resume working for alsaplayer ] [mms-for-mplayer address@hidden mplayer also supports mms:// URLs ] [DoTheRightThing with player pausing and emms-bookmarks.el address@hidden [Added emms-bookmarks.el address@hidden [Added `emms-pause' to emms-playlist-mode.el bound to to ``P''. address@hidden [browser: add deletion started/finished message Damien Elmes **20060923051128] [Added a link to the online version of the manual. address@hidden [emms-playing-time.el now works with `seek-to'. address@hidden [Added `seek-to' to emms.el and emms-player-mplayer.el. address@hidden [browser/cache: support deleting files, make emms-cache-dirty a defsubst Damien Elmes **20060922090553] [TAG 2.1 address@hidden Patch bundle hash: 58334ae665ba914740e5f5ad83cffc389a0a8551