More than two months ago I globed about QStrings and paths. The problem was this: my app accepts paths via command line, which are processed via KCmdLineOptions; which in turn converts everything to QStrings. What I wanted were paths, which are more like QByteArrays, not QStrings (because the latter have internally an unicode representation; more on that later). Including PyQt4 in the equation forced me to resort to QByteArray to get the path as a str instead of using QString.constData() (PyQt4 doesn't export that function). But that's only the beginning of the problem.

Take for instance this situation. I have a music collection that I've been building for years now (more that 10, I think). In the old times of this collection the filenames were encoded in iso-8859-1. Then the future came and converted all my machines to utf-8. But only the software; the filesystems were in one way or another inherited from system to system, from machine to machine. So I ended with a mixture of utf and iso filenames, to the point where I have a file whose filename is in iso, but the directory where it is is in utf. Yes, I know, it is a mess. But if I take any decent media player, I can play the file allright. That's because the filesystem knows nothing of encodings (otherwise it would reject badly encoded filenames).

I just spent last saturday making sure that satyr only stored filepaths in strs, not unicodes or QStrings. It took concentration, but having just a bunch of classes and only 3 or 4 points where the filepaths are managed it wasn't that difficult. Still, it took a day. But then, as I mentioned in that post, Phonon the is not able to play such files... or so I thought.

If you run satyr after executing export PHONON_XINE_DEBUG=1 you'll see a lot of Phonon debug info in the console (not that there is another way to run satyr right now anyways). Among all that info you'll see lines such as these two:

void Phonon::Xine::XineStream::setMrl(const QByteArray&, Phonon::Xine::XineStream::StateForNewMrl) ...
bool Phonon::Xine::XineStream::xineOpen(Phonon::State) xine_open succeeded for m_mrl = ...

If you're sharp enough (I'm not; sandsmark from #phonon had to tell me) you'll note the mention of MRL's. MRL's are xine's URL for media. As any URL, they can (and most of the time must) encode 'strange' characters with the so-called "percent encoding". This means that no matter what encodings the different parts of a filepath is in, I just add file:// at the beginning and then I can safely encode it scaping non-ascii characters to %xx representations... or that's what the theory says. One thing to note is that the file:// part must not be scaped; xine complains that the file does not exist in that case.

Looking for help in Qt's classes one can find QUrl and the already known QByteArray. I can call QByteArray.toPercentEnconding() from my str and feed that to QUrl.fromPercentEncoding() (which strangely returns a QString, which is exactly what we're avoiding) or QUrl.fromEncoded(). But then the first function encodes too much, replacing :// with %3A%2F%2F. No fun.

Ok, let's try creating a QByteArray with only the file:// and then append() the toPercentEncoding() of the path only. It works:

PyQt4.QtCore.QByteArray('file://%2Fhome%2Fmdione...%2F%C3%9Altimo%20bondi%20a%20Finisterre%2F07-%20La%20peque%F1a%20novia%20del%20carioca.wav')

But then calling QUrl.fromEncoded() gives:

PyQt4.QtCore.QUrl("file://xn--/home/mdione.../ltimo bondi a finisterre/07- la pequea novia del carioca-wkmz60758d.wav")

The URL got somehow puny-encoded, which of course xine doesn't recognize for local files.

Another option is to create an empty QUrl, call setEncodedUrl() with the ParsingMode to QUrl.StrictMode so we avoid 50 lines of code that start here[1] that try to escape everything all over again (and I already had some double-or-even-triple-enconding nightmares parsing RSS/Atom feeds last year, thank you), but we get puny-encoded again (maybe it is 'pwny-encoded'?).

Last resort: backtrack to the point were we created only one QByteArray with the path and call toPercentEncoding(); feed that to the method setEncodedPath() of an empty QUrl. Then we add the last piece calling setScheme('file') and we're ready! Of course we're not:

PyQt4.QtCore.QByteArray('file:%2Fhome%2Fmdione...%2F%C3%9Altimo%20bondi%20a%20Finisterre%2F07-%20La%20peque%F1a%20novia%20del%20carioca.wav')

Notice the lack of the two // after file:? xine doesn't like it; hence, I don't either.

Ok, this post got too long. I hope I can resolve this soon, I already spent too much time on it. At least a good part of it was expaining it, so others don't have to suffer the same as I did.

BTW, satyr will shortly be released, whether I fix this bug or not.


satyr pykde phonon


[1] Look at the size of that file! 6k lines to handle URL's! Who would say it was so difficult... Once more I'm remembered of how lucky I am to have this libraries at the tips of my fingers, yay!