OSDN Git Service

Added Hungarian translation, thanks to Zityi's Translator Team <zityisoft@gmail.com>.
[lamexp/LameXP.git] / src / PlaylistImporter.cpp
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.
10 //
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.
15 //
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.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include "PlaylistImporter.h"
24
25 //Internal
26 #include "Global.h"
27
28 //MUtils
29 #include <MUtils/Global.h>
30
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>
41
42 //Un-escape XML characters
43 static const struct
44 {
45         char *escape;
46         char *output;
47 }
48 g_xmlEscapeSequence[] =
49 {
50         {"&amp;", "&"},
51         {"&lt;", "<"},
52         {"&gt;", ">"},
53         {"&apos;", "'"},
54         {"&nbsp;", " "},
55         {"&quot;", "\""},
56         {NULL, NULL}
57 };
58
59 const char *PlaylistImporter::supportedExtensions = "*.m3u *.m3u8 *.pls *.asx *.wpl";
60
61
62 ////////////////////////////////////////////////////////////
63 // Public Functions
64 ////////////////////////////////////////////////////////////
65
66 bool PlaylistImporter::importPlaylist(QStringList &fileList, const QString &playlistFile)
67 {
68         QFileInfo file(playlistFile);
69         QDir baseDir(file.canonicalPath());
70
71         QDir rootDir(baseDir);
72         while(rootDir.cdUp());
73         
74         //Detect playlist type
75         playlist_t playlistType = isPlaylist(file.canonicalFilePath());
76
77         //Exit if not a playlist
78         if(playlistType == notPlaylist)
79         {
80                 return false;
81         }
82         
83         QFile data(playlistFile);
84
85         //Open file for reading
86         if(!data.open(QIODevice::ReadOnly))
87         {
88                 return false;
89         }
90
91         //Skip very large files (parsing could take very long)
92         if((file.size() < 3) || (file.size() > 524288))
93         {
94                 qWarning("File is very big. Probably not a Playlist. Rejecting...");
95                 return false;
96         }
97
98         //Parse playlist depending on type
99         switch(playlistType)
100         {
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;
113         }
114 }
115
116 ////////////////////////////////////////////////////////////
117 // Private Functions
118 ////////////////////////////////////////////////////////////
119
120 PlaylistImporter::playlist_t PlaylistImporter::isPlaylist(const QString &fileName)
121 {
122         QFileInfo file (fileName);
123         
124         if(file.suffix().compare("m3u", Qt::CaseInsensitive) == 0 || file.suffix().compare("m3u8", Qt::CaseInsensitive) == 0)
125         {
126                 return m3uPlaylist;
127         }
128         else if(file.suffix().compare("pls", Qt::CaseInsensitive) == 0)
129         {
130                 return  plsPlaylist;
131         }
132         else if(file.suffix().compare("asx", Qt::CaseInsensitive) == 0 || file.suffix().compare("wpl", Qt::CaseInsensitive) == 0)
133         {
134                 return  wplPlaylist;
135         }
136         else
137         {
138                 return notPlaylist;
139         }
140 }
141
142 bool PlaylistImporter::parsePlaylist_m3u(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
143 {
144         const QTextCodec *codec = QTextCodec::codecForName("System");
145         const bool preferUTF8 = data.fileName().endsWith(".m3u8", Qt::CaseInsensitive);
146         bool foundAtLeastOneFile = false;
147         
148         data.reset();
149
150         while(!data.atEnd())
151         {
152                 QString filePath[3];
153                 QByteArray line = data.readLine();
154
155                 if(preferUTF8)
156                 {
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()));
160                 }
161                 else
162                 {
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()));
166                 }
167
168                 for(size_t i = 0; i < 3; i++)
169                 {
170                         if(!(filePath[i].isEmpty() || filePath[i].startsWith("#") || filePath[i].contains(QChar(QChar::ReplacementCharacter))))
171                         {
172                                 QFileInfo filename(filePath[i]);
173                                 filename.setCaching(false);
174                                 fixFilePath(filename, baseDir, rootDir);
175
176                                 if(filename.exists() && filename.isFile())
177                                 {
178                                         qDebug("Found: \"%s\"", MUTILS_UTF8(filePath[i]));
179                                         if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
180                                         {
181                                                 fileList << filename.canonicalFilePath();
182                                         }
183                                         foundAtLeastOneFile = true;
184                                         break;
185                                 }
186                         }
187                 }
188         }
189
190         //If we did not find any files yet, try UTF-16 now
191         if(!foundAtLeastOneFile)
192         {
193                 const char* codecs[2] = {"UTF-16LE", "UTF-16BE"};
194
195                 for(size_t i = 0; i < 2; i++)
196                 {
197                         QTextStream stream(&data);
198                         stream.setAutoDetectUnicode(false);
199                         stream.setCodec(codecs[i]);
200                         stream.seek(0i64);
201
202                         while(!stream.atEnd())
203                         {
204                                 QString filePath = stream.readLine().trimmed();
205
206                                 if(!(filePath.isEmpty() || filePath.startsWith("#") || filePath.contains(QChar(QChar::ReplacementCharacter))))
207                                 {
208                                         QFileInfo filename(filePath);
209                                         filename.setCaching(false);
210                                         fixFilePath(filename, baseDir, rootDir);
211
212                                         if(filename.exists() && filename.isFile())
213                                         {
214                                                 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
215                                                 {
216                                                         fileList << filename.canonicalFilePath();
217                                                 }
218                                                 foundAtLeastOneFile = true;
219                                         }
220                                 }
221                         }
222
223                         if(foundAtLeastOneFile) break;
224                 }
225         }
226
227         return foundAtLeastOneFile;
228 }
229
230 bool PlaylistImporter::parsePlaylist_pls(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
231 {
232         QRegExp plsEntry("File(\\d+)=(.+)", Qt::CaseInsensitive);
233         const QTextCodec *codec = QTextCodec::codecForName("System");
234         bool foundAtLeastOneFile = false;
235
236         data.reset();
237
238         while(!data.atEnd())
239         {
240                 QString filePath[3];
241                 QByteArray line = data.readLine();
242
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()));
246                 
247                 for(size_t i = 0; i < 3; i++)
248                 {
249                         if(!filePath[i].contains(QChar(QChar::ReplacementCharacter)))
250                         {
251                                 if(plsEntry.indexIn(filePath[i]) >= 0)
252                                 {
253                                         QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed());
254                                         filename.setCaching(false);
255                                         fixFilePath(filename, baseDir, rootDir);
256
257                                         if(filename.exists() && filename.isFile())
258                                         {
259                                                 if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
260                                                 {
261                                                         fileList << filename.canonicalFilePath();
262                                                 }
263                                                 foundAtLeastOneFile = true;
264                                                 break;
265                                         }
266                                 }
267                         }
268                 }
269         }
270
271         //If we did not find any files yet, try UTF-16 now
272         if(!foundAtLeastOneFile)
273         {
274                 const char* codecs[2] = {"UTF-16LE", "UTF-16BE"};
275
276                 for(size_t i = 0; i < 2; i++)
277                 {
278                         QTextStream stream(&data);
279                         stream.setAutoDetectUnicode(false);
280                         stream.setCodec(codecs[i]);
281                         stream.seek(0i64);
282
283                         while(!stream.atEnd())
284                         {
285                                 QString filePath = stream.readLine().trimmed();
286
287                                 if(!filePath.contains(QChar(QChar::ReplacementCharacter)))
288                                 {
289                                         if(plsEntry.indexIn(filePath) >= 0)
290                                         {
291                                                 QFileInfo filename(QDir::fromNativeSeparators(plsEntry.cap(2)).trimmed());
292                                                 filename.setCaching(false);
293                                                 fixFilePath(filename, baseDir, rootDir);
294
295                                                 if(filename.exists() && filename.isFile())
296                                                 {
297                                                         if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
298                                                         {
299                                                                 fileList << filename.canonicalFilePath();
300                                                         }
301                                                         foundAtLeastOneFile = true;
302                                                 }
303                                         }
304                                 }
305                         }
306
307                         if(foundAtLeastOneFile) break;
308                 }
309         }
310
311         return foundAtLeastOneFile;
312 }
313
314 bool PlaylistImporter::parsePlaylist_wpl(QFile &data, QStringList &fileList, const QDir &baseDir, const QDir &rootDir)
315 {
316         bool foundAtLeastOneFile = false;
317
318         QRegExp skipData("<!--(.+)-->", Qt::CaseInsensitive);
319         QRegExp wplEntry("<(media|ref)[^<>]*(src|href)=\"([^\"]+)\"[^<>]*>", Qt::CaseInsensitive);
320         
321         skipData.setMinimal(true);
322
323         QByteArray buffer = data.readAll();
324         QString line = QString::fromUtf8(buffer.constData(), buffer.size()).simplified();
325         buffer.clear();
326
327         int index = 0;
328
329         while((index = skipData.indexIn(line)) >= 0)
330         {
331                 line.remove(index, skipData.matchedLength());
332         }
333
334         int offset = 0;
335
336         while((offset = wplEntry.indexIn(line, offset) + 1) > 0)
337         {
338                 QFileInfo filename(QDir::fromNativeSeparators(unescapeXml(wplEntry.cap(3)).trimmed()));
339                 filename.setCaching(false);
340                 fixFilePath(filename, baseDir, rootDir);
341
342                 if(filename.exists() && filename.isFile())
343                 {
344                         if(isPlaylist(filename.canonicalFilePath()) == notPlaylist)
345                         {
346                                 fileList << filename.canonicalFilePath();
347                                 foundAtLeastOneFile = true;
348                         }
349                 }
350         }
351
352         return foundAtLeastOneFile;
353 }
354
355 void PlaylistImporter::fixFilePath(QFileInfo &filename, const QDir &baseDir, const QDir &rootDir)
356 {
357         if(filename.filePath().startsWith("/"))
358         {
359                 while(filename.filePath().startsWith("/"))
360                 {
361                         filename.setFile(filename.filePath().mid(1));
362                 }
363                 filename.setFile(rootDir.filePath(filename.filePath()));
364         }
365         
366         if(!filename.isAbsolute())
367         {
368                 filename.setFile(baseDir.filePath(filename.filePath()));
369         }
370 }
371
372 QString &PlaylistImporter::unescapeXml(QString &str)
373 {
374         for(int i = 0; (g_xmlEscapeSequence[i].escape && g_xmlEscapeSequence[i].output); i++)
375         {
376                 str.replace(g_xmlEscapeSequence[i].escape, g_xmlEscapeSequence[i].output);
377         }
378         
379         return str;
380 }