LastCollage
Description
LastCollage is a bash script that generates a collage of album covers from your last.fm profile. It runs on linux and Mac and on Windows with cygwin.
Here is the kind of picture it produces:
It makes a really nice desktop wallpaper. Check out the gallery to see many other examples.
Inspiration: Gijsco's Last.fm Desktop Generator for the use of last.fm data, and Jamie Zawinski's webcollage, for the collage.
NEW: please check wallpaperfm.py, a new script which includes the collage and two more wallpapers (and is much faster) (and has less requirements).
Files
You need these 2 files (lastCollage and parse_pictures.xsl).
lastCollage [Show source]
#!/bin/bash
# Generate a collage with the covers of your favorite albums according to your last.fm profile.
# Inspired by Gijsco's Last.fm Desktop Generator (http://build.last.fm/item/37) and Jamie Zawinski's webcollage (http://www.jwz.org/webcollage/)
# Requires imagemagick and xsltproc (fairly standard)
# Also requires parse_pictures.xsl which you should find wherever you found this script.
#
# Usage:
# Copy the script and parse_pictures.xsl in a directory of your choice.
# Running the script from the command line will use the default values.
# Use ./lastCollage -u yourlastfmusername to make the collage with your profile and default values. See output for a description of the parameters.
# You can delete the sub-directory lccache if you want to start afresh. It'll be re-created.
#
# More explanations at http://ledazibao.free.fr/lastCollage
#
# Koant (http://www.last.fm/user/Koant/)
# Thu 15 May 2008
#
# Possible improvements, if you feel like it:
# . better random; RANDOM produces extremely correlated numbers. Had to re-seed after each use.
# . use other feeds than top albums
# . speed things up
# Default Settings
username='Koant' # last.fm username
type='overall' # 3month, 6month, 12month or overall
AlbumSize=300 # size of each album cover, in pixel
Final=1280x1024 # dimension of the final image, in pixel
AlbumOpacity=80 # from 0 to 100 (100 is completely opaque, the cover won't blend into the background)
GradientSize=20 # in pixel. Transparency gradient size for an album. The bigger the larger the area of the cover is transparent.
ImageOpacity=70 # darken the final image, from 0 to 100 (0 is completely black)
iteration=1 # Number of times the algorithm is run. 2 is usually enough
cache='lccache' # name of cache directory
# Checking out dependencies (bc, ImageMagick and xsltproc)
if which bc > /dev/null; then echo "bc is installed."
else
echo
echo " bc is not installed! I can't proceed."
exit
fi
if which convert > /dev/null; then echo "ImageMagick is installed."
else
echo
echo " ImageMagick is not installed! I can't proceed."
exit
fi
if which xsltproc > /dev/null; then echo "xsltproc is installed."
else
echo
echo " xsltproc is not installed! I can't proceed."
exit
fi
# Reading out parameters
# from http://www.ibm.com/developerworks/library/l-bash-parameters.html by Ian Shields
while getopts ":u:O:o:n:f:c:a:t:g:i:" optval "$@"
do
case "$optval" in
"u")
username=$OPTARG
;;
"O")
ImageOpacity=$OPTARG
;;
"o")
AlbumOpacity=$OPTARG
;;
"n")
imagename=$OPTARG
;;
"f")
Final=$OPTARG
;;
"c")
Canvas=$OPTARG
;;
"a")
AlbumSize=$OPTARG
;;
"t")
type=$OPTARG
;;
"g")
GradientSize=$OPTARG
;;
"i")
iteration=$OPTARG
;;
"?")
echo "Unknown option $OPTARG, ignored."
;;
":")
echo "No argument value for option $OPTARG"
;;
*)
# Should not occur
echo "Unknown error while processing options"
;;
esac
done
# processing and reverting to default if needs be
imagename=${imagename:-"$username"} # Default imagename is username
Canvas=${Canvas:-"$Final"} # Default Canvas size is Final's
FinalWidth=$(echo $Final | cut -d'x' -f1)
FinalHeight=$(echo $Final | cut -d'x' -f2)
CanvasWidth=$(echo $Canvas | cut -d'x' -f1)
CanvasHeight=$(echo $Canvas | cut -d'x' -f2)
echo "I'll be using the following parameters:-"
echo " (u) Username: $username "
echo " (t) How far back: $type (should be one of '3month', '6month' or '12month')"
echo " (n) Name of the picture: $imagename.jpg"
echo " (c) Size of the canvas: "$CanvasWidth"x"$CanvasHeight
echo " (f) Size of the final image: "$FinalWidth"x"$FinalHeight
echo " (a) Album size: $AlbumSize pixels"
echo " (o) Album opacity: $AlbumOpacity (in [0 100]. 100 means no transparency)"
echo " (g) Gradient size: $GradientSize pixels (should be between 1 and half the album size)"
echo " (O) Darkness of final image: $ImageOpacity (in [0 100]. 0 makes the image completely black)"
echo " (i) Number of iterations: $iteration"
echo "(use the letter in parentheses to change the value, as argument to the script)"
# URL
URL="http://ws.audioscrobbler.com/1.0/user/$username/topalbums.xml?type=$type"
# Make script crontab friendly:
cd $(dirname $0)
# Create cache if doesn't exist already
if test ! -d $cache
then
mkdir $cache
fi
# returns a random integer between $1 and $2
function get_rand {
RANDOM=$(dd if=/dev/urandom count=2 2> /dev/null | cksum | cut -f1 -d" ") # re-seed
l=`expr $2 - $1`
r=`echo "r=($l+1)* ($RANDOM/32767); if (r<1) print 0 else print r" |bc -l |cut -d'.' -f1`
echo `expr $r + $1`
}
#add + sign if needs be
function add_sign {
if ! echo $1 | grep -e "-" >/dev/null
then
echo +$1
else
echo $1
fi
}
# Create picture log if doesn't exist already
# (list of all URL of pics that have been downloaded)
if test ! -f $cache/pixlog
then
touch $cache/pixlog
chmod a+w $cache/pixlog
echo "http://cdn.last.fm/depth/catalogue/noimage/cover_med.gif" >> $cache/pixlog # empty album cover, won't be dl.
fi
# fetching the list
echo "Fetching the list of albums..."
rm -f albumlist.xml
wget -q -O albumlist.xml "$URL"
# Download new pictures only
echo "Downloading new pictures..."
urllist=$(xsltproc parse_pictures.xsl albumlist.xml 2> /dev/null)
touch filelist
for url in $urllist
do
if ! grep "$url" $cache/pixlog > /dev/null # check whether it's already in the cache
then # download and add to the list if need be
echo $url
wget -q -P $cache "$url" && echo $url >> $cache/temp.log
cat $cache/temp.log >> $cache/pixlog
rm -f $cache/temp.log
fi
done
# list of pictures to actually use, in random order
echo "Preparing the list of pictures..."
rm -f tempfilelist
touch tempfilelist
for url in $urllist # shuflles the list of pictures
do
x=$(get_rand 1000 9999)
echo $x ${url##*/} >> tempfilelist
done
sort tempfilelist | cut -d' ' -f2 > filelist
rm -f tempfilelist
# Useful quantities
Dx=`echo "r=($CanvasWidth - $FinalWidth)/2; if (r*r<1) print 0 else print r" |bc -l |cut -d'.' -f1`
Dy=`echo "r=($CanvasHeight - $FinalHeight)/2; if (r*r<1) print 0 else print r" |bc -l |cut -d'.' -f1`
startx=`expr 0 - $Dx`
starty=`expr 0 - $Dy`
rangex=`expr $FinalWidth + $Dx - $AlbumSize`
rangey=`expr $FinalHeight + $Dy - $AlbumSize`
# prepare the mask
echo "Preparing the mask..."
intensity=`echo "254*($AlbumOpacity/100)+1" | bc -l | cut -d'.' -f1`
ln=`expr $AlbumSize - $GradientSize`
convert -size "$AlbumSize"x"$AlbumSize" xc:black -stroke rgb\($intensity,$intensity,$intensity\) -fill rgb\($intensity,$intensity,$intensity\) -draw "roundRectangle $GradientSize,$GradientSize $ln,$ln $GradientSize,$GradientSize" -gaussian "$GradientSize"x"$GradientSize" mask.png
# make the background
echo "Preparing the background..."
if test -f $imagename.jpg # use former image as background if exists. Resize if necessary
then
echo "$imagename.jpg exists, I'll paste the new collage on it."
convert $imagename.jpg -resize "$FinalWidth"x"$FinalHeight"! background.png
else
convert -size "$FinalWidth"x"$FinalHeight" xc:black background.png
fi
# add images from list
echo "Add images (might take a while)..."
for ((i=1;i<=$iteration;i+=1));
do
echo " iteration $i of $iteration"
while read fn
do
if test -f $cache/$fn
then
# echo $cache/$fn
convert -resize "$AlbumSize"x"$AlbumSize"! $cache/$fn mask.png +matte -compose CopyOpacity -composite temp.png
x=$(add_sign $(get_rand $startx $rangex))
y=$(add_sign $(get_rand $starty $rangey))
convert background.png \( temp.png -repage "$x""$y" \) -flatten background.png
fi
done < filelist
done
# darken and jpg
echo "Darken the final image..."
intensity=`echo "254*($ImageOpacity/100)+1" |bc -l | cut -d'.' -f1`
convert background.png \( -size "$FinalWidth"x"$FinalHeight" xc:rgb\($intensity,$intensity,$intensity\) \) +matte -compose Multiply -composite $imagename.jpg
# cleaning up
rm -f background.png mask.png temp.png filelist albumlist.xml
echo "Finished! Image saved in $imagename.jpg"
parse_pictures.xsl [Show source]
<?xml version="1.0"?>
<stylesheet version="1.0"
xmlns="http://www.w3.org/1999/XSL/Transform">
<output method="text"/>
<template match="/">
<apply-templates select="/topalbums/album/image"/>
</template>
<template match="image">
<value-of select="large"/><text> </text>
</template>
</stylesheet>
Requirements
- You need 2 files to use it: the script itself (lastCollage) and parse_pictures.xsl.
- The script uses ImageMagick, xsltproc and bc. This is fairly standard stuff. Install them if you don't have them.
- I've also noticed that wget is not installed by default on Mac.
If you're on a Mac and have macports installed, run this before installing the script, to install wget and ImageMagick:
sudo port install wget
sudo port install imagemagick
If you're on Windows with cygwin, you'll probably need to install wget, xsltproc and bc, since they don't seem to be there by default.
Simply run setup.exe and select them for installation.
You'll find wget in the 'Web' category, xlstproc in the 'Interpreters' category (as 'libxslt') and bc in the 'Utils' category (as 'bc the GNU numeric processing language and reverse Polish calculator').
Installation
Here is one way to install lastCollage from the command line:
mkdir lastCollage
cd lastCollage
wget http://ledazibao.free.fr/lastCollage/lastCollage
wget http://ledazibao.free.fr/lastCollage/parse_pictures.xsl
chmod a+x lastCollage
lastCollage is then run with ./lastCollage and will produce a jpg file.
For a limited period: the web version, no install necessary.
Usage
./lastCollage
will run the script with the default values:
I'll be using the following parameters:-
(u) Username: Koant
(t) How far back: overall (should be one of '3month', '6month','12month' and 'overall')
(n) Name of the picture: Koant.jpg
(c) Size of the canvas: 1280x1024
(f) Size of the final image: 1280x1024
(a) Album size: 300 pixels
(o) Album opacity: 80 (in [0 100]. 100 means no transparency)
(g) Gradient size: 20 pixels (should be between 1 and half the album size)
(O) Darkness of final image: 70 (in [0 100]. 0 makes the image completely black)
(i) Number of iterations: 1
You can change the default values by passing the following parameters:
-u username
-t one of '3month', '6month', '12month', 'overall'
-n name of the picture
-c size of the canvas as WidthxHeight
-f size of the final image as WidthxHeight
-a album size in pixels
-o album opacity, in [0 100]. 100 means no transparency.
-g gradient size in pixels
-O darkness of final image, in [0 100]. 0 makes the image completely black.
-i number of iterations, 1 by default.
For example, if you want to have quick try with a smaller final image, for example 640x480, and for the user RJ, use the following command. We use a smaller album size as well, say 150 pixels.
./lastCollage -u RJ -f 640x480 -a 150
You need an internet connection, for the program to fetch your profile.
When it's finished, a jpg image (username.jpg by default) will contain the collage.
You should be able to use the script in a cron job.
/usr/bin/gconftool -t str -s /desktop/gnome/background/picture_filename "yourpicture" will update the wallpaper in Gnome. (well, almost)
Tips
By default, the script uses a uniformly black background, unless the picture $imagename.jpg exists.
In the latter case, this is used as a background.
So if you think your collage looks a bit 'empty' the first time around (there're only 50 albums in the feed, and they don't cover the whole screen due to the overlap), run the script again; it'll look 'fuller' and nicer.
You can also use the '-i' switch to specify how many runs through the album list you want.
Note that '-i 2' is not equivalent to running lastCollage twice in a row, since the first batch is not darkened before applying the second one.
The canvas is the surface which the album covers are pasted on. By default, it has the same dimensions as the final image.
If you want to have a black border around the collage, use a canvas that is smaller than the final image.
If on the other hand, you'd rather not have any borders, use a canvas bigger than the final image.
It has the effect of generating the collage on a bigger surface and cropping the central part to produce the final image.
A good dimension for the canvas in this case is final image size + album size; at a minimum, half of a cover is pasted on the final image.
Examples:
Canvas is smaller than final image |
Canvas is bigger than final image |
Canvas is smaller than final image on the y-axis, and bigger than final image on the x-axis |
|
|
|
Contact
You can contact me at http://www.last.fm/user/Koant or in the last.fm's forum thread.
Comments, questions and suggestions welcome!
Last changes
Thu 15 May
- I've just realized that last.fm also provide the overall top albums - actually that's their default. It's now the default in the script as well.
Tue 29 Apr
- Checks if bc, ImageMagick and xsltproc are installed. Stops and complains if anything is missing. Thanks to holloway87 for the suggestion.
Mon 31 Mar
- Some cosmetic changes - iterations are now shown when adding images.
Sun 30 Mar
- The shuffled list of albums is now copied multiple times when the switch '-i' is used, instead of reshuffling i times.
This means that it is now less likely to see the same album twice in the final image.
The proof of that is left as an exercise to the reader.
Thu 27 March 2008
- Replaced 'seq' by a for loop, for better compatibility with Mac, following Peddro's remark.
Mon 24 March 2008
- Added the switch '-i' for controlling the number of iterations.
Sun 23 March 2008
- Parameters are now passed through the command line, so there is no need to edit the script anymore.
It should make life easier for Windows users since there seemed to be problems with extra '\r' and other oddities.
Thur 20 March 2008
- Album covers are now resized to a square.
Wed 19 March 2008
- Added a couple of functions (get_rand and add_sign)
- Added canvas after juxi's suggestion.