MP3 collection filesystem structure
The reason
I recently moved away from amarok and started using Exaile as my favorite audio player. I found out that Exaile automatically displays the cover art when it finds a “cover.jpg” in the album folder. Maybe I’ll start a ‘which-player-supports-what’ post some day but don’t hold your breath
I was very excited about this because my tast in music is ‘not mainstream’ to say the least so all those cover fetcher tools leave a lot of gaps for my album art. Since I started to clean up a bit I wrote down the structure on a note to remember it once and for all. I thought I’d might as well publish it and so this post was born.
Folder setup
It’s all in folder “~/Music/” but that isn’t that important. The structure is set up as follows:
Artist/Album/
cover.jpg
back.jpg
info/ # For stuff like lyrics.txt, etc.
bookletX.jpg # booklet images.
[back.jpg] # if info/ exists this file moves here.
If it’s a multiple disc album the CD directories are called “cd1″, “cd2″, so smalls and no spaces. Then the info directory can stay on album-top level, but I place a copy of cover.jpg in all cdX directories so Exaile displays them.
Then if the album is a compilation it’s placed in this directory (got this from iTunes
.
~/Music/Compilations/
You can find your Artists tag based because your player indexes those anyway. Assuming you use the tracks artist and not “Various artists”.
Track file naming
Then for naming the MP3 files themselves. When it’s a track in a full album:
01 - Trackname.jpg
When the track is in an album which is a compilation of various artists:
01 - Artist - Trackname.jpg
I noticed,when I used XMBC that it displays the tracks with “01. Trackname” even when naming them as above so that is at least understood without ambiguity.
When the track is just a single song:
Artist - Trackname.jpg
I have some old cassettes and MD’s I converted to MP3 in the past which have a Compilation suffix with “MD”, “AC”, “LP” at the end:
Trilok Gurtu - African Fantasy - Crazy Saints - Compilation MD.mp3
TAGs
Not to much hassle there. The only thing significant is when it’s a 1-pass MP3 of a full album the track title get’s some extra information. A full album suffix between brackets and a “cd1″ addition when it’s a multiple CD album.
Logical Progression cd1 (Full album)
Geeky
I think the subject of this post is totally geeky anyway but nevertheless .. Currently in the tags all words get first caps but with words being between brackets like the aformentioned (Full album) only the first word get’s capitalized. (Although I’m not sure yet what to do when it’s a subtitle or name).
Logical Progression cd1 (Full album)
I think that covers most of it for now. Hope it’s helpfull to anyone going bonkers over there MP3 collections.
Suggestions are welcome ofcourse.
GrtzG.
January 19, 2010
Tags: audio, clean, filestructure, mp3 Posted in: All ENGLISH articles, Technical
No Comments
Amarok kicked out the window. Hello Exaile!!
My god, what happend to Amarok. It used to be a great player. The latest version turned into a -non-intuitive-non-flexible-interface- audio player.
As Brian says it very eloquently in Songbird vs. Amarok: How not to design a GUI, and currently working on interface design myself. Amarok .. WTF?
After reading the Middle pane thread on the KDE forums I was baffled. Why not add customizable (drag ‘n drop) pane widgetry layout? And for the available widgets to fill that middle pane …
- Wiki info – How about a browser?!?!
- Track info – That’s already above the playlist.
- Album info – With file/folder view you can see what albums you have with one mouse click. (as was Amaroks strong point of critisism once towards iTunes).
- Lyrics – Do you think I’m studying lyrics while I’m working?
And some other fun features:
- The control buttons – Way to big, and worse the size is not adjustable. People are not born with visual impairments by default you know.
- After upgrading Amarok it decides to hang at 62% every time when it tries to scan my audio collection.
I heard a KDE member speak at a dutch conference not to long ago about how KDE has clean code and isn’t to bloated. Now I know why.
Exaile
Looking for that smooth flexible, clean player? then Exaile is the new big thing. It’s written in Python, supports OSD-notify out of the box. Supports plugins (and then some!). And ofcourse also the usuals like systray support, album cover fetching .. the works.
Props to the developers of Exaile!!
And for Amarok .. I am outta here!
GrtzG
January 4, 2010
Tags: audio, exaile, player, python Posted in: All ENGLISH articles, Diepe zucht, Technical
One Comment
Reis en bestemming
Je bent onderweg, ergens naar toe in de drukke stad. Je ziet alle mensen druk met hun ding. Alles waait voorbij als herfst bladeren op de wind. Ik ben wel onderweg maar eigenlijk ben ik er al. Ik zie de klaarheid van dingen. Hier is waar ik hoor te zijn.
- GP -
December 24, 2009
Tags: leven, vergankelijk, zijn Posted in: Leven en wijsheid
No Comments
Django: get distinct field selection
I recently have been dinging around in Django’s ORM because I needed a certain ‘fields … group by’ selection. I was very impressed by the logic that the Django developers put in there. Especially the joins and aggregations that have been released with version 1.1. However, after a hours of testing and googling, I was not able to solve my problem with it. I needed a distinct selection like this:
SELECT id, name FROM myapp_model WHERE owner_id='1' GROUP BY name;
This would have been (easily) possible except for the WHERE clause on ownership that screwed me. Since the SQL statement was that simple I decided to write a bypass function that get’s the data straight from the database. It’s readonly access and I dont do anything more with it then create a list of links. Even more so, from a KISS point of view this code is easier to read back after several months.
And for reusability I turned it in to a function. It takes the model and the distinct field, and for my purpose the needed ownership as parameter:
from django.db import connection
def get_latest_objects(model_name=None, distinct_field=None, user_id=None):
""" Get lastest distinct selection (as tuples) of a given model
"""
model_name = model_name.lower()
query = ("select id,%(distinct_field)s from myapp_%(model_name)s "
"where owner_id='%(user_id)s' "
"group by %(distinct_field)s;") % {
'model_name': model_name,
'distinct_field': distinct_field,
'user_id': user_id,
}
cursor = connection.cursor()
cursor.execute(query)
There’s not much to it, I just hope it helps you to avoid ‘the hard way’. And it helps to keeps your view methods more readable because you dont need anything more then this:
latest_objects = get_latest_objects(model_name='MyModel',
distinct_field='name',
user_id=request.user.id)
What is returned (latest_objects) is a list of tuples that you can unpack in your templates straight away. Note that there’s no error checking in there, because I know what I’m doing
Besides, worst case you get back an empty list and possible errors are caught when you write your tests. You do write those don’t you?
Grtz Gerard.
December 16, 2009
Tags: django, python Posted in: All ENGLISH articles, Technical
No Comments
Het antwoord op de Goudengids (en telefoonboek)
Lees het artikel even uit want onderaan staat waar je de gids kwijt kan. Al is het alleen maar om een statement te maken!
Je kent het vast wel, de bezorging van de ongevraagde Goudengids/Telefoonboek. Typisch zo’n actie van ‘we vinden geld belangrijker dan milieu’. Politiek en de ‘Captains of Industry’ hoef je niet te vragen, want daar is geen land mee te bezeilen. Nou dan doen we het gewoon zelf.
Het euvel in kwestie
De initiatiefnemer, die er voor zorgde dat de exemplaren in ons trappenhuis te vinden waren had nog een leuke actie. Ik kreeg een kaartje in de bus waarmee ik kosteloos een uitgebreide versie kon bestellen met daarop een antwoordnummer. He da’s mooi, dan sturen we de hele stapel terug. Mijn medebewoners vonden het een prima initiatief. Dus ben ik vandaag met mijn lieftallige buurvrouw even naar het postkantoor geweest. Ergo …
Wil je van de goudengids af? Gooi hem niet in de papierbak maar ga naar het postkantoor en geef hem af geadresseerd aan:
De Telefoongids BV
Antw. nummer 46490
1060 WDÂ Amsterdam
En als je van de komende exemplaren af wilt kan je uiteraard hier terecht: www.stopdetelefoongids.nl
Of stuur het ingevulde formulier Opzeggen recht op De Telefoongids & Gouden Gids op (Met dank aan een andere lieftallige buurvrouw).
(Milieu) bewuste Groeten!
October 29, 2009
Tags: burgerinitiatief, goudengids, oplossing Posted in: Diepe zucht, Diversen
2 Comments
Django: QueryScreener, a record level ownership development auditor
During the development stage of a Django app I’m working on I was exploring how to best implement rowlevel user ownerships. There are several ways to overwrite methods on object managers and even the Django admin interface is properly configurable to take a ownership from “request.user”.
But since wrongfull data disclosure is absolutely unacceptable I was still afraid that I would miss something somewhere. A nice example I ran into was populating a dropdown list in a form, where all records were visible instead of only those owned by the logged in user.
That got me thinking and eventually I wrote this small but sweet piece of middleware. Further elaboration below the code.
from django.db import connection
import re
"""
QueryScreener is a middleware development tool. This tool helps to avoid
unwanted data disclosure once you go into production.
It monitors queries to the models in your model_list and warns you when queries
are executed that do not contain a ownership where clause. And thus can be a
potential data disclosure hazard.
It requires a owner attribute in your model definition, e.g:
owner = models.ForeignKey(User, editable=False)
Edit the 'model_list' below for what models should be monitored. And add
QueryScreener to MIDDLEWARE_CLASSES in you settings.py
Note: This can/should only be used while running Django's testserver command
with e.g: ./manage.py runserver 192.168.1.81:8000
"""
class QueryScreener(object):
model_list = ['myapp_customer', 'myapp_order', 'myapp_product']
def process_view(self, request, view_func, view_args, view_kwargs):
if len(connection.queries) > 0:
query_parse(connection.queries, self.model_list, 'process_view')
def process_response(self, request, response):
if len(connection.queries) > 0:
query_parse(connection.queries, self.model_list, 'process_response')
return response
def query_parse(self, model_list, caller_process):
for query in connection.queries:
for modelname in model_list:
modelstring = 'FROM `'+modelname
if re.search(modelstring, query['sql']) and not \
re.search(r'^SELECT.\(1\).AS', query['sql']):
reg = re.compile(r'^SELECT.*WHERE.*owner.*(ORDER BY.*)?$',
re.DOTALL)
if not reg.search(query['sql']):
print ('<<< WARNING >>> Query execution without ownership '
'clause, called from "' + caller_process + '"')
print query['sql']
if re.search(r'^SELECT.\(1\).AS.`a`.FROM.*WHERE.*$', query['sql']):
print ('<<< Django Farted >>>')
# print query['sql']
Update1: The ‘ORDER BY’ in the regex needs to be optional.
Update2: Django does a ‘try update’ in save_base() without owner (seperated the select statement)
The comment in the code above sums up how to get it working. What it does is print a warning and the query in question that does not respect ownership. If enabled while developing just keep track of your console output for:
<<< WARNING >>> Query execution without ownership clause, called from "process_response"
Should you have suggestion, criticism, or words of admiration then please, do tell me
GrtzG
October 26, 2009
Tags: development, django, python Posted in: All ENGLISH articles, Technical
No Comments
Wouter Hamel in Watt
En voor mij de eerste keer in de grote zaal van voormalig Nighttown. Ziet er allemaal goed uit. Recyclebaar gebeuren hoor, drukvloer + dansen = stroom voor de verlichting, toilet spoelen met regen water … Goed bezig Watt! Verder zijn gelukkig ook de rook gelegenheden erg goed geregeld. Nou maar hopen dat Wouter er zin in heeft.
–timewarp–
Naar hij vertelde ‘on stage’ stonden ze de avond ervoor in Londen op de planken maar daar was niet veel van te merken. Vanuit ‘muzikaal vuurwerk’ opzicht was het wat aan de gladgestreken kant, maar de kwaliteit en combinatie van de overige bandleden was er niet minder om.
Na afloop was in de kleine zaal Caro Emerald en band te bewonderen. Een mixture van funk en ‘Chicago in sixties’ boogiewoogie. Lekker swingen dus!
Geslaagd avondje … En bedankt voor jullie heerlijke gezelschap meiden!
Info: Wouter Hamel, Caro Emerald
NB: deze post is geschreven met de Wordpress for iPhone client dus benieuwd hoe hij uit de verf komt
EDIT: Tochmaar even ge-update m.b.v. TinyMCE
October 2, 2009
Tags: muziek, rotterdam Posted in: D'r uit
No Comments
WoW: Loremaster achievement? … Really you should!
No clue what WoW in the title means? It’s an MMORPG and it’s called World of Warcraft. As for the rest of you, this post describes my 9 months in WoW and why I advise you to go for the Loremaster Achievement. Well, maybe not if you’re a total PvP or Raid freak but that’s a bit to chaotic for my taste
From 0 to 160 in 9 months
I’ll briefly run you through my 9 months up to the point where I’m now. Starting WoW as a total newbie I chose a human called Nobroody. After reaching level 10 somebody told me I was a warrior. Huh? Oh 6 different classes, cool!
So keeping my nose to the grind stone it took me approximately 6 months for my warrior tank to get to 80 and get a feel for the talents, stats, and spells. Only then for the real work to start … tanking. It felt as being fairly exhausting, my gear wasn’t rading material yet and I was reluctant to start raiding as it felt to stressful.
During my fun playtime with my fellow guildies I was totally swept of my feet by the night elfs so I started a new toon, a druid called Broodala. I leveled as feral, as I was adviced by one of my guildies, and changed to balance at level 75. Reaching 80 briefly thereafter i’d decided to make this my main. As my guild -The Old Guard- has an immense social ring to it I contacted one of the officers that this was gonna be my main. It would take a few weeks for I could participate in raids with this one. So ahead lies a nice period to grind for emblems and upgrade my gear. 2 weeks after that my gear levels were up to 200-226. I am now saving up for a few tier9 items, but then this toons is sort of maxed out (aye, also the fishing, cooking and weapons skills).
So why Loremaster?
Totally smitten by my elf, I planned to dual spec balance/resto, thus having 2 roles and contribute to my guild. Although I like teaming up with my guildies a lot, I can’t deny the fact that I’m somewhat of a lone ranger. So once resto is in the pocket, then what? Leveling a female dwarf enchanter/inscriptor feels like another grind job. Having gained my title “Ambassador” I got a taste for more. So I figured what’s the coolest there is? Well, Loremaster sounds very SF-ish, elf-ish and if not fairy taily. It needs approximately 700 quests per contintent. But more important it makes you see everything in the Eastern Kingdoms and Kalimdor as well.
Then why go for the title since it’s such a tedious job. It’s all about having fun and the beauty of the zones from the original warcraft game is absolutely amazing. Besides the fact that those speedlevel like ‘projects’Â are all about the end game and there’s just so much more to see and experience. And on another subject .. It’s payback time!
The undead donut of death
Where my warrior died time and time again, at level 80 my druid caster killed the mobs like a knife through butter which felt great, as are the big pulls in the low level dungeons. I realize it all depends on the person and what your likes and dislikes are but it definitely is fun to get that title.
Screenies
Some screenshots for the people that are not convinced yet
Yep, it’ me
Back home
Fiendish colors
Pine trees and fluffy clouds
Broodala vs. Balnazzar
Funnies
Wouldn’t want to withhold you my moments of joy.
Are you old enough to do this?
Arrow surfing
Yep, it’s us
Huh?
Hope you enjoyed this post and continue to have fun in WoW!
October 1, 2009
Tags: gaming, reizen, warcraft Posted in: Diversen
No Comments
“Walk-around” for pre lighttpd-1.4.23 feature “fix-root-scriptname”
Walk-around should ofcourse be workaround but it feels more like a walk around …
Today I ‘moved’ the first version of my application from the Django development setup (run via “./manage runserver”) to a lighttpd/fcgi production setup. Been cleaning up code and concluded I wanted the app straight on the ‘/’ or docroot with a simple config like this:
HTTP["host"] == "example.int" {
fastcgi.server = (
"/" => (
"main" => (
"host" => "192.168.1.2",
"port" => 8000,
"check-local" => "disable",
#"fix-root-scriptname" => "enable",
)
),
)
}
After a lot of messing around and not getting the aformetioned setup to work I found the ‘PATH_INFO’ problem. The easiest way is to have lighttpd version 1.4.23 running, because then you can enable:
"fix-root-scriptname" => "enable",
And therewith actually acces your app on “/” instead of “/dd” as shown in the below example under the line with “fastcgi.server (“.
The problem however is that lighttpd version 1.4.23 is not apt-get-able yet and I like my systems clean. So here’s the somewhat cumbersome but functional example that works:
HTTP["host"] == "example.int" {
fastcgi.server = (
"/dd" => (
"main" => (
"host" => "192.168.1.2",
"port" => 8000,
"check-local" => "disable",
)
),
)
url.rewrite-once = (
"^(/.*)$" => "/dd/$1",
)
}
What happens is that lighttpd passes an incoming request like “/url_to_something” to your fcgi proces, but fcgi is on “/dd/url_to_something”. So with the rewrite rule we make sure the “/dd” prefix is added, other wise it would not be passed to the fcgi handler.
The problem however is that you should modify all your in-app links so they would be prefixed with /dd otherwise you end up with a 404. This can be globally resolved by adding the line below to your Django settings.py
FORCE_SCRIPT_NAME = ""
Django then strips of the “/dd” prefix just before it gets parsed by your django app URL Dispatcher.
So when lighttpd version 1.4.23 is in the debian and/or Ubuntu repository, you apt get it and adjust your setup in 3 places.
Remove the rewrite rule and the “dd” part from your lighttpd host config and disable the FORCE_SCRIPT_NAME in your settings.py. Doing it like this keeps everything running with the least adjustments now and only a little work to be done in the near future.
August 15, 2009
Tags: django, framework, python Posted in: All ENGLISH articles, Technical
No Comments
Super praktische Yoga tas
In mijn wekelijks reizen naar de Yoga les neem ik altijd mijn eigen matje en meditatie kussen mee. Ik heb een mini sport tas maar daar past het nauwelijks in. Het is sowieso beter om je yoga mat op te rollen, en zoekend naar een nieuwe tas kijk je dus minaal naar een lengte van 60 cm. Daarbij komt dat een meditatie (zit) kussen ook al gauw een diameter heeft van 35 cm en minimaal 15 cm hoog is. Zoekend naar een tas met die eisen kom je op veel te grote en niet handzame tassen uit. Naast een matje en een kussen neem je vaak niet veel meer mee dan alleen een handdoek en bijvoorbeeld een flesje water.
Met dank aan Karin voor de praktische impuls tijdens een middagje winkelcentrum. Hierbij een geweldige tip voor eenieder die het bovenstaande herkend. De tas die uitstekend voldoet is een tennistas, te koop bij Scapino voor € 15,- (en nu zelfs met nog 40% korting).
Er is nog voldoende ruimte over voor een boek, handdoek, flesje water, of wat je ook maar meeneemt naar de les. Naast een praktisch handvat gaat hij met behulp van de banden ook heel eenvoudig over je schouder.
En met de draagbanden de andere kant op doe je hem even gemakkelijk om als rugzak.
Dus wees gewaarschuwd; Als je volgende keer iemand ziet met een tennis tas kan het zomaar zijn dat hij aan Yoga doet ..
Groet Gerard.
July 24, 2009
Tags: meditatie, vervoer, yoga Posted in: Diversen
One Comment














