log4p

Peter Maas’s Weblog

Archive for June, 2009

A simple couchdb info widget for wordpress

I wrote a small wordpress widget to display some version details of the CouchDB server I'm running. Writing a wordpress widget wasn't to hard (I used this excellent tutorial to get started), parsing CouchDB JSON responses is even simpler.

The code for the widget (put it in 'wp-content/plugins' and enable the plugin afterwards) looks like this:

  1. <?php
  2. /*
  3. Plugin Name: Couch-Info
  4. Plugin URI: http://log4p.com
  5. Description: Shows information of the configured couchdb server
  6. Version: 0.1
  7. Author: Peter Maas
  8. Author URI: http://log4p.com
  9. */
  10.  
  11. add_action("widgets_init", array('Couch_Info_Widget', 'register'));
  12. class Couch_Info_Widget {
  13.   function control(){
  14.     $data = get_option('couchdb_info_widget');
  15.     ?>
  16.     <p>Configure CouchDB location</p>
  17.     <p><label for="couchdb_info_widget_host">Host</label><input name="couchdb_info_widget_host" type="text" value="<?= $data['host'] ?>" /></p>
  18.     <p><label for="couchdb_info_widget_port">Port</label><input name="couchdb_info_widget_port" type="text" value="<?= $data['port'] ?>" /></p>
  19.     <?php
  20.     if (isset($_POST['couchdb_info_widget_host'])){
  21.       $data['host'] = attribute_escape($_POST['couchdb_info_widget_host']);
  22.       $data['port'] = attribute_escape($_POST['couchdb_info_widget_port']);
  23.       update_option('couchdb_info_widget', $data);
  24.     }
  25.   }
  26.  
  27.   function widget($args){
  28.     $data = get_option('couchdb_info_widget');
  29.  
  30.     $couchdb_version = json_decode(file_get_contents('http://'.$data['host'].':'.$data['port'].'/'))->version;     
  31.     $num_articles = json_decode(file_get_contents('http://'.$data['host'].':'.$data['port'].'/articles/'))->doc_count;
  32.  
  33.     echo $args['before_widget'];
  34.     echo $args['before_title'] . 'COUCHDB INFO' . $args['after_title'];
  35.     ?>
  36.     Parts of this blog are fed by <a href="http://couchdb.apache.org">CouchDB</a>
  37.     I'm running CouchDB of the <a href="http://svn.apache.org/repos/asf/couchdb/trunk/">current SVN trunk</a> and update once in a while. Current version: <strong><?= $couchdb_version ?></strong>
  38.     My CouchDB instance contains all <strong><?= $num_articles ?></strong> articles of this blog. All numbers in this widget are retrieved from CouchDB as well
  39.     <?php
  40.     echo $args['after_widget'];
  41.   }
  42.   function register(){
  43.     register_sidebar_widget('CouchDB Info', array('Couch_Info_Widget', 'widget'));
  44.     register_widget_control('CouchDB Info', array('Couch_Info_Widget', 'control'));
  45.   }
  46. }
  47. ?>

As you can see it is one class which has two functions which are registered in wordpress. 'widget' retrieves the needed data and renders the actual widget. 'control' creates the control form to edit the widgets' settings. This is what the control form looks like in the admin interface:

Widget controlpanel (right bottom)

The output of the 'widget' function should be visible in the bottem of the right sidebar.

2 comments

3v12 api from Scala

I rewrote the code in my previous post in Scala, with a minor difference.. I'm not using an RSS api here.. Scala has native XML support... which makes writing basic RSS a breeze:

  1. package v12_rss
  2.  
  3. import java.net.{URLConnection, URL}
  4. import scala.xml._
  5.  
  6. // define some case classes as a simple model for the rss feed we're going to build
  7. case class Channel(title:String, link:String, description:String, items:List[Item]) {
  8.   def toXML = <channel>
  9.                 <title>{title}</title>
  10.                 <link>{link}</link>
  11.                 <description>{description}</description>
  12.                 {items.map{_.toXML}}
  13.               </channel>
  14. }
  15. case class Item(title:String, link:String, description:String, enclosure:Enclosure) {
  16.   def toXML = <item>
  17.                 <title>{title}</title>
  18.                 <link>{link}</link>
  19.                 <description>{}</description>
  20.                 {enclosure.toXML}
  21.               </item>
  22. }
  23. case class Enclosure(url:String) {
  24.   def toXML = <enclosure url={url} type="image/jpeg"/>
  25. }
  26.  
  27. // Helper for working with URN values from the API
  28. case class Urn(urn:String) {
  29.   def comps = urn.split(":").slice(1, 4).toArray // remove the first value ('urn') not interesting
  30.  
  31.   def src = comps.apply(0)
  32.   def mediaType = comps.apply(1)
  33.   def number = comps.apply(2)
  34. }
  35.  
  36. object Main {
  37.  
  38.   def main(args: Array[String]) = {
  39.     val groupElem = retrieveGroup(41129661)
  40.     println(<rss version="2.0">
  41.               {new Channel(title(groupElem), "http://3voor12.vpro.nl/tv/", shortTitle(groupElem), itemize(groupElem)).toXML}
  42.             </rss>)
  43.   }
  44.  
  45.   def retrieveGroup(num:Int):Elem = {
  46.     XML.load("http://3voor12.vpro.nl/api/media/1/rest/group/"+ num + ".xml")
  47.   }
  48.  
  49.   // short for getting an attribute as string
  50.   def attr(node:Node, name:String) = node.attribute(name).get.text
  51.  
  52.   // retrieve the first title field from the given XML
  53.   def title(elem:Elem):String = (elem \\ "title").first.text
  54.  
  55.   // retrieve the first shortTitle field from the given XML
  56.   def shortTitle(elem:Elem):String = (elem \\ "shortTitle").first.text
  57.  
  58.   // determine the image url for the given media xml
  59.   def imageUrl(elem:Elem):String = "http://images.vpro.nl/images/" + new Urn(attr((elem \\ "image").first, "urn")).number
  60.  
  61.   def itemize(group:Elem):List[Item] = {
  62.     val items = (group \\ "media") slice(0, 15) map{ m =>
  63.       val urn = new Urn(attr(m, "urn"))
  64.       println ("http://3voor12.vpro.nl/api/media/1/rest/"+ urn.mediaType +"/"+ urn.number + ".xml")
  65.       val media = XML.load("http://3voor12.vpro.nl/api/media/1/rest/"+ urn.mediaType +"/"+ urn.number + ".xml")
  66.  
  67.       new Item(title(media), "http://3voor12.vpro.nl/tv/#/41129661/" + urn.number , shortTitle(media), new Enclosure(imageUrl(media)))
  68.     }
  69.  
  70.     items.toList
  71.   }
  72. }

1 comment

Using the 3voor12 api to get an RSS feed of your favorite playlist

A couple of months ago we developed 3voor12TV. During festivals like Noorderslag, Pinkpop and (upcoming) Lowlands the 3voor12 crew tries to get as much high quality (h264) material online in the shortest time possible.

The application was developed in actionscript 3.0 on top of a public API. Well, it is public... but no public documentation yet. Sorry ;-) The 3voor12 Pinkpop mashup was the first real utilization of the API.

Whilst waiting for the new video's I found myself refreshing the player over and over again; checking for new content.

I decided to automate this using the API. The Ruby script below uses the API to create a simple RSS feed with images and a direct link to each concert:

  1. require 'rubygems'
  2. require 'json'
  3. require 'open-uri'
  4. require 'rss'
  5.  
  6. API_URL_BASE = "http://3voor12.vpro.nl/api/media/1/rest/"
  7. PLAYLIST_ID = "41129661"
  8.  
  9. # function to convert urn to urls
  10. def urn_to_api_url(urn)
  11.   urn_parts = urn.split(":") # urn contains source, entity type and unique number
  12.   "#{API_URL_BASE}#{urn_parts[2]}/#{urn_parts[3]}.json" #  bypass content negotiation, force json formatted responses
  13. end
  14.  
  15. def urn_to_url(urn)
  16.   urn_parts = urn.split(":")
  17.   "http://3voor12.vpro.nl/tv/\#/#{PLAYLIST_ID}/#{urn_parts[3]}"
  18. end
  19.  
  20. # retrieve and parse a playlist (group)
  21. playlist = JSON.load(open("#{API_URL_BASE}group/#{PLAYLIST_ID}.json"))
  22. # extract the playlist items
  23. programUrns = playlist['group']['members']['member'].map{|m| m['media']['@urn']}
  24.  
  25. # create playlist
  26. rss = RSS::Maker.make("2.0") do |maker|
  27.   maker.channel.title = "3voor12tv :: #{playlist['group']['title']}"
  28.   maker.channel.description = playlist['group']['shortTitle']
  29.   maker.channel.about = "http://3voor12.vpro.nl/tv/"
  30.   maker.channel.link = "http://3voor12.vpro.nl/tv/"
  31.   # retrieve the playlist items
  32.   programUrns[0..15].each do |urn|
  33.     maker.items.new_item do |item|
  34.       program = JSON.load(open(urn_to_api_url(urn)))['program']
  35.       img_url = "http://images.vpro.nl/images/#{program['relatedImages']['relatedImage']['image']['@id']}+s(320)"
  36.      
  37.       item.title = program['title']
  38.       item.description = (program['synopsis'] || program['title']) + "<br/> <img src=\"#{img_url}\"/>"
  39.       item.link = urn_to_url(urn)
  40.      
  41.       enclosure = maker.items.last.enclosure
  42.       enclosure.url = img_url
  43.       enclosure.length = -1
  44.       enclosure.type = "image/jpeg"
  45.     end
  46.   end
  47. end
  48.  
  49. # write to disk
  50. File.open("3v12feed.xml","w") do |f|
  51.  f.write(rss)
  52. end

I have a cronjob executing the script once in a while:

http://feeds2.feedburner.com/3voor12tvPinkpop

3 comments