![]() |
Mixxx
|
00001 // 00002 // C++ Implementation: parserm3u 00003 // 00004 // Description: module to parse m3u(plaintext) formated playlists 00005 // 00006 // 00007 // Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004 00008 // Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011 00009 // 00010 // Copyright: See COPYING file that comes with this distribution 00011 // 00012 // 00013 #include <QTextStream> 00014 #include <QDebug> 00015 #include <QDir> 00016 #include <QMessageBox> 00017 #include "parserm3u.h" 00018 #include <QUrl> 00019 #include <QTextCodec> 00020 00040 ParserM3u::ParserM3u() : Parser() 00041 { 00042 } 00043 00044 ParserM3u::~ParserM3u() 00045 { 00046 00047 } 00048 00049 00050 QList<QString> ParserM3u::parse(QString sFilename) 00051 { 00052 QFile file(sFilename); 00053 QString basepath = sFilename.section('/', 0, -2); 00054 00055 clearLocations(); 00056 //qDebug() << "ParserM3u: Starting to parse."; 00057 if (file.open(QIODevice::ReadOnly) && !isBinary(sFilename)) { 00058 /* Unfortunately, QT 4.7 does not handle <CR> (=\r or asci value 13) line breaks. 00059 * This is important on OS X where iTunes, e.g., exports M3U playlists using <CR> 00060 * rather that <LF> 00061 * 00062 * Using QFile::readAll() we obtain the complete content of the playlist as a ByteArray. 00063 * We replace any '\r' with '\n' if applicaple 00064 * This ensures that playlists from iTunes on OS X can be parsed 00065 */ 00066 QByteArray ba = file.readAll(); 00067 //detect encoding 00068 bool isCRLF_encoded = ba.contains("\r\n"); 00069 bool isCR_encoded = ba.contains("\r"); 00070 if(isCR_encoded && !isCRLF_encoded) 00071 ba.replace('\r','\n'); 00072 QTextStream textstream(ba.data()); 00073 00074 if (isUtf8(ba.data())) { 00075 textstream.setCodec("UTF-8"); 00076 } else { 00077 textstream.setCodec("windows-1252"); 00078 } 00079 00080 while(!textstream.atEnd()) { 00081 QString sLine = getFilepath(&textstream, basepath); 00082 if(sLine.isEmpty()) 00083 break; 00084 00085 //qDebug() << "ParserM3u: parsed: " << (sLine); 00086 m_sLocations.append(sLine); 00087 } 00088 00089 file.close(); 00090 00091 if(m_sLocations.count() != 0) 00092 return m_sLocations; 00093 else 00094 return QList<QString>(); // NULL pointer returned when no locations were found 00095 00096 } 00097 00098 file.close(); 00099 return QList<QString>(); //if we get here something went wrong 00100 } 00101 00102 00103 QString ParserM3u::getFilepath(QTextStream *stream, QString basepath) 00104 { 00105 QString textline,filename = ""; 00106 00107 textline = stream->readLine(); 00108 00109 while(!textline.isEmpty()){ 00110 //qDebug() << "Untransofrmed text: " << textline; 00111 if(textline.isNull()) 00112 break; 00113 00114 if(!textline.contains("#")){ 00115 filename = textline; 00116 filename.remove("file://"); 00117 QByteArray strlocbytes = filename.toUtf8(); 00118 //qDebug() << "QByteArray UTF-8: " << strlocbytes; 00119 QUrl location = QUrl::fromEncoded(strlocbytes); 00120 //qDebug() << "QURL UTF-8: " << location; 00121 QString trackLocation = location.toString(); 00122 //qDebug() << "UTF8 TrackLocation:" << trackLocation; 00123 if(isFilepath(trackLocation)) { 00124 return trackLocation; 00125 } else { 00126 // Try relative to m3u dir 00127 QString rel = basepath + "/" + trackLocation; 00128 if (isFilepath(rel)) { 00129 return rel; 00130 } 00131 // We couldn't match this to a real file so ignore it 00132 } 00133 } 00134 textline = stream->readLine(); 00135 } 00136 00137 // Signal we reached the end 00138 return 0; 00139 00140 } 00141 00142 bool ParserM3u::writeM3UFile(const QString &file_str, QList<QString> &items, bool useRelativePath) { 00143 return writeM3UFile(file_str, items, useRelativePath, false); 00144 } 00145 00146 bool ParserM3u::writeM3U8File(const QString &file_str, QList<QString> &items, bool useRelativePath) { 00147 return writeM3UFile(file_str, items, useRelativePath, true); 00148 } 00149 00150 bool ParserM3u::writeM3UFile(const QString &file_str, QList<QString> &items, bool useRelativePath, bool useUtf8) 00151 { 00152 // Important note: 00153 // On Windows \n will produce a <CR><CL> (=\r\n) 00154 // On Linux and OS X \n is <CR> (which remains \n) 00155 00156 QTextCodec* codec; 00157 if (useUtf8) { 00158 codec = QTextCodec::codecForName("UTF-8"); 00159 } else { 00160 // according to http://en.wikipedia.org/wiki/M3U the default encoding of m3u is Windows-1252 00161 // see also http://tools.ietf.org/html/draft-pantos-http-live-streaming-07 00162 // check if the all items can be properly encoded to Latin1. 00163 codec = QTextCodec::codecForName("windows-1252"); 00164 for (int i = 0; i < items.size(); ++i) { 00165 if (!codec->canEncode(items.at(i))) { 00166 // filepath contains incompatible character 00167 QMessageBox::warning(NULL,tr("Playlist Export Failed"), 00168 tr("File path contains characters, not allowed in m3u playlists.\n") + 00169 tr("Export a m3u8 playlist instead!\n") + 00170 items.at(i)); 00171 return false; 00172 } 00173 } 00174 } 00175 00176 QFile file(file_str); 00177 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { 00178 QMessageBox::warning(NULL,tr("Playlist Export Failed"), 00179 tr("Could not create file") + " " + file_str); 00180 return false; 00181 } 00182 00183 // Base folder of file 00184 QString base = file_str.section('/', 0, -2); 00185 QDir base_dir(base); 00186 00187 qDebug() << "Basepath: " << base; 00188 QTextStream out(&file); 00189 out.setCodec(codec); 00190 out << "#EXTM3U\n"; 00191 for (int i = 0; i < items.size(); ++i) { 00192 out << "#EXTINF\n"; 00193 // Write relative path if possible 00194 if (useRelativePath) { 00195 //QDir::relativePath() will return the absolutePath if it cannot compute the 00196 //relative Path 00197 out << base_dir.relativeFilePath(items.at(i)) << "\n"; 00198 } else { 00199 out << items.at(i) << "\n"; 00200 } 00201 } 00202 return true; 00203 }