fixed icons
[tomamp] / tomamp / playlistmanager.cpp
1 #include "playlistmanager.h"
2 #include <QDir>
3 #include <QUrl>
4 #include <QMap>
5 #include <QTemporaryFile>
6 #include <QtNetwork/QNetworkAccessManager>
7 #include <QtNetwork/QNetworkReply>
8
9 QStringList PlaylistManager::allowedExtensions;
10
11
12 PlaylistManager::PlaylistManager(QWidget* parent)
13     : parentWidget (parent), lastMetaRead (-1)
14 {
15     if (!allowedExtensions.size ())
16     {
17         allowedExtensions << "mp3" << "ogg" << "wav" << "mlp" << "wma" << "flac" << "amr";
18         allowedExtensions << "midi" << "mid" << "mpeg" << "qcp" << "ac3" << "aiff" << "act";
19         allowedExtensions << "alac" << "alaw" << "au" << "eac3" << "aac" << "ape" << "gsm";
20         allowedExtensions << "mod" << "mp2" << "sbc" << "spx" << "tta" << "ra" << "riff";
21         allowedExtensions << "m4a" << "mp4" << "m4p" << "pcm" << "amr";
22     }
23     metaInformationResolver = new Phonon::MediaObject(parent);
24     connect(metaInformationResolver, SIGNAL(stateChanged(Phonon::State,Phonon::State)),
25         this, SLOT(metaStateChanged(Phonon::State,Phonon::State)));
26     downloadmanager = new QNetworkAccessManager(this);
27     connect(downloadmanager, SIGNAL(finished(QNetworkReply*)),
28             this, SLOT(playlistDownloadFinished(QNetworkReply*)));
29 }
30
31 int PlaylistManager::indexOf(const Phonon::MediaSource &s) const
32 {
33     for (int i = 0; i < items.size(); ++i)
34     {
35         if (items[i].source == s)
36             return i;
37     }
38     return -1;
39 }
40
41 void PlaylistManager::parseAndAddFolder(const QString &dir, bool recursive)
42 {
43     QStringList filters;
44
45     QStringList files = QDir (dir).entryList(filters);
46
47     if (files.isEmpty())
48         return;
49
50
51     int index = items.size();
52     foreach (QString string, files)
53     {
54         if (string == "."  || string == "..")
55             continue;
56         QString fname = dir + "/" + string;
57         QFileInfo fi (fname);
58         if (fi.isDir())
59         {
60             if (recursive)
61                 parseAndAddFolder(fname, true);
62             continue;
63         }
64         if (fileSupported(fname))
65         {
66             qDebug () << "Adding: " << fname;
67             items.append(PlaylistItem (PlaylistItem (fname)));
68         }
69     }
70     if (items.size () > index)
71     {
72         metaInformationResolver->setCurrentSource(items.at(index).source);
73         lastMetaRead = index;
74     }
75     emit playlistChanged (index);
76 }
77
78 void PlaylistManager::addStringList(const QStringList& list)
79 {
80     int index = items.size();
81     foreach (QString string, list)
82     {
83         if (fileSupported(string) || string.toLower().startsWith("http"))
84         {
85             if (string.toLower().startsWith("http") && ((string.toLower().endsWith("pls")
86                                         || (string.toLower().endsWith("m3u")))))
87             {
88                 downloadmanager->get(QNetworkRequest(QUrl(string)));
89             }
90             else
91             {
92                 items.append(PlaylistItem (string));
93             }
94         }
95     }
96     if (items.size () > index)
97     {
98         metaInformationResolver->setCurrentSource(items.at(index).source);
99         lastMetaRead = index;
100     }
101     emit playlistChanged(index);
102 }
103
104 void PlaylistManager::playlistDownloadFinished(QNetworkReply* rep)
105 {
106     QTemporaryFile tmp;
107     tmp.open ();
108     tmp.write (rep->readAll());
109     tmp.close ();
110     QString filename = tmp.fileName();
111     int index = items.size();
112     if (rep->url().toString().right(4).toLower() == ".m3u")
113         appendPlaylist(filename);
114     else if (rep->url().toString().right(4).toLower() == ".pls")
115         appendPlaylistPLS(filename);
116     if (items.size () > index)
117     {
118         metaInformationResolver->setCurrentSource(items.at(index).source);
119         lastMetaRead = index;
120         emit playlistChanged (index);
121     }
122 }
123
124
125 void PlaylistManager::metaStateChanged(Phonon::State newState, Phonon::State /*oldState*/)
126 {
127     // NOTE: This is an ugly hack, since the metaInformationResolver doesn't properly load the assigned source when it's in the error state
128     // In order to properly read the next file we have to set it as current source again when the resolver entered the stopped state after the error
129     static bool wasError = false;
130     if (wasError && newState == Phonon::StoppedState)
131     {
132         metaInformationResolver->setCurrentSource(items[lastMetaRead].source);
133         wasError = false;
134         return;
135     }
136     if (newState == Phonon::ErrorState)
137     {
138         wasError = true;
139     }
140
141     if (newState != Phonon::StoppedState && newState != Phonon::ErrorState)
142     {
143         return;
144     }
145
146     int index = lastMetaRead;
147
148     QMap<QString, QString> metaData = metaInformationResolver->metaData();
149
150
151     if (index >= 0 && newState != Phonon::ErrorState && index < items.size ())
152     {
153         items[index].artist = metaData.value("ARTIST");
154         items[index].title = metaData.value("TITLE");
155         items[index].album = metaData.value("ALBUM");
156 /*        items[index].year = metaData.value("DATE");
157         items[index].genre = metaData.value("GENRE");
158         qDebug () << "Meta " << metaData;
159         qDebug () << "Info is: " << items[index].year << " - " << items[index].genre;*/
160         if (!metaData.isEmpty())
161             items[index].playable = true;
162         emit itemUpdated (index);
163     }
164     if (index >= 0 && items.size () > index + 1)
165     {
166         metaInformationResolver->setCurrentSource(items[index + 1].source);
167         lastMetaRead = index + 1;
168     }
169 }
170
171 bool PlaylistManager::savePlaylist(const QString& filenam)
172 {
173     QString filename = filenam;
174     if (filename.isEmpty())
175         return false;
176     bool writepls = false;
177     if (filename.length() < 4 || (filename.right(4).toLower() != ".m3u" && filename.right(4).toLower() != ".pls"))
178     {
179         filename += ".pls";
180         writepls = true;
181     }
182     else if (filename.right(4).toLower() == ".pls")
183         writepls = true;
184     QFile f (filename);
185     try
186     {
187         f.open(QFile::WriteOnly);
188         if (writepls)
189         {
190             f.write ("[playlist]\n");
191             f.write (QString ("NumberOfEntries=%1\n").arg (items.size ()).toAscii());
192         }
193         for (int i = 0; i < items.size(); ++i)
194         {
195             if (writepls)
196             {
197                 f.write (QString ("File%1=%2\n").arg (i + 1).arg (items[i].uri).toAscii());
198                 f.write (QString ("Title%1=%2 - %3 - %4\n").arg (i + 1).arg (items[i].artist).arg (items[i].title).arg (items[i].album).toAscii());
199             }
200             else
201             {
202                 f.write (items[i].uri.toAscii());
203                 f.write ("\n");
204             }
205         }
206         if (writepls)
207             f.write ("Version=2\n");
208         f.close ();
209         return true;
210     }
211     catch (...)
212     {
213     }
214     return false;
215 }
216
217 void PlaylistManager::loadPlaylist(const QString& filename)
218 {
219     clearPlaylist();
220     if (filename.right(4).toLower() == ".m3u")
221         appendPlaylist(filename);
222     else if (filename.right(4).toLower() == ".pls")
223         appendPlaylistPLS(filename);
224     if (!items.isEmpty())
225     {
226         metaInformationResolver->setCurrentSource(items.at(0).source);
227         lastMetaRead = 0;
228     }
229     emit playlistChanged (0);
230 }
231
232 void PlaylistManager::addPlaylist(const QString& filename)
233 {
234     int index = items.size();
235     if (filename.right(4).toLower() == ".m3u")
236         appendPlaylist(filename);
237     else if (filename.right(4).toLower() == ".pls")
238         appendPlaylistPLS(filename);
239     if (items.size () > index)
240     {
241         metaInformationResolver->setCurrentSource(items.at(index).source);
242         lastMetaRead = index;
243         emit playlistChanged (index);
244     }
245 }
246
247 void PlaylistManager::appendPlaylist(const QString& filename)
248 {
249     QFile f(filename);
250     if (!f.open (QFile::ReadOnly))
251         return;
252     QString tmp = f.readAll();
253     f.close ();
254     QStringList lines = tmp.split("\n");
255     foreach (QString l, lines)
256     {
257         if (l.isEmpty() || (!QFileInfo (l).exists() && (l.indexOf("http") != 0))
258             || l.trimmed().startsWith("#"))
259         {
260             continue;
261         }
262         l = l.replace("\r", "");
263         items.append(PlaylistItem (l));
264     }
265 }
266
267 void PlaylistManager::appendPlaylistPLS(const QString& filename)
268 {
269     QFile f(filename);
270     if (!f.open (QFile::ReadOnly))
271         return;
272     QString tmp = f.readAll();
273     f.close ();
274     QStringList lines = tmp.split("\n");
275     QMap<int, int> filemap;
276
277     foreach (QString l, lines)
278     {
279         if (l.isEmpty() || l.trimmed().toLower() == "[playlist]" || l.trimmed().toLower() == "version=2"
280             || l.trimmed().startsWith(";"))
281         {
282             continue;
283         }
284         l = l.replace("\r", "");
285         if (l.trimmed().toLower().left(4) == "file")
286         {
287             QStringList tokens = l.split('=');
288             if (tokens.size () < 2)
289                 continue;
290             tokens[0] = tokens[0].mid (4);
291             filemap.insert(tokens[0].toInt (), items.size ());
292             items.append(PlaylistItem (tokens[1]));
293         }
294         else if (l.trimmed().toLower().left(5) == "title")
295         {
296             QStringList tokens = l.split('=');
297             if (tokens.size () < 2)
298                 continue;
299             tokens[0] = tokens[0].mid (5);
300             int toupdate = filemap[tokens[0].toInt()];
301             QStringList metatok = tokens[1].split (" - ");
302             if (metatok.size() > 2 && toupdate >= 0 && toupdate < items.size ())
303             {
304                 items[toupdate].artist = metatok[0];
305                 items[toupdate].title = metatok[1];
306                 metatok = metatok.mid (2);
307                 items[toupdate].album = metatok.join (" - ");
308             }
309             else
310             {
311                 items[toupdate].title = metatok.join (" - ");
312             }
313         }
314     }
315 }
316
317
318 void PlaylistManager::clearPlaylist()
319 {
320     items.clear();
321     emit playlistChanged(0);
322 }
323
324 QStringList PlaylistManager::playlistStrings() const
325 {
326     QStringList ret;
327     for (int i = 0; i < items.size (); ++i)
328         ret << items[i].uri;
329     return ret;
330 }
331
332 void PlaylistManager::removeItem(int i)
333 {
334     items.removeAt (i);
335     emit itemRemoved(i);
336 }
337
338
339 bool PlaylistManager::fileSupported (const QString& fname) const
340 {
341     if (fname.lastIndexOf('.') < 0)
342         return false;
343     QString ext = fname.right(fname.size() - fname.lastIndexOf('.') - 1).toLower();
344     foreach (QString e, allowedExtensions)
345     {
346         if (ext == e)
347             return true;
348     }
349
350     return false;
351 }
352
353 bool PlaylistManager::moveItemUp (int i)
354 {
355     if (i)
356     {
357         PlaylistItem tmp = items[i - 1];
358         items[i - 1] = items[i];
359         items[i] = tmp;
360         return true;
361     }
362     return false;
363 }
364
365 bool PlaylistManager::moveItemDown (int i)
366 {
367     if (i < items.size () - 1)
368     {
369         PlaylistItem tmp = items[i + 1];
370         items[i + 1] = items[i];
371         items[i] = tmp;
372         return true;
373     }
374     return false;
375 }