Actually make RegExp-based file renaming work.
[LameXP.git] / src / PlaylistImporter.cpp
bloba34fa6dc3f2209ec366342c765f386b852b9bdca
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2015 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
23 #include "PlaylistImporter.h"
25 //Internal
26 #include "Global.h"
28 //MUtils
29 #include <MUtils/Global.h>
31 //Qt
32 #include <QString>
33 #include <QStringList>
34 #include <QDir>
35 #include <QFileInfo>
36 #include <QProcess>
37 #include <QDate>
38 #include <QTime>
39 #include <QDebug>
40 #include <QTextCodec>
42 //Un-escape XML characters
43 static const struct
45 char *escape;
46 char *output;
48 g_xmlEscapeSequence[] =
50 {"&amp;", "&"},
51 {"&lt;", "<"},
52 {"&gt;", ">"},
53 {"&apos;", "'"},
54 {"&nbsp;", " "},
55 {"&quot;", "\""},
56 {NULL, NULL}
59 const char *PlaylistImporter::supportedExtensions = "*.m3u *.m3u8 *.pls *.asx *.wpl";
62 ////////////////////////////////////////////////////////////
63 // Public Functions
64 ////////////////////////////////////////////////////////////
66 bool PlaylistImporter::importPlaylist(QStringList &fileList, const QString &playlistFile)
68 QFileInfo file(playlistFile);
69 QDir baseDir(file.canonicalPath());
71 QDir rootDir(baseDir);
72 while(rootDir.cdUp());
74 //Detect playlist type
75 playlist_t playlistType = isPlaylist(file.canonicalFilePath());
77 //Exit if not a playlist
78 if(playlistType == notPlaylist)
80 return false;
83 QFile data(playlistFile);
85 //Open file for reading
86 if(!data.open(QIODevice::ReadOnly))
88 return false;
91 //Skip very large files (parsing could take very long)
92 if((file.size() < 3) || (file.size() > 524288))
94 qWarning("File is very big. Probably not a Playlist. Rejecting...");
95 return false;
98 //Parse playlist depending on type
99 switch(playlistType)
101 case m3uPlaylist:
102 return parsePlaylist_m3u(data, fileList, baseDir, rootDir);
103 break;
104 case plsPlaylist:
105 return parsePlaylist_pls(data, fileList, baseDir, rootDir);
106 break;
107 case wplPlaylist:
108 return parsePlaylist_wpl(data, fileList, baseDir, rootDir);
109 break;
110 default:
111 return false;
112 break;
116 ////////////////////////////////////////////////////////////
117 // Private Functions
118 ////////////////////////////////////////////////////////////
120 PlaylistImporter::playlist_t PlaylistImporter::isPlaylist(const QString &fileName)
122 QFileInfo file (fileName);
124 if(file.suffix().compare("m3u", Qt::CaseInsensitive) == 0 || file.suffix().compare("m3u8", Qt::CaseInsensitive) == 0)
126 return m3uPlaylist;
128 else if(file.suffix().compare("pls", Qt::CaseInsensitive) == 0)
130 return plsPlaylist;
132 else if(file.suffix().compare("asx", Qt::CaseInsensitive) == 0 || file.suffix().compare("wpl", Qt::CaseInsensitive) == 0)
134 return wplPlaylist;
136 else
138 return notPlaylist;
142 bool PlaylistImporter::parsePlaylist_m3u(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
144 const QTextCodec *codec = QTextCodec::codecForName("System");
145 const bool preferUTF8 = data.fileName().endsWith(".m3u8", Qt::CaseInsensitive);
146 bool foundAtLeastOneFile = false;
148 data.reset();
150 while(!data.atEnd())
152 QString filePath[3];
153 QByteArray line = data.readLine();
155 if(preferUTF8)
157 filePath[0] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed()));
158 filePath[1] = QString(QDir::fromNativeSeparators(codec->toUnicode(line.constData(), line.size()).trimmed()));
159 filePath[2] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed()));
161 else
163 filePath[0] = QString(QDir::fromNativeSeparators(codec->toUnicode(line.constData(), line.size()).trimmed()));
164 filePath[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed()));
165 filePath[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed()));
168 for(size_t i = 0; i < 3; i++)
170 if(!(filePath[i].isEmpty() || filePath[i].startsWith("#") || filePath[i].contains(QChar(QChar::ReplacementCharacter))))
172 QFileInfo filename(filePath[i]);
173 filename.setCaching(false);
174 fixFilePath(filename, baseDir, rootDir);
176 if(filename.exists() && filename.isFile())
178 qDebug("Found: \"%s\"", MUTILS_UTF8(filePath[i]));
179 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
181 fileList << filename.canonicalFilePath();
183 foundAtLeastOneFile = true;
184 break;
190 //If we did not find any files yet, try UTF-16 now
191 if(!foundAtLeastOneFile)
193 const char* codecs[2] = {"UTF-16LE", "UTF-16BE"};
195 for(size_t i = 0; i < 2; i++)
197 QTextStream stream(&data);
198 stream.setAutoDetectUnicode(false);
199 stream.setCodec(codecs[i]);
200 stream.seek(0i64);
202 while(!stream.atEnd())
204 QString filePath = stream.readLine().trimmed();
206 if(!(filePath.isEmpty() || filePath.startsWith("#") || filePath.contains(QChar(QChar::ReplacementCharacter))))
208 QFileInfo filename(filePath);
209 filename.setCaching(false);
210 fixFilePath(filename, baseDir, rootDir);
212 if(filename.exists() && filename.isFile())
214 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
216 fileList << filename.canonicalFilePath();
218 foundAtLeastOneFile = true;
223 if(foundAtLeastOneFile) break;
227 return foundAtLeastOneFile;
230 bool PlaylistImporter::parsePlaylist_pls(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
232 QRegExp plsEntry("File(\\d+)=(.+)", Qt::CaseInsensitive);
233 const QTextCodec *codec = QTextCodec::codecForName("System");
234 bool foundAtLeastOneFile = false;
236 data.reset();
238 while(!data.atEnd())
240 QString filePath[3];
241 QByteArray line = data.readLine();
243 filePath[0] = QString(QDir::fromNativeSeparators(codec->toUnicode(line.constData(), line.size()).trimmed()));
244 filePath[1] = QString(QDir::fromNativeSeparators(QString::fromLatin1(line.constData(), line.size()).trimmed()));
245 filePath[2] = QString(QDir::fromNativeSeparators(QString::fromUtf8(line.constData(), line.size()).trimmed()));
247 for(size_t i = 0; i < 3; i++)
249 if(!filePath[i].contains(QChar(QChar::ReplacementCharacter)))
251 if(plsEntry.indexIn(filePath[i]) >= 0)
253 QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed());
254 filename.setCaching(false);
255 fixFilePath(filename, baseDir, rootDir);
257 if(filename.exists() && filename.isFile())
259 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
261 fileList << filename.canonicalFilePath();
263 foundAtLeastOneFile = true;
264 break;
271 //If we did not find any files yet, try UTF-16 now
272 if(!foundAtLeastOneFile)
274 const char* codecs[2] = {"UTF-16LE", "UTF-16BE"};
276 for(size_t i = 0; i < 2; i++)
278 QTextStream stream(&data);
279 stream.setAutoDetectUnicode(false);
280 stream.setCodec(codecs[i]);
281 stream.seek(0i64);
283 while(!stream.atEnd())
285 QString filePath = stream.readLine().trimmed();
287 if(!filePath.contains(QChar(QChar::ReplacementCharacter)))
289 if(plsEntry.indexIn(filePath) >= 0)
291 QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed());
292 filename.setCaching(false);
293 fixFilePath(filename, baseDir, rootDir);
295 if(filename.exists() && filename.isFile())
297 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
299 fileList << filename.canonicalFilePath();
301 foundAtLeastOneFile = true;
307 if(foundAtLeastOneFile) break;
311 return foundAtLeastOneFile;
314 bool PlaylistImporter::parsePlaylist_wpl(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
316 bool foundAtLeastOneFile = false;
318 QRegExp skipData("<!--(.+)-->", Qt::CaseInsensitive);
319 QRegExp wplEntry("<(media|ref)[^<>]*(src|href)=\"([^\"]+)\"[^<>]*>", Qt::CaseInsensitive);
321 skipData.setMinimal(true);
323 QByteArray buffer = data.readAll();
324 QString line = QString::fromUtf8(buffer.constData(), buffer.size()).simplified();
325 buffer.clear();
327 int index = 0;
329 while((index = skipData.indexIn(line)) >= 0)
331 line.remove(index, skipData.matchedLength());
334 int offset = 0;
336 while((offset = wplEntry.indexIn(line, offset) + 1) > 0)
338 QFileInfo filename(QDir::fromNativeSeparators(unescapeXml(wplEntry.cap(3)).trimmed()));
339 filename.setCaching(false);
340 fixFilePath(filename, baseDir, rootDir);
342 if(filename.exists() && filename.isFile())
344 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
346 fileList << filename.canonicalFilePath();
347 foundAtLeastOneFile = true;
352 return foundAtLeastOneFile;
355 void PlaylistImporter::fixFilePath(QFileInfo &filename, const QDir &baseDir, const QDir &rootDir)
357 if(filename.filePath().startsWith("/"))
359 while(filename.filePath().startsWith("/"))
361 filename.setFile(filename.filePath().mid(1));
363 filename.setFile(rootDir.filePath(filename.filePath()));
366 if(!filename.isAbsolute())
368 filename.setFile(baseDir.filePath(filename.filePath()));
372 QString &PlaylistImporter::unescapeXml(QString &str)
374 for(int i = 0; (g_xmlEscapeSequence[i].escape && g_xmlEscapeSequence[i].output); i++)
376 str.replace(g_xmlEscapeSequence[i].escape, g_xmlEscapeSequence[i].output);
379 return str;