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