Meta Artists & Rolling Queue patch
Richard Jaques <[email protected]> Sun, 04 Apr 2004 21:16:42 +0100
| Newsgroups | gmane.comp.audio.bossogg.devel |
|---|---|
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format.
--------------030808070403010308030006
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit
Hello all,
I have a patch that makes a few tweaks to boss, at the moment they are
very unstable and I have sent this just so Wishy can see what I am
blathering on about.
The summary is
Boss:
- Added rolling queue mode
- Meta-artist work...
* If a song is added with a meta-artist then that meta-artist owns
* the album
* Get Artist now returns only artists that have at least one album
* & displays
weather the artist is a "meta-artist" or not.
bosync:
- Tweaked interactive mode.
- Fixed problem deleting files
- Modified meta-artist support
---------
Rolling queue mode is an alternative queue mode, which can be modified
through the config file (which I havn't upversioned BTW). Enabling it
provides a queue mode where by once a track is played it is removed from
the queue, so that only track that are due to be played exist in the
queue, more like a pub jukebox.
I have made some tweaks to bosync's interactive mode that now have
"default" values that are those extracted by boss itself.
bosync encodes files scheduled for deletion.
And now onto meta artist support:
My test music folder is layed out as so
music/
albums/
Aretha Frankin/
Aretha Now/
Greatest Hits Disc One
soundtrack/
Apocalypse Now- Disc 1
various/
All Woman 4
I populate the database with
bosync music/albums
bosync -a --METAARTIST Soundtracks music/soundtrack
bosync -a --METAARTIST Various music/various
Note the use of the -a flag for bosync to indicate adding to the
database.
Now in Geezer (the only "official" client I have tried this stuff under)
I get three Artists (Aretha Franklin, Soundtrack and Various) even
though there are many artists in the database. Selecting Various then
the album "All Woman 4" shows all the songs on the album All Woman,
complete with the correct artists.
Now for some terminology, I define meta-artists to be "Soundtrack" and
"Various", where each artist on the album "All Woman 4", Tina Turner,
Annie Lennox and Aretha Franklin are artists. In Aretha's case she owns
an albums so she is shown in the "Artists" view. Asking for all songs
by that artist return all of her songs on "Aretha Now" and "Greatest
Hits" and her single song on "All Woman 4".
So meta-artist do not have any songs only albums. Putting meta-artist
in the song table is just database denormalising (not always a bad
thing).
Wishy: if you don't like it, don't use it. It's just an idea at this
stage!
Jaques.
--------------030808070403010308030006
Content-Type: text/plain;
name="rpj-bosschanges.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="rpj-bosschanges.patch"
Only in dev/: Makefile
diff -ru bossogg-0.13.6/boss3/CommandInterface.py dev/boss3/CommandInterface.py
--- bossogg-0.13.6/boss3/CommandInterface.py 2004-04-02 02:44:26.000000000 +0100
+++ dev/boss3/CommandInterface.py 2004-04-04 18:40:00.024788328 +0100
@@ -102,7 +102,7 @@
self.player = player
self.dbh = dbh
- def artists(self, anchor=""):
+ def artists(self, anchor="", artistid = 0):
"""
Returns a list of all the artists in the
database. Anchor will only return
@@ -111,6 +111,7 @@
Parameters:
* anchor - string(optional)
+ * artistid - int(optional)
Returns:
* Array
@@ -119,7 +120,7 @@
* artistname - string
"""
#print self.dbh.listArtists(anchor)
- return self.dbh.listArtists(anchor)
+ return self.dbh.listArtists(anchor,artistid)
def albums(self, artistid=None, anchor=""):
"""
Only in dev/boss3: CommandInterface.pyc
diff -ru bossogg-0.13.6/boss3/Database.py dev/boss3/Database.py
--- bossogg-0.13.6/boss3/Database.py 2004-04-02 02:44:27.000000000 +0100
+++ dev/boss3/Database.py 2004-04-04 20:17:39.896952352 +0100
@@ -82,6 +82,7 @@
sqlite.Cursor.execute(self, SQL, *args)
except:
log.exception("SQL ERROR")
+ print SQL
if needlock and not self.nolock:
sql_lock.release()
log.debug("lock", "Release lock for database writes", stack=1)
@@ -274,24 +275,35 @@
result = {"userid":row['userid'],"authlevel":['authlevel']}
return result
- def listArtists(self, anchor=""):
+ def listArtists(self, anchor="", artistid=0):
result = []
cursor = self.conn.cursor()
- SQL = "SELECT artistid, artistname FROM artists "
+
+ if (artistid != 0):
+ SQL = 'select a.artistid, a.artistname, -1 as "s.metaartistid" FROM artists a where a.artistid = %d'%artistid
+ else:
+ ##SQL now returns only artists that have at least one album, those that have
+ ##represent "meta artists" have the meta-artist flag set
+ SQL = "select distinct a.artistid, a.artistname, s.metaartistid from songs s left join artists a on a.artistid = s.metaartistid or (s.metaartistid = -1 and a.artistid = s.artistid)"
+
if (anchor != None and anchor != ""):
- SQL += "WHERE artistname like '%s%%' " % anchor
- SQL += "ORDER BY lower(artistname) ASC"
+ SQL += "WHERE a.artistname like '%s%%' " % anchor
+ SQL += "ORDER BY lower(a.artistname) ASC"
cursor.execute(SQL)
+
for row in cursor.fetchall():
log.debug("sqlresult", "Row: %s", row)
- if row['artistname'] != '':
- result.append({"artistid":row['artistid'],"artistname":row['artistname']})
+ if row['a.artistname'] != '':
+ if row['s.metaartistid'] == -1:
+ metaartist="False"
+ else:
+ metaartist="True"
+ result.append({"artistid":row['a.artistid'],"artistname":row['a.artistname'],"metaartist":metaartist})
return result
def listAlbums(self, artistid=None, anchor=""):
result = []
cursor = self.conn.cursor()
- #Look for real albums first and stick them at the top of the list
if artistid != None:
SQL = "SELECT albumid, albumname, year FROM albums WHERE artistid = %i " % artistid
@@ -302,22 +314,12 @@
for row in cursor.fetchall():
log.debug("sqlresult", "Row: %s", row)
result.append({"albumid":row['albumid'],"albumname":row['albumname'],"albumyear":row['year'],"metaartist":0})
- #Now look for metaartist related albums
- SQL = "SELECT a.albumid, a.albumname, a.year FROM albums a INNER JOIN songs s ON a.albumid = s.albumid WHERE s.metaartistid = %s " % artistid
-
- if (anchor != None and anchor != ""):
- SQL += "AND a.albumname like '%s%%' " % anchor.replace("'", "\\'")
- SQL += "ORDER BY a.year, lower(a.albumname) ASC"
- cursor.execute(SQL)
- for row in cursor.fetchall():
- log.debug("sqlresult", "Row: %s", row)
- result.append({"albumid":row['a.albumid'],"albumname":row['a.albumname'],"albumyear":row['a.year'],"metaartist":1})
else:
SQL = "SELECT ar.artistid, ar.artistname, a.albumid, a.albumname, a.year FROM albums a INNER JOIN artists ar ON ar.artistid = a.artistid ORDER BY ar.artistname, a.year, lower(a.albumname) ASC"
cursor.execute(SQL)
for row in cursor.fetchall():
log.debug("sqlresult", "Row: %s", row)
- result.append({"artistid":row['ar.artistid'],"artistname":row['ar.artistname'],"albumid":row['a.albumid'],"albumname":row['a.albumname'],"albumyear":row['a.year'],"metaartist":0})
+ result.append({"artistid":row['ar.artistid'],"artistname":row['ar.artistname'],"albumid":row['a.albumid'],"albumname":row['a.albumname'],"albumyear":row['a.year']})
return result
def listPlaylists(self):
@@ -416,9 +418,9 @@
SQL = "SELECT s.songid, s.artistid, ar.artistname, s.albumid, s.tracknum, s.songname, s.filename, s.filesize, s.bitrate, s.songlength, s.timesplayed, a.albumname, a.year, s.metaartistid, m.artistname"
if (playlistid != None and playlistid != ""):
SQL += ", p.indexid"
- SQL += " FROM songs s, albums a, artists ar"
+ SQL += " FROM songs s, albums a, artists ar "
if (playlistid != None and playlistid != ""):
- SQL += ", playlistdata p"
+ SQL += ", playlistdata p "
SQL += " LEFT OUTER JOIN artists m on s.metaartistid = m.artistid WHERE a.albumid = s.albumid and ar.artistid = s.artistid "
if (albumid != None and albumid != ""):
SQL += "AND s.albumid = %i " % albumid
@@ -428,7 +430,7 @@
SQL += "AND p.playlistid = %i and p.songid = s.songid " % playlistid
if (anchor != None and anchor != ""):
SQL += "AND s.songname like '%s%%' " % anchor
- SQL += "ORDER BY"
+ SQL += " ORDER BY"
if playlistid != None and playlistid != "":
SQL += " p.indexid,"
SQL +=" a.year, lower(a.albumname), s.tracknum, s.songname"
@@ -450,9 +452,8 @@
somesong = {"songid":row['s.songid'],"artistid":row['s.artistid'],"albumid":row['s.albumid'],"songname":row['s.songname'],"filename":row['s.filename'],"filesize":row['s.filesize'],"songlength":row['s.songlength'],"tracknum":row['s.tracknum'],"timesplayed":timesplayed,"bitrate":row['s.bitrate'],"albumname":row['a.albumname'],"albumyear":row['a.year'],"artistname":row['ar.artistname']}
if 'p.indexid' in row:
somesong['indexid'] = row['p.indexid']
- if row['m.artistname'] != None and row['s.metaartistid'] != '-1':
+ if row['s.metaartistid'] != '-1':
somesong['metaartistid'] = row['s.metaartistid']
- somesong['metaartistname'] = row['m.artistname']
if getgenres == True:
somesong['genres'] = self.fillSongGenreHash(row['s.songid'])
result.append(somesong)
@@ -567,7 +568,11 @@
metaartistid = -1
if 'metaartistname' in song.keys():
metaartistid = self._getArtist(self.checkBinary(song['metaartistname']),True)
- albumid = self._getAlbum(self.checkBinary(song['albumname']), artistid, song['year'])
+ albumartistid = artistid
+ if (metaartistid != -1):
+ albumartistid = metaartistid
+
+ albumid = self._getAlbum(self.checkBinary(song['albumname']), albumartistid, song['year'])
songid = self._getNSong(self.checkBinary(song['songname']),artistid,self.checkBinary(song['filename']),song['tracknum'],albumid=albumid,year=song['year'],metaartistid=metaartistid, bitrate=song["bitrate"], songlength=song["songlength"])
else:
log.debug("import", "Could not get bitrate of song %s. Assuming bad file.", song["filename"])
Only in dev/boss3: Database.pyc
Only in dev/boss3: DatabaseSchema.pyc
Only in dev/boss3: Makefile
Only in dev/boss3: Player.pyc
Only in dev/boss3: UserManager.pyc
Only in dev/boss3: __init__.pyc
Only in dev/boss3: bocmd.pyc
Only in dev/boss3/bossao: .deps
Only in dev/boss3/bossao: .libs
Only in dev/boss3/bossao: Makefile
Only in dev/boss3/bossao: __init__.pyc
Only in dev/boss3/bossao: _bossao.la
Only in dev/boss3/bossao: _bossao.so
Only in dev/boss3/bossao: bossao.lo
Only in dev/boss3/bossao: bossao.o
Only in dev/boss3/bossao: bossao.pyc
Only in dev/boss3/bossao: bossao_wrap.lo
Only in dev/boss3/bossao: bossao_wrap.o
Only in dev/boss3/bossao: flac.lo
Only in dev/boss3/bossao: flac.o
Only in dev/boss3/bossao: mp3.lo
Only in dev/boss3/bossao: mp3.o
Only in dev/boss3/bossao: ogg.lo
Only in dev/boss3/bossao: ogg.o
Only in dev/boss3/bossao: oss_mixer.lo
Only in dev/boss3/bossao: oss_mixer.o
Only in dev/boss3/bossexceptions: EndOfQueueException.pyc
Only in dev/boss3/bossexceptions: __init__.pyc
Only in dev/boss3/metadata: .deps
Only in dev/boss3/metadata: .libs
Only in dev/boss3/metadata: Makefile
Only in dev/boss3/metadata: __init__.pyc
Only in dev/boss3/metadata: _id3.la
Only in dev/boss3/metadata: _id3.so
Only in dev/boss3/metadata: _id3_la-id3.lo
Only in dev/boss3/metadata: _id3_la-id3.o
Only in dev/boss3/metadata: _id3_la-id3_wrap.lo
Only in dev/boss3/metadata: _id3_la-id3_wrap.o
Only in dev/boss3/metadata: _id3_la-id3lib_wrapper.lo
Only in dev/boss3/metadata: _id3_la-id3lib_wrapper.o
Only in dev/boss3/metadata: _id3_la-vorbis.lo
Only in dev/boss3/metadata: _id3_la-vorbis.o
Only in dev/boss3/metadata: id3.pyc
diff -ru bossogg-0.13.6/boss3/util/ConfigFile.py dev/boss3/util/ConfigFile.py
--- bossogg-0.13.6/boss3/util/ConfigFile.py 2004-04-02 03:09:26.000000000 +0100
+++ dev/boss3/util/ConfigFile.py 2004-04-03 17:34:37.000000000 +0100
@@ -80,6 +80,14 @@
#change if volume changes aren't getting recognized.
mixerdevice: %(default_mixerdevice)s
+#queuetype. Two types: standard and rolling.
+# standard adds to the current queue and the player
+# has a concept of the current song.
+# rolling removes songs from the top of the queue once
+# they are played so that the current song is always the
+# top of the playlist, more like a pub jukebox.
+queuetype: %(default_queuetype)s
+
[SHOUT]
#Set to 1 to enable icecasting
@@ -152,6 +160,7 @@
"default_audiodriver" : self.cfgparser.get("DEFAULT", "audiodriver"),
"default_mixertype" : self.cfgparser.get("DEFAULT", "mixertype"),
"default_mixerdevice" : self.cfgparser.get("DEFAULT", "mixerdevice"),
+ "default_queuetype" : self.cfgparser.get("DEFAULT", "queuetype"),
"shout_enable" : self.cfgparser.get("SHOUT", "enable"),
"shout_host" : self.cfgparser.get("SHOUT", "host"),
@@ -183,7 +192,8 @@
self.cfgparser.set("DEFAULT","audiodriver", "auto")
self.cfgparser.set("DEFAULT","mixertype","oss"),
self.cfgparser.set("DEFAULT","mixerdevice","/dev/mixer"),
-
+ self.cfgparser.set("DEFAULT","queuetype","standard")
+
self.cfgparser.set("SHOUT", "enable", "0")
self.cfgparser.set("SHOUT", "host", "127.0.0.1")
self.cfgparser.set("SHOUT", "user", "user")
Only in dev/boss3/util: ConfigFile.pyc
Only in dev/boss3/util: Logger.pyc
diff -ru bossogg-0.13.6/boss3/util/Queue.py dev/boss3/util/Queue.py
--- bossogg-0.13.6/boss3/util/Queue.py 2004-04-02 02:44:29.000000000 +0100
+++ dev/boss3/util/Queue.py 2004-04-04 15:18:49.465792216 +0100
@@ -36,9 +36,13 @@
self.items.append(item)
def remove(self, item):
if self.manager.usingendofqueue or ((item != self.currentindex) and ( item >= 0) and (item < len(self.items))):
- self.items.pop(item)
- if (item < self.currentindex):
- self.currentindex=self.currentindex-1
+ if (self.manager.session["cfg"].get("queuetype") != "rolling" or internalcall):
+ if(item < self.currentindex):
+ self.currentindex=self.currentindex-1
+ self.items.pop(item)
+ else:
+ #rolling queue adjustment
+ self.items.pop(item+1)
def clear(self):
self.items=[]
self.currentindex=0
@@ -48,7 +52,12 @@
print self.items
def getSongIDs(self):
result = []
- for i in self.items:
+ if (self.manager.session["cfg"].get("queuetype") != "rolling") or self.manager.usingendofqueue == 1:
+ songsweshow = self.items
+ else:
+ #we don't show the song we are currently on in the queue
+ songsweshow = self.items[1:]
+ for i in songsweshow:
if 'songid' in i:
result.append(int(i['songid']))
return result
@@ -70,15 +79,27 @@
return None
def next(self):
self.currentindex += 1
+ #if we are in rolling queue mode then delete the top entry if it exists,
+ if (self.manager.session["cfg"].get("queuetype") == "rolling"):
+ if len(self.items) > 0:
+ self.items.pop(0)
+ self.currentindex = 0
+
if self.currentindex >= len(self.items):
self.currentindex = len(self.items)
return None
else:
- return self.items[self.currentindex]
+ return self.items[self.currentindex]
def prev(self):
if self.currentindex > 0:
self.currentindex=self.currentindex-1
def move(self,oldindex,newindex):
+
+ if (self.manager.session["cfg"].get("queuetype") == "rolling"):
+ #correct numbers
+ oldindex = oldindex + 1
+ newindex = newindex + 1
+
if (newindex > -1) and ( newindex < len(self.items)):
if (oldindex > -1) and ( oldindex < len(self.items)):
tmp=self.items[newindex]
Only in dev/boss3/util: Queue.pyc
Only in dev/boss3/util: Session.pyc
Only in dev/boss3/util: UTFstring.pyc
Only in dev/boss3/util: __init__.pyc
Only in dev/boss3/util: bosslog.pyc
Only in dev/boss3/util/logging: __init__.pyc
Only in dev/boss3: version.pyc
Only in dev/boss3/xmlrpc: Auth.pyc
Only in dev/boss3/xmlrpc: Control.pyc
Only in dev/boss3/xmlrpc: DB.pyc
Only in dev/boss3/xmlrpc: Edit.pyc
Only in dev/boss3/xmlrpc: Info.pyc
diff -ru bossogg-0.13.6/boss3/xmlrpc/List.py dev/boss3/xmlrpc/List.py
--- bossogg-0.13.6/boss3/xmlrpc/List.py 2004-04-02 02:44:27.000000000 +0100
+++ dev/boss3/xmlrpc/List.py 2004-04-04 19:20:56.419359592 +0100
@@ -32,8 +32,8 @@
list("artists"): Displays a list of all the artists in the
- database. Ordered in alphabetical order.
-
+ database. Ordered in alphabetical order. Note that this will
+ only return artists with one or more albums.
Parameters:
* anchor - string(optional)
@@ -43,10 +43,21 @@
* artistname - string(base64)
* artistid - int
+ list("artist"): Return info about a single artist in the
+ same format as list("artists")
+
+ Parameters
+ * artistid - int
+
+ Returns:
+ * Array
+ * Struct
+ * artistname - string(base64)
+ * artistid - int
list("albums"): Returns a list of all the albums in the
database for the given artistid. Anchor will only return
- albumnames that start with the given anchor string.
+ albumnames that start with the given anchor string.
Parameters:
* artistid - int
@@ -58,8 +69,6 @@
* albumname - string(base64)
* albumid - int
* albumyear - int
- * metaartist - int (1 = True, 0 = False)
-
list("songs"): Returns a list of all the albums in the
database for the given artistid or albumid. Anchor will
@@ -260,6 +269,13 @@
if "artistname" in i:
i['artistname'] = UTFstring.encode(i['artistname'])
return tmp
+ if (cmd == "artist"):
+ tmp = None
+ tmp = cmdint.list.artists("",args[0])
+ for i in tmp:
+ if "artistname" in i:
+ i['artistname'] = UTFstring.encode(i['artistname'])
+ return tmp
elif (cmd == "albums"):
tmp = None
#if (len(args)>0):
Only in dev/boss3/xmlrpc: List.pyc
Only in dev/boss3/xmlrpc: Load.pyc
Only in dev/boss3/xmlrpc: Set.pyc
Only in dev/boss3/xmlrpc: SimpleXMLRPCServer.pyc
Only in dev/boss3/xmlrpc: Util.pyc
Only in dev/boss3/xmlrpc: XmlRpcServer.pyc
Only in dev/boss3/xmlrpc: __init__.pyc
Only in dev/boss3/xmlrpc: bossxmlrpclib.pyc
Only in dev/boss3/xmlrpc: bossxmlrpcserver.pyc
Only in dev/boss3/xmlrpc: xmlrpclib.pyc
diff -ru bossogg-0.13.6/bosync dev/bosync
--- bossogg-0.13.6/bosync 2004-04-02 04:25:54.000000000 +0100
+++ dev/bosync 2004-04-04 16:32:53.207240832 +0100
@@ -96,6 +96,7 @@
( "", "ARTIST", "artist", "Force all songs to have this artist. Ignore matched"),
( "", "ALBUM", "album", "Force all songs to have this album. Ignore matched"),
( "", "TRACK", "track", "Force all songs to have this track number. Ignore matched"),
+( "", "METAARTIST", "metaartist","Force all songs to have this meta-artist. Ignore matched"),
)
@@ -209,8 +210,16 @@
files[filename] = st[ST_MTIME]
return files
+def getInput(prompt, default):
+ ##we modify the prompt so that it of the form
+ ## prompt [default]:
+ returnVal = raw_input(" > " + prompt + " [" + default + "]:")
+ if (returnVal == ""):
+ return default
+ else:
+ return returnVal
-def file_info(file):
+def file_info(file, metadata):
global comp
info = { }
tmp = { 'artistname':'',
@@ -233,11 +242,10 @@
if match:
regex[2] += 1
info.update(match.groupdict())
- # Update with any info we want to force
- info.update(config["force"])
return info
log.info("File does not match any format: %s", file)
else:
+ print file
try:
import readline
readline.parse_and_bind("tab: complete")
@@ -248,20 +256,23 @@
if readline:
readline.set_completer(comp.artist)
- info["artistname"] = raw_input("Album Artist Name: ")
+ ##RPJ: I am confused here....
+ ## I thought that this was supposed to be the Song Artist Name
+ info["metaartistname"] = getInput("Album Artist Name", metadata["artistname"])
if readline:
- comp.current_artist = info["artistname"]
+ comp.current_artist = info["metaartistname"]
readline.set_completer(comp.album)
- info["albumname"] = raw_input("Album Name: ")
+ info["albumname"] = getInput("Album Name", metadata["albumname"])
if readline:
- comp.add_album(info["artistname"], info["albumname"])
+ comp.add_album(info["metaartistname"], info["albumname"])
readline.set_completer(None)
- info["year"] = int(raw_input("Year: "))
- info["tracknum"] = int(raw_input("Track Number: "))
- info["songname"] = raw_input("Song Title: ")
+ info["year"] = int(getInput("Year", str(metadata["year"])))
+ info["tracknum"] = int(getInput("Track Number", str(metadata["tracknum"])))
+ info["songname"] = getInput("Song Title", metadata["songname"])
if readline:
readline.set_completer(comp.artist)
- info["metaartistname"] = raw_input("Song Artist Name: ")
+ ##RPJ: and this the Album Artists name.
+ info["artistname"] = getInput("Song Artist Name", metadata["artistname"])
return info
@@ -310,14 +321,18 @@
cnt += 1
progress_meter(cnt, total)
time.sleep(0.02)
- metadata = rpc.db("getmetadata", UTFstring.encode(file))
- #import pprint
- #pprint.pprint(metadata)
+ metadata = rpc.db("getmetadata", UTFstring.encode(file))
for key in metadata.keys():
metadata[key] = UTFstring.decode(metadata[key])
- sinfo = file_info(file)
+ sinfo = file_info(file, metadata)
metadata.update(sinfo)
metadata["filename"] = file
+
+ # Update with any info we want to force
+ metadata.update(config["force"])
+
+ #import pprint
+ #pprint.pprint(metadata)
additions.append(metadata)
log.debug("import", "Starting song import %d", time.time())
@@ -347,8 +362,12 @@
if del_files:
display("Deleting files: %d files removed from database" % len(del_files))
+ #encode all the filenames first
+ del_tmp = []
+ for tmpfile in del_files:
+ del_tmp.append(UTFstring.encode(tmpfile))
if not config["noop"]:
- rpc.db("importdelete", del_files)
+ rpc.db("importdelete", del_tmp)
if not config["noop"]:
rpc.db("importend")
@@ -482,6 +501,8 @@
config["force"]["tracknum"] = arg
if opt in ("--GENRE",):
config["force"]["genrename"] = arg
+ if opt in ("--METAARTIST",):
+ config["force"]["metaartistname"] = arg
if opt in ("-i", "--interactive"):
config["interactive"] = True
Only in dev/: config.h
Only in dev/: config.log
Only in dev/: config.status
Only in dev/: libtool
Only in dev/: stamp-h1
--------------030808070403010308030006--
-------------------------------------------------------
This SF.Net email is sponsored by: IBM Linux Tutorials
Free Linux tutorial presented by Daniel Robbins, President and CEO of
GenToo technologies. Learn everything from fundamentals to system
administration.http://ads.osdn.com/?ad_id=1470&alloc_id=3638&op=click