<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>6.34942</title>
  <id>http://634942.com/</id>
  <updated>2011-09-22</updated>
  <author>
    <name>ændrük</name>
  </author>
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/634942" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="634942" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:feedFlare xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" href="http://fusion.google.com/add?feedurl=http%3A%2F%2Ffeeds.feedburner.com%2F634942" src="http://buttons.googlesyndication.com/fusion/add.gif">Subscribe with Google</feedburner:feedFlare><entry>
    <title>Wrapping up the long tail</title>
    <link rel="alternate" href="http://634942.com/2011/09/22/wrapping-up-the-long-tail/" />
    <id>http://634942.com/2011/09/22/wrapping-up-the-long-tail/</id>
    <published>2011-09-22</published>
    <updated>2011-09-22</updated>
    <author>
      <name>ændrük</name>
    </author>
    <content type="html">&lt;p&gt;I don’t listen to a wide range of music. I tend to stick to just a handful of artists, listening to the albums I love over and over.&lt;/p&gt;

&lt;p&gt;This gives the distribution of what artists I listen to a well-defined &lt;a href="http://en.wikipedia.org/wiki/Long_Tail"&gt;long tail&lt;/a&gt;, and I think &lt;a href="http://www.last.fm/user/aendruk/charts?rangetype=overall&amp;amp;subtype=artists"&gt;Last.fm’s histogram&lt;/a&gt; doesn’t quite do it justice. With the assistance of the &lt;a href="http://www.last.fm/api/show?service=288"&gt;Last.fm API&lt;/a&gt;, the R &lt;a href="http://cran.r-project.org/web/packages/wordcloud/"&gt;wordclould package&lt;/a&gt;, a &lt;a href="http://onertipaday.blogspot.com/2011/07/word-cloud-in-r.html"&gt;code sample&lt;/a&gt;, and some &lt;a href="http://nsaunders.wordpress.com/2011/07/28/i-cant-resist-a-word-cloud-now-using-r/"&gt;inspiration&lt;/a&gt;, I’ve generated a new visualization:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://634942.com/:wordcloud-artists_2011-09-22.png"&gt;&lt;img src="http://634942.com/:wordcloud-artists_2011-09-22.png!" alt="Word cloud of artists logged to Last.fm, ranked by play count"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I think this more clearly illustrates the sheer difference in scale between the few dozen artists that I listen to regularly and the thousand others I’ve had opportunity to evaluate.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Custom keyboard symbols for Unity</title>
    <link rel="alternate" href="http://634942.com/2011/09/03/custom-keyboard-symbols-for-unity/" />
    <id>http://634942.com/2011/09/03/custom-keyboard-symbols-for-unity/</id>
    <published>2011-09-03</published>
    <updated>2011-09-03</updated>
    <author>
      <name>ændrük</name>
    </author>
    <content type="html">&lt;p&gt;Dot stickers make an excellent tool for customizing the Apple aluminum keyboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/andrewk/6110904986/"&gt;&lt;img src="http://634942.com/:2011-09-03-17-30-29_e.jpg!" alt="Custom keyboard symbols for Ubuntu Unity"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/andrewk/6110359499/"&gt;&lt;img src="http://634942.com/:2011-09-03-17-23-38_e.jpg!" alt="Custom keyboard symbols for Ubuntu Unity"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.flickr.com/photos/andrewk/6110905380/"&gt;&lt;img src="http://634942.com/:2011-09-03-17-33-06_e.jpg!" alt="Custom keyboard symbols for Ubuntu Unity"&gt;&lt;/a&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Adrenaline triphosphate</title>
    <link rel="alternate" href="http://634942.com/2011/07/12/adrenaline-triphosphate/" />
    <id>http://634942.com/2011/07/12/adrenaline-triphosphate/</id>
    <published>2011-07-12</published>
    <updated>2011-07-12</updated>
    <author>
      <name>ændrük</name>
    </author>
    <content type="html">&lt;p&gt;ATP.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://634942.com/:adrenaline-triphosphate.png" alt="Adrenaline triphosphate"&gt;&lt;/p&gt;

&lt;p&gt;That’s what it stands for, right?&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Automatic submission of Rockbox scrobbler logs</title>
    <link rel="alternate" href="http://634942.com/2011/07/07/automatic-submission-of-rockbox-scrobbler-logs/" />
    <id>http://634942.com/2011/07/07/automatic-submission-of-rockbox-scrobbler-logs/</id>
    <published>2011-07-07</published>
    <updated>2011-07-07</updated>
    <author>
      <name>ændrük</name>
    </author>
    <content type="html">&lt;p&gt;Rockbox has the ability to &lt;a href="http://www.rockbox.org/wiki/LastFMLog"&gt;generate Audioscrobbler logs&lt;/a&gt; for later submission to Last.fm. A custom udev rule can be combined with a command-line Last.fm scrobbler to automate the process of submitting tracks every time the digital audio player is connected to the computer.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://634942.com/:scrobbler-osd2.png" alt="notify-send"&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="http://reactivated.net/writing_udev_rules.html#external-run"&gt;udev rule&lt;/a&gt; is designed to call a script whenever a Rockbox device is connected:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# /etc/udev/rules.d/80-custom.rules

ACTION=="add", SUBSYSTEM=="block", ATTR{partition}=="1", ATTRS{vendor}=="Rockbox ", ATTRS{model}=="Internal Storage", RUN+="/opt/rockbox/upload-scrobbler-log"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Last.fm credentials can be stored in GNOME Keyring and accessed using &lt;a href="https://launchpad.net/gkeyring"&gt;gkeyring&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gkeyring.py --set --name 'http://www.last.fm/' --password 'YOUR_PASSWORD_HERE' -p username_value=YOUR_USERNAME_HERE,origin_url=http://www.last.fm/
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The script will wait for the device to be mounted, retrieve the Last.fm credentials from GNOME Keyring, display a graphical notification using &lt;a href="http://packages.ubuntu.com/natty/libnotify-bin"&gt;notify-send&lt;/a&gt;, call &lt;a href="http://ximik.net/code/laspyt/"&gt;Laspyt&lt;/a&gt; to submit the scrobbler log, and then display a report:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;#!/bin/bash
# /opt/rockbox/upload-scrobbler-log
# WARNING: This script must finish quickly because udev is effectively paused while it is running

# Run all this in the background and detach from the parent process
{

    ubuntuuser="ak"
    logfile="/tmp/upload-scrobbler-log.log"
    backupscrobblerlog="/tmp/scrobbler.log.backup"
    gkeyring="/opt/rockbox/gkeyring.py"
    laspyt="/opt/rockbox/laspyt.py"

    echo "Script starting at $(date)" &amp;gt;&amp;gt; "$logfile"

    dapdescription="$(echo -e "$ID_FS_LABEL_ENC")"
    echo "Detected $dapdescription at $DEVNAME" &amp;gt;&amp;gt; "$logfile"

    # Wait for filesystem to be automounted
    echo "Waiting up to 20 seconds for automount..." &amp;gt;&amp;gt; "$logfile"
    counter=0
    while [ $counter -lt 20 ]; do
        grep -q "$DEVNAME" /proc/mounts &amp;amp;&amp;amp; break
        sleep 1
        let counter++
    done

    # Abort if timed out
    if [ $counter -ge 20 ]; then
        echo "Unable to locate mountpoint; aborting" &amp;gt;&amp;gt; "$logfile"
        exit 1
    fi

    # Determine mountpoint
    mountpoint="$(echo -e "$(grep $DEVNAME /proc/mounts | awk '{print $2}')")"
    echo "Detected mountpoint at $mountpoint" &amp;gt;&amp;gt; "$logfile"

    # Identify log file
    scrobblerlog="$mountpoint/.scrobbler.log"
    echo "Using scrobbler log file $scrobblerlog" &amp;gt;&amp;gt; "$logfile"

    # Get credentials from keyring
    echo "Getting credentials from keyring..." &amp;gt;&amp;gt; "$logfile"
    credentials="$(sudo -H -u $ubuntuuser \
        env $(grep -v "^#" /home/$ubuntuuser/.dbus/session-bus/$(cat /var/lib/dbus/machine-id)-0) \
        $gkeyring -p origin_url=http://www.last.fm/ --output username_value,secret)"
    username="$(echo "$credentials" | awk '{print $1}')"
    password="$(echo "$credentials" | awk '{print $2}')"
    if [ -z "$username" -o -z "$password" ]; then
        echo "Failed to get credentials; aborting" &amp;gt;&amp;gt; "$logfile"
        exit 1
    else
        echo "Got credentials for $username" &amp;gt;&amp;gt; "$logfile"
    fi

    # Back up scrobbler log
    echo "Backing up scrobbler log to $backupscrobblerlog" &amp;gt;&amp;gt; "$logfile"
    cp "$scrobblerlog" "$backupscrobblerlog"

    # Upload scrobbler log
    timezone="$(date +%:::z)"
    echo "Uploading scrobbler log file with timezone offset $timezone..." &amp;gt;&amp;gt; "$logfile"
    sudo -H -u $ubuntuuser \
        env DISPLAY=$(w -s $ubuntuuser | grep -m 1 " :" | awk "{print \$3}") \
        notify-send --icon "/usr/share/icons/Humanity/devices/48/multimedia-player.svg" \
            "Uploading scrobbler log" \
            "Submitting $(grep -v "^#" "$scrobblerlog" | wc -l) tracks from $dapdescription to Last.fm as $username"
    sudo -H -u $ubuntuuser \
        $laspyt --file "$scrobblerlog" --timezone "$timezone" --clear \
            --user "$username" --password "$password" \
            2&amp;gt;&amp;amp;1 | sed 's/\x1B\[[0-9;]*[mK]//g' &amp;gt;&amp;gt; "$logfile"

    # Report
    echo "Done" &amp;gt;&amp;gt; "$logfile"
    sudo -H -u $ubuntuuser \
        env DISPLAY=$(w -s $ubuntuuser | grep -m 1 " :" | awk "{print \$3}") \
        zenity --text-info --title "Scrobbler log upload" --filename "$logfile" &amp;amp;

} &amp;amp; disown

exit 0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Details from a run:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://634942.com/:scrobbler-log1.png" alt="zenity"&gt;&lt;/p&gt;

&lt;p&gt;and confirmation on Last.fm:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://634942.com/:scrobbler-lastfm.png" alt="Last.fm"&gt;&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Learning to use R</title>
    <link rel="alternate" href="http://634942.com/2011/06/03/learning-to-use-r/" />
    <id>http://634942.com/2011/06/03/learning-to-use-r/</id>
    <published>2011-06-03</published>
    <updated>2011-06-03</updated>
    <author>
      <name>ændrük</name>
    </author>
    <content type="html">&lt;p&gt;To begin familiarizing myself with R, I’ve been experimenting with &lt;a href="http://cran.r-project.org/web/packages/KernSmooth/"&gt;binned kernel density estimates&lt;/a&gt; using &lt;a href="http://api.askubuntu.com/"&gt;data from Ask Ubuntu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://634942.com/:plot-unacc.png" alt="Unaccepted answers for user: ændrük"&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://634942.com/:plot-acc.png" alt="Accepted answers for user: ændrük"&gt;&lt;/p&gt;

&lt;p&gt;The resulting plots aren’t very meaningful, but they’re quite pretty.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Implementing a hybrid SSD/HDD home folder</title>
    <link rel="alternate" href="http://634942.com/2011/05/17/implementing-a-hybrid-ssdhdd-home-folder/" />
    <id>http://634942.com/2011/05/17/implementing-a-hybrid-ssdhdd-home-folder/</id>
    <published>2011-05-17</published>
    <updated>2011-05-17</updated>
    <author>
      <name>ændrük</name>
    </author>
    <content type="html">&lt;p&gt;I’m currently experimenting with a low-effort technique for mimicking the benefits of a hybrid solid state/hard disk drive.&lt;/p&gt;

&lt;p&gt;My approach is to use a union mount with multiple writable branches to combine folders from both an SSD and HDD into a single home folder in the root filesystem. The result is a unified home folder that contains a mixture of files from each drive in a way that is transparent to the operating system:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://634942.com/:ssd-emblem.png"&gt;&lt;img src="http://634942.com/:ssd-emblem.png!" alt="Hybrid SSD/HDD home folder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Newly created files are stored on the same device as their parent folder. Files created at the root of the home folder are typically configuration files and so default to the SSD.&lt;/p&gt;

&lt;p&gt;As the screenshot reveals, I have also drafted a Nautilus extension that detects which storage back end each file uses and labels those which use the SSD.&lt;/p&gt;

&lt;h2 id="implementation"&gt;Implementation&lt;/h2&gt;

&lt;p&gt;The branches of the union mount exist externally as folders within the root filesystem. I chose to place them within &lt;code&gt;/home/.ssd&lt;/code&gt; and &lt;code&gt;/home/.hdd&lt;/code&gt;. On my system &lt;code&gt;/&lt;/code&gt; already uses the SSD so &lt;code&gt;/home/.ssd&lt;/code&gt; can simply exist as a regular folder, while &lt;code&gt;/home/.hdd&lt;/code&gt; is mounted separately. &lt;code&gt;/etc/fstab&lt;/code&gt; contains:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# / containing /home/.ssd (Physical SSD)
UUID=... / ext4 noatime,discard,errors=remount-ro 0 1
# /home/.hdd (Physical HDD)
UUID=... /home/.hdd ext4 errors=remount-ro 0 2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The default file creation policy in &lt;a href="http://aufs.sourceforge.net/"&gt;aufs&lt;/a&gt; is to store new files within the same branch as their parent folder, with files created in the root directory of the union filesystem defaulting to the first listed branch. As long as the SSD branch is listed first, no other policy needs to be explicitly defined:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# /home/ak from SSD and HDD mounts
none /home/ak aufs noauto,br:/home/.ssd/ak=rw:/home/.hdd/ak=rw 0 0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It is critical that this entry in &lt;code&gt;/etc/fstab&lt;/code&gt; be mounted only after the previous two lines so that the incorporated branches contain mounted filesystems and not empty mount points. Until I learn how to &lt;a href="http://askubuntu.com/q/40185/1859"&gt;specify a particular mount order in /etc/fstab&lt;/a&gt;, I have disabled automatic mounting of the union mount (&lt;code&gt;noauto&lt;/code&gt; above) and instead mount it during &lt;code&gt;/etc/rc.local&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# Workaround to mount aufs only *after* its branches exist
mount /home/ak
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Back end detection is accomplished with a &lt;a href="http://localhostr.com/file/ovNwaN6/emblem-ssd.tar.gz"&gt;custom emblem&lt;/a&gt; in &lt;code&gt;~/.local/share/icons/hicolor/24x24/emblems&lt;/code&gt; and a &lt;a href="http://projects.gnome.org/nautilus-python/"&gt;Nautilus-Python&lt;/a&gt; extension saved as &lt;code&gt;~/.nautilus/python-extensions/ssd-emblem.py&lt;/code&gt; that checks for parallel files in &lt;code&gt;/home/.ssd&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import os.path
import nautilus

class SsdEmblem(nautilus.InfoProvider):
    def __init__(self):
        pass

    def update_file_info(self, file):
        filepath = os.path.realpath(file.get_location().get_path())
        if filepath.find('/home/ak/') == 0 and os.path.exists(filepath.replace('/home/ak/', '/home/.ssd/ak/',1)) == True:
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="thoughts"&gt;Thoughts&lt;/h2&gt;

&lt;p&gt;After using this for a week I’m pleased with the results. There is a noticeable improvement in login times compared with my previous configuration that stored the entire home folder on a HDD.&lt;/p&gt;

&lt;p&gt;I would like there to be an easy way to switch the storage back end of individual files and folders, ideally via a Nautilus context menu item. This looks possible via &lt;a href="http://aufs.sourceforge.net/aufs2/man.html#User's%20Direct%20Branch%20Access%20(UDBA)"&gt;UDBA&lt;/a&gt;, but there are several potential quirks involved that I haven’t spent the time to thoroughly investigate.&lt;/p&gt;

&lt;p&gt;Obviously, more work would need to be done for this to be scalable to multi-user environments or to be compatible with encrypted home folders. I have no plans to develop these areas.&lt;/p&gt;</content>
  </entry>
</feed>

