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.
[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!