[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: anything is del.icio.us
From: |
Mark Plaksin |
Subject: |
Re: anything is del.icio.us |
Date: |
Mon, 03 Sep 2007 14:11:03 -0400 |
User-agent: |
Gnus/5.110007 (No Gnus v0.7) Emacs/23.0.51 (gnu/linux) |
Mark Plaksin <address@hidden> writes:
> Here's an anything source for your del.icio.us posts.
This version notices if you update the post cache
(anything-c-delicious-post-cache) outside of anything-delicious.el.
There's a simple shell script in the comments that you can run once a
day to fetch your posts. I do want to resolve the problems fetching
posts via the web inside of Emacs.
New action list:
* browse-url
* browse-url-firefox
* w3m-browse-url
* Copy URL to kill ring.
Be sure browse-url-new-window-flag and
browse-url-firefox-new-window-is-tab are set as you want. I like this:
(setq browse-url-new-window-flag t)
(setq browse-url-firefox-new-window-is-tab t)
This version also fixes a silly bug which made it check for updates once
every 60 days instead of once a day :)
------------------------------------------------------------------------------
;; anything-delicicous.el
;;
;; address@hidden
;; You want to specify a username and password with
;; anything-c-delicious-username and anything-c-delicious-password before
;; loading anything-delicious.el. This is because there seems to be a bug
;; in url-retrieve-synchronously which messes things up when "401 auth
;; required" is returned. See
;; http://comments.gmane.org/gmane.emacs.devel/77533
;; If url-retrieve-synchronously is causing your grief, run something like this
;; out of cron once a day and anything-delicious will notice your new posts:
;;
;; #!/bin/sh
;;
;; # This gets auth from ~/.netrc. Sample .netrc contents:
;; # machine api.del.icio.us login YOURUSERNAME password YOURPASSWODR
;;
;; dir=~/src/backup/delicious
;; out="$dir/backup.`/bin/date +%Y-%m-%d`.xml.gz"
;; curl -n -s https://api.del.icio.us/v1/posts/all | gzip > $out
;; gunzip -dc $out > ~/.emacs.d/anything-delicious-posts
;; TODO:
;; - Better error handling for unexpected status (503, etc) from url-retrieve?
;; - Use/integrate-with/fix delicious-el?
(require 'url-http)
(defvar anything-c-delicious-version "0.3")
(defvar anything-c-delicious-hash
(make-hash-table :test 'equal)
"Hash table which maps Delicious hashes to full posts.")
(defvar anything-c-delicious-tag-hash
(make-hash-table :test 'equal)
"Hash table which maps Delicious tags to posts.
Each tag maps to a list of hashes. Each hash is a key into
`anything-c-delicious-tag-hash'.")
(defvar anything-c-delicious-username nil
"Delicious username.")
(defvar anything-c-delicious-password nil
"Delicious password.")
(defvar anything-c-delicious-api-hostname
"api.del.icio.us"
"Delicious API server's FQDN.")
(defvar anything-c-delicious-api
"/v1/"
"Path to Delicious API.")
(defvar anything-c-delicious-update
(concat anything-c-delicious-api "posts/update")
"Path to fetch timestamp of most recent Delicious post.")
(defvar anything-c-delicious-all-posts
(concat anything-c-delicious-api "posts/all?")
"Path to fetch all Delicious posts.")
(defvar anything-c-delicious-post-cache
(if (boundp 'user-emacs-directory)
(concat user-emacs-directory "anything-delicious-posts")
"~/.anything-delicious-posts")
"Path to Delicious post cache.")
(defvar anything-c-delicious-post-cache-timestamp
(elt (file-attributes anything-c-delicious-post-cache) 5)
"Last noted modification time of cache file.")
(defvar anything-c-delicious-update-time
nil
"Time of last update of all Delicious posts in internal Emacs time format.")
(defvar anything-c-delicious-sort-function
'anything-c-delicious-sort-recent-first
"Function used to sort posts.")
(defvar anything-c-delicious-last-check
(current-time)
"Last time anything-c-delicious checked for new posts.")
(defvar anything-c-delicious-check-interval
1440
"Minutes to wait between checks for new Delicious posts.
Defaults to one day.")
(defvar anything-c-delicious-user-agent
(concat "User-Agent: anything-delicious/" anything-c-delicious-version)
"User-Agent to send to Delicious.
Delicious asks each application to use its own User-Agent")
(defvar anything-c-delicious-debug
nil
"If non-nil, do not delete buffers created by url-retrieve.")
;; stolen from delicioapi.el
(defun anything-c-delicious-auth ()
"Return the authorization string.
It is determined using `anything-c-delicious-username' and
`anything-c-delicious-password'."
(base64-encode-string
(format "%s:%s" anything-c-delicious-username
anything-c-delicious-password)))
;; stolen from delicioapi.el
(defun anything-c-delicious-register-auth ()
"Register delicious auth information."
(let ((auth-info (list (format "%s:443" anything-c-delicious-api-hostname)
(cons "del.icio.us API" (anything-c-delicious-auth)))))
(add-to-list 'url-http-real-basic-auth-storage auth-info)))
;; Until the bug in (or my cluefullness about) url-retrieve is fixed.
(if (and anything-c-delicious-username
anything-c-delicious-password)
(anything-c-delicious-register-auth))
;; There must be a function that does this already! The ones I found and tried
;; didn't work.
(defun anything-c-delicious-encode-time (string)
"Convert STRING to internal Emacs time.
Sample STRING: 2007-08-21T14:42:20Z"
(apply 'encode-time
(mapcar 'string-to-number
(cdr (reverse (split-string string "[-T:Z]"))))))
;; Mostly stolen from delicioapi.el. I changed it so it works when the url
;; is a string or an array. It still feels fragile though.
(defadvice url-http-user-agent-string
(after anything-c-delicious-user-agent activate)
"If talking to api.del.icio.us override User-Agent."
(if (or
(and (stringp url)
(string-match "https*://api.del.icio.us" url))
(and (not (stringp url))
(string= (aref url 4) "api.del.icio.us")))
(setq ad-return-value
(concat "User-Agent: " anything-c-delicious-user-agent "\r\n"))))
(defun anything-c-delicious-maybe-update-posts ()
"Maybe fetch new Delicious posts.
If `anything-c-delicious-check-interval' has passed, posts have
not been read into hashes, fetch posts."
;; If the hash is empty or the timestamp on the cache file has changed,
;; try to read posts in from cache.
(if (or (= (hash-table-count anything-c-delicious-hash) 0)
(and anything-c-delicious-post-cache-timestamp
(< (float-time anything-c-delicious-post-cache-timestamp)
(float-time (elt (file-attributes
anything-c-delicious-post-cache) 5)))))
(anything-c-delicious-get-posts t)
;; Otherwise, if the check interval has passed, check for updates
(if (not (time-less-p
(current-time)
(time-add anything-c-delicious-last-check
(seconds-to-time
(* anything-c-delicious-check-interval 60)))))
(save-excursion
(setq anything-c-delicious-last-check (current-time))
(let* ((update-url
(concat "https://"
anything-c-delicious-api-hostname
anything-c-delicious-update))
(buffer (progn
(sit-for 1)
;; TODO: This is ugly error handling!
(condition-case nil
(url-retrieve-synchronously update-url)
(error nil)))))
(if buffer
(progn
;; 8/30/2007: Update URL returns this:
;; ?xml version='1.0' standalone='yes'?>
;; <update time="2007-08-29T11:47:52Z" />
(set-buffer buffer)
(goto-char (point-min))
(search-forward "<update " nil t)
(if (or (not anything-c-delicious-update-time)
(time-less-p anything-c-delicious-update-time
(anything-c-delicious-encode-time
(anything-c-delicious-read-post-field
"time"))))
(anything-c-delicious-get-posts)
;; If there's no update and the hashes are not built,
;; try to read posts from cache.
(if (= (hash-table-count anything-c-delicious-hash)
0)
(anything-c-delicious-get-posts t)))
(if (not anything-c-delicious-debug)
(kill-buffer buffer)))))))))
(defun anything-c-delicious-get-posts (&optional fromcache)
"Fetch all delicious posts and create tag tables.
If FROMCACHE is non-nil, try to read posts from
`anything-c-delicious-post-cache'. If that fails, fetch them via
the web."
(clrhash anything-c-delicious-hash)
(clrhash anything-c-delicious-tag-hash)
(save-excursion
(let* ((update-url
(concat "https://"
anything-c-delicious-api-hostname
anything-c-delicious-all-posts))
(buffer))
;; If FROMCACHE is non-nil, try to read the posts from the cache.
(if fromcache
(progn
(setq buffer (get-buffer-create " *delicious post cache*"))
(set-buffer buffer)
(condition-case nil
(insert-file-contents anything-c-delicious-post-cache)
;; If reading from cache fails, read from the web and be sure to
;; cache the result
(error
(setq buffer nil
fromcache nil)))))
(if (not buffer)
(progn
(message "Getting Delicious posts via the web. Hang on.")
(sit-for 1)
;; TODO: This is ugly error handling!
(setq buffer (condition-case nil
(url-retrieve-synchronously update-url)
(error nil)))))
(if buffer
(let (href desc extd hash tags time temp)
(set-buffer buffer)
(goto-char (point-min))
(search-forward "<posts " nil t)
(setq anything-c-delicious-update-time
(anything-c-delicious-encode-time
(anything-c-delicious-read-post-field "update")))
(while (search-forward "<post " nil t)
(setq href (anything-c-delicious-read-post-field "href")
desc (anything-c-delicious-read-post-field "description")
extd (anything-c-delicious-read-post-field "extended")
hash (anything-c-delicious-read-post-field "hash")
time (anything-c-delicious-encode-time
(anything-c-delicious-read-post-field "time"))
tags (split-string (anything-c-delicious-read-post-field
"tag") " "))
(puthash hash (list tags href desc extd time)
anything-c-delicious-hash )
(dolist (tag tags)
(puthash tag
(cons hash (gethash tag anything-c-delicious-tag-hash))
anything-c-delicious-tag-hash)))
(if (not fromcache)
(write-file anything-c-delicious-post-cache))
(setq anything-c-delicious-post-cache-timestamp
(elt (file-attributes anything-c-delicious-post-cache) 5))
(if (not anything-c-delicious-debug)
(kill-buffer buffer)))))))
(defun anything-c-delicious-read-post-field (field)
"Return the value of FIELD in the current post."
(save-excursion
(re-search-forward (concat field "=\"\\(.*?\\)\""))
(match-string 1)))
;; How slow is this? It's pretty speedy for me and my ~1300 posts.
(defun anything-c-delicious-string-search (string)
"Return a list of delicious posts matching STRING.
Searches delicious post tags and contents for STRING. Returns a
list of posts."
(let ((result))
;; search tags first
(maphash '(lambda (tag hash-list)
(if (string-match string tag)
(dolist (hash hash-list)
(if (not (member hash result))
(add-to-list 'result hash)))))
anything-c-delicious-tag-hash)
;; then search everything else
(maphash '(lambda (key value)
(dolist (item (cdr value))
(if (and
(and (stringp item)
(string-match string item))
(not (member key result)))
(add-to-list 'result key))))
anything-c-delicious-hash)
(if (functionp anything-c-delicious-sort-function)
(funcall anything-c-delicious-sort-function result)
result)))
(defun anything-c-delicious-sort-recent-first (list)
"Sort posts in LIST with most recent posts first."
(sort list
'(lambda (a b)
(time-less-p
(elt (gethash b anything-c-delicious-hash) 4)
(elt (gethash a anything-c-delicious-hash) 4)))))
(defun anything-c-delicious-filtered-candidate-transformer (list source)
"Transform a list of delicious posts into (DISPLAY . REAL) pairs."
(mapcar
(lambda (candidate)
;; candidate is simply a hash
(let ((post (gethash candidate anything-c-delicious-hash)))
;; elt 2 is desc, elt 1 is URL
`(,(elt post 2) .
,(elt post 1))))
list))
(defvar anything-c-delicious-source
'((name . "delicious")
(init . anything-c-delicious-maybe-update-posts)
(candidates
. (lambda ()
(anything-c-delicious-string-search anything-pattern)))
(match
. ((lambda (candidate)
(string-match anything-pattern (prin1-to-string (gethash candidate
anything-c-delicious-hash) t)))))
(filtered-candidate-transformer
. (lambda (list source)
(anything-c-delicious-filtered-candidate-transformer list source)))
(action .
(("browse-url" . browse-url)
("browse-url-firefox" . browse-url-firefox)
("w3m-browse-url" . w3m-browse-url)
("Copy URL to kill ring" .
(lambda (url)
(kill-new url)))))
(delayed)
;; Needed?
(volatile)
(requires-pattern . 3))
"Search your Delicious posts.
It searches each part of every post--the URL, the tags and both descriptions.")
- anything is del.icio.us, Mark Plaksin, 2007/09/01
- Re: anything is del.icio.us, address@hidden, 2007/09/01
- Re: anything is del.icio.us, Mark Plaksin, 2007/09/02
- Re: anything is del.icio.us, Xavier Maillard, 2007/09/02
- Re: anything is del.icio.us, Michaël Cadilhac, 2007/09/03
- Re: anything is del.icio.us, Richard Stallman, 2007/09/03
- Re: anything is del.icio.us, Xavier Maillard, 2007/09/04
- Re: anything is del.icio.us, Richard Stallman, 2007/09/05
- Message not available
- Re: anything is del.icio.us, Xavier Maillard, 2007/09/05
- Message not available
- Re: anything is del.icio.us, Xavier Maillard, 2007/09/03
- Re: anything is del.icio.us, Michaël Cadilhac, 2007/09/04