1 //
2 // C++ Implementation: parserpls
3 //
4 // Description: module to parse pls formatted playlists
5 //
6 //
7 // Author: Ingo Kossyk <kossyki@cs.tu-berlin.de>, (C) 2004
8 // Author: Tobias Rafreider trafreider@mixxx.org, (C) 2011
9 //
10 // Copyright: See COPYING file that comes with this distribution
11 //
12 //
13 #include "library/parserpls.h"
14
15 #include <QDir>
16 #include <QFile>
17 #include <QMessageBox>
18 #include <QUrl>
19 #include <QtDebug>
20
21 #include "moc_parserpls.cpp"
22
23 /**
24 ToDo:
25 - parse ALL information from the pls file if available ,
26 not only the filepath;
27 **/
28
ParserPls()29 ParserPls::ParserPls() : Parser() {
30 }
31
~ParserPls()32 ParserPls::~ParserPls() {
33 }
34
parse(const QString & sFilename)35 QList<QString> ParserPls::parse(const QString& sFilename) {
36 //long numEntries =0;
37 QFile file(sFilename);
38 const auto basePath = sFilename.section('/', 0, -2);
39
40 clearLocations();
41
42 if (file.open(QIODevice::ReadOnly)) {
43 /* Unfortunately, QT 4.7 does not handle <CR> (=\r or asci value 13) line breaks.
44 * This is important on OS X where iTunes, e.g., exports M3U playlists using <CR>
45 * rather that <LF>
46 *
47 * Using QFile::readAll() we obtain the complete content of the playlist as a ByteArray.
48 * We replace any '\r' with '\n' if applicaple
49 * This ensures that playlists from iTunes on OS X can be parsed
50 */
51 QByteArray ba = file.readAll();
52 //detect encoding
53 bool isCRLF_encoded = ba.contains("\r\n");
54 bool isCR_encoded = ba.contains("\r");
55 if (isCR_encoded && !isCRLF_encoded) {
56 ba.replace('\r','\n');
57 }
58 QTextStream textstream(ba.constData());
59
60 while(!textstream.atEnd()) {
61 QString psLine = getFilePath(&textstream, basePath);
62 if(psLine.isNull()) {
63 break;
64 } else {
65 m_sLocations.append(psLine);
66 }
67
68 }
69
70 file.close();
71
72 if (m_sLocations.count() != 0) {
73 return m_sLocations;
74 } else {
75 return QList<QString>(); // NULL pointer returned when no locations were found
76 }
77 }
78
79 file.close();
80 return QList<QString>(); //if we get here something went wrong :D
81 }
82
getNumEntries(QTextStream * stream)83 long ParserPls::getNumEntries(QTextStream *stream) {
84 QString textline;
85 textline = stream->readLine();
86
87 if (textline.contains("[playlist]")) {
88 while (!textline.contains("NumberOfEntries")) {
89 textline = stream->readLine();
90 }
91
92 QString temp = textline.section("=",-1,-1);
93
94 return temp.toLong();
95
96 } else{
97 qDebug() << "ParserPls: pls file is not a playlist! \n";
98 return 0;
99 }
100
101 }
102
103
getFilePath(QTextStream * stream,const QString & basePath)104 QString ParserPls::getFilePath(QTextStream *stream, const QString& basePath) {
105 QString textline = stream->readLine();
106 while (!textline.isEmpty()) {
107 if (textline.isNull()) {
108 break;
109 }
110
111 if(textline.contains("File")) {
112 int iPos = textline.indexOf("=", 0);
113 ++iPos;
114
115 QString filename = textline.right(textline.length() - iPos);
116 auto trackFile = playlistEntryToTrackFile(filename, basePath);
117 if (trackFile.checkFileExists()) {
118 return trackFile.location();
119 }
120 // We couldn't match this to a real file so ignore it
121 qWarning() << trackFile << "not found";
122 }
123 textline = stream->readLine();
124 }
125
126 // Signal we reached the end
127 return QString();
128 }
129
writePLSFile(const QString & file_str,const QList<QString> & items,bool useRelativePath)130 bool ParserPls::writePLSFile(const QString &file_str, const QList<QString> &items, bool useRelativePath)
131 {
132 QFile file(file_str);
133 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
134 QMessageBox::warning(nullptr,
135 tr("Playlist Export Failed"),
136 tr("Could not create file") + " " + file_str);
137 return false;
138 }
139 //Base folder of file
140 QString base = file_str.section('/', 0, -2);
141 QDir base_dir(base);
142
143 QTextStream out(&file);
144 out << "[playlist]\n";
145 out << "NumberOfEntries=" << items.size() << "\n";
146 for (int i = 0; i < items.size(); ++i) {
147 //Write relative path if possible
148 if (useRelativePath) {
149 //QDir::relativePath() will return the absolutePath if it cannot compute the
150 //relative Path
151 out << "File" << i << "=" << base_dir.relativeFilePath(items.at(i)) << "\n";
152 } else {
153 out << "File" << i << "=" << items.at(i) << "\n";
154 }
155 }
156
157 return true;
158 }
159