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