1 /*
2 * Strawberry Music Player
3 * This file was part of Clementine.
4 * Copyright 2010, David Sansome <me@davidsansome.com>
5 *
6 * Strawberry is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Strawberry 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
17 * along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 #include <algorithm>
22
23 #include <QObject>
24 #include <QIODevice>
25 #include <QDir>
26 #include <QFile>
27 #include <QFileInfo>
28 #include <QByteArray>
29 #include <QString>
30 #include <QStringBuilder>
31 #include <QStringList>
32 #include <QtDebug>
33
34 #include "asxiniparser.h"
35 #include "asxparser.h"
36 #include "core/logging.h"
37 #include "cueparser.h"
38 #include "m3uparser.h"
39 #include "playlistparser.h"
40 #include "playlistparsers/parserbase.h"
41 #include "plsparser.h"
42 #include "wplparser.h"
43 #include "xspfparser.h"
44
45 const int PlaylistParser::kMagicSize = 512;
46
PlaylistParser(CollectionBackendInterface * collection,QObject * parent)47 PlaylistParser::PlaylistParser(CollectionBackendInterface *collection, QObject *parent) : QObject(parent) {
48
49 default_parser_ = new XSPFParser(collection, this);
50 parsers_ << new M3UParser(collection, this);
51 parsers_ << default_parser_;
52 parsers_ << new PLSParser(collection, this);
53 parsers_ << new ASXParser(collection, this);
54 parsers_ << new AsxIniParser(collection, this);
55 parsers_ << new CueParser(collection, this);
56 parsers_ << new WplParser(collection, this);
57
58 }
59
file_extensions() const60 QStringList PlaylistParser::file_extensions() const {
61
62 QStringList ret;
63
64 for (ParserBase *parser : parsers_) {
65 ret << parser->file_extensions();
66 }
67
68 std::stable_sort(ret.begin(), ret.end());
69 return ret;
70
71 }
72
mime_types() const73 QStringList PlaylistParser::mime_types() const {
74
75 QStringList ret;
76
77 for (ParserBase *parser : parsers_) {
78 if (!parser->mime_type().isEmpty()) ret << parser->mime_type();
79 }
80
81 std::stable_sort(ret.begin(), ret.end());
82 return ret;
83
84 }
85
filters() const86 QString PlaylistParser::filters() const {
87
88 QStringList filters;
89 filters.reserve(parsers_.count() + 1);
90 QStringList all_extensions;
91 for (ParserBase *parser : parsers_) {
92 filters << FilterForParser(parser, &all_extensions);
93 }
94
95 filters.prepend(tr("All playlists (%1)").arg(all_extensions.join(" ")));
96
97 return filters.join(";;");
98
99 }
100
FilterForParser(const ParserBase * parser,QStringList * all_extensions)101 QString PlaylistParser::FilterForParser(const ParserBase *parser, QStringList *all_extensions) {
102
103 const QStringList file_extensions = parser->file_extensions();
104 QStringList extensions;
105 extensions.reserve(file_extensions.count());
106 for (const QString &extension : file_extensions) {
107 extensions << "*." + extension;
108 }
109
110 if (all_extensions) *all_extensions << extensions;
111
112 return tr("%1 playlists (%2)").arg(parser->name(), extensions.join(" "));
113
114 }
115
default_extension() const116 QString PlaylistParser::default_extension() const {
117 QStringList file_extensions = default_parser_->file_extensions();
118 return file_extensions[0];
119 }
120
default_filter() const121 QString PlaylistParser::default_filter() const {
122 return FilterForParser(default_parser_);
123 }
124
ParserForExtension(const QString & suffix) const125 ParserBase *PlaylistParser::ParserForExtension(const QString &suffix) const {
126
127 for (ParserBase *p : parsers_) {
128 if (p->file_extensions().contains(suffix)) return p;
129 }
130 return nullptr;
131
132 }
133
ParserForMimeType(const QString & mime_type) const134 ParserBase *PlaylistParser::ParserForMimeType(const QString &mime_type) const {
135
136 for (ParserBase *p : parsers_) {
137 if (!p->mime_type().isEmpty() && (QString::compare(p->mime_type(), mime_type, Qt::CaseInsensitive) == 0)) {
138 return p;
139 }
140 }
141 return nullptr;
142
143 }
144
ParserForMagic(const QByteArray & data,const QString & mime_type) const145 ParserBase *PlaylistParser::ParserForMagic(const QByteArray &data, const QString &mime_type) const {
146
147 for (ParserBase *p : parsers_) {
148 if ((!mime_type.isEmpty() && mime_type == p->mime_type()) || p->TryMagic(data)) {
149 return p;
150 }
151 }
152 return nullptr;
153
154 }
155
LoadFromFile(const QString & filename) const156 SongList PlaylistParser::LoadFromFile(const QString &filename) const {
157
158 QFileInfo info(filename);
159
160 // Find a parser that supports this file extension
161 ParserBase *parser = ParserForExtension(info.suffix());
162 if (!parser) {
163 qLog(Warning) << "Unknown filetype:" << filename;
164 return SongList();
165 }
166
167 // Open the file
168 QFile file(filename);
169 if (!file.open(QIODevice::ReadOnly)) return SongList();
170
171 SongList ret = parser->Load(&file, filename, info.absolutePath());
172 file.close();
173
174 return ret;
175
176 }
177
LoadFromDevice(QIODevice * device,const QString & path_hint,const QDir & dir_hint) const178 SongList PlaylistParser::LoadFromDevice(QIODevice *device, const QString &path_hint, const QDir &dir_hint) const {
179
180 // Find a parser that supports this data
181 ParserBase *parser = ParserForMagic(device->peek(kMagicSize));
182 if (!parser) {
183 return SongList();
184 }
185
186 return parser->Load(device, path_hint, dir_hint);
187
188 }
189
Save(const SongList & songs,const QString & filename,const Playlist::Path path_type) const190 void PlaylistParser::Save(const SongList &songs, const QString &filename, const Playlist::Path path_type) const {
191
192 QFileInfo info(filename);
193
194 // Find a parser that supports this file extension
195 ParserBase *parser = ParserForExtension(info.suffix());
196 if (!parser) {
197 qLog(Warning) << "Unknown filetype:" << filename;
198 return;
199 }
200
201 // Open the file
202 QFile file(filename);
203 if (!file.open(QIODevice::WriteOnly)) return;
204
205 parser->Save(songs, &file, info.absolutePath(), path_type);
206
207 file.close();
208
209 }
210