Version bump
[someplayer] / src / coverfinder.cpp
1 /*
2  * SomePlayer - An alternate music player for Maemo 5
3  * Copyright (C) 2010 Nikolay (somebody) Tischenko <niktischenko@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19
20
21 #include "coverfinder.h"
22 #include <QDebug>
23 #include <QDir>
24 #include <QFileInfo>
25 #include <QtConcurrentRun>
26 #include <mpegfile.h>
27 #include <flacfile.h>
28 #include <oggfile.h>
29 #include <id3v2tag.h>
30 #include <mpeg/id3v2/frames/attachedpictureframe.h>
31
32 using namespace SomePlayer::DataObjects;
33
34 CoverFinder::CoverFinder(QObject *parent) :
35                 QObject(parent)
36 {
37         _defaultCover = QImage(":/images/defaultcover.png");
38         SUFFIXES << "png" << "jpg" << "jpeg" << "bmp" << "gif";
39         NAMES << "cover" << "folder" << "album";
40         DIRS << "cover" << "folder" << ".cover" << ".folder";
41         PRIOR_NAMES << "front" << "cover";
42         UNPRIOR_NAMES << "disk" << "back" << "booklet" << "cd";
43 }
44
45 bool CoverFinder::_find(QString path) {
46         QDir dir(path);
47         QFileInfoList items = dir.entryInfoList(QDir::Files);
48         QFileInfoList dirs = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllDirs | QDir::Hidden);
49         QFileInfoList pics;
50         QFileInfoList appropriate_pics;
51         foreach (QFileInfo item, items) {
52                 if (SUFFIXES.contains(item.suffix().toLower())) {
53                         pics << item;
54                         if (NAMES.contains(item.baseName().toLower())) {
55                                 emit found(QImage(item.absoluteFilePath()));
56                                 emit foundPath(item.absoluteFilePath());
57                                 return true;
58                         }
59                 }
60         }
61         foreach (QFileInfo item, pics) {
62                 QImage i(item.absoluteFilePath());
63                 if (0.8 <= (i.width()*1.0/(i.height()+1.0)) <= 1.2) {
64                         appropriate_pics << item;
65                 }
66         }
67         if (!appropriate_pics.isEmpty()) {
68                 foreach (QString name, PRIOR_NAMES) {
69                         foreach (QFileInfo item, appropriate_pics) {
70                                 if (item.baseName().toLower().contains(name)) {
71                                         emit found(QImage(item.absoluteFilePath()));
72                                         emit foundPath(item.absoluteFilePath());
73                                         return true;
74                                 }
75                         }
76                 }
77                 QFileInfoList unprior;
78                 foreach (QString name, UNPRIOR_NAMES) {
79                         foreach (QFileInfo item, appropriate_pics) {
80                                 if (item.baseName().toLower().contains(name)) {
81                                         unprior << item;
82                                 }
83                         }
84                 }
85                 if (appropriate_pics.size() > unprior.size()) {
86                         foreach (QFileInfo item, appropriate_pics) {
87                                 if (!unprior.contains(item)) {
88                                         emit found(QImage(item.absoluteFilePath()));
89                                         emit foundPath(item.absoluteFilePath());
90                                         return true;
91                                 }
92                         }
93                 }
94                 emit found(QImage(unprior.at(0).absoluteFilePath()));
95                 emit foundPath(unprior.at(0).absoluteFilePath());
96                 return true;
97         }
98         foreach(QFileInfo item, dirs) {
99                 if (DIRS.contains(item.fileName().toLower())) {
100                         if (_find(item.absoluteFilePath())) {
101                                 return true;
102                         }
103                 }
104         }
105         return false;
106 }
107
108 QImage &CoverFinder::defaultCover() {
109         return _defaultCover;
110 }
111
112 bool CoverFinder::_extract(QString file) {
113         QFileInfo info(file);
114         QString suffix = info.suffix().toLower();
115         TagLib::ID3v2::Tag *tag = NULL;
116         bool todo = false;
117         TagLib::File *f = NULL;
118         if (suffix == "mp3") {
119                 f = new TagLib::MPEG::File(QFile::encodeName(file).data(), true, TagLib::AudioProperties::Fast);
120                 if (f == NULL) return false;
121                 tag = ((TagLib::MPEG::File*)f)->ID3v2Tag();
122                 todo = f->isValid();
123         } else if (suffix == "flac") {
124                 f = new TagLib::FLAC::File(QFile::encodeName(file).data(), true, TagLib::AudioProperties::Fast);
125                 if (f == NULL) return false;
126                 tag = ((TagLib::FLAC::File*)f)->ID3v2Tag();
127                 todo = f->isValid();
128         }
129         if (todo && tag != NULL) {
130                 TagLib::ID3v2::FrameList l = tag->frameList("APIC");
131                 if (l.isEmpty())
132                         return false;
133                 TagLib::ID3v2::AttachedPictureFrame *pic = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(l.front());
134                 QImage img;
135                 img.loadFromData((const uchar *) pic->picture().data(), pic->picture().size());
136                 emit found(img);
137                 emit foundPath("");
138                 return true;
139         }
140         if (f != NULL) delete f;
141         return false;
142 }
143
144 void CoverFinder::find(Track track) {
145         QFileInfo filePath(track.source());
146         QtConcurrent::run(this, &CoverFinder::_async_find, filePath, track);
147 }
148
149 bool CoverFinder::_async_find(QFileInfo filePath, Track track) {
150         if (!_find(filePath.absolutePath()) && !_tfind(track.metadata().artist(), track.metadata().album()) &&
151             !_malfind(track) && !_extract(filePath.absoluteFilePath())) {
152                 emit found(_defaultCover);
153                 emit foundPath("");
154                 return false;
155         }
156         return true;
157 }
158
159 bool CoverFinder::_tfind(QString artist, QString album) {
160         QString aname = artist.toLower();
161         QString aalbum = album.toLower();
162         aname.replace("/", "");
163         QString aaname = aname+" - "+aalbum;
164         aaname.replace("/", "");
165         QString fname1 = QDir::homePath()+"/.covers/"+aaname+".jpg";
166         QString fname2 = QDir::homePath()+"/.covers/"+aname+".jpg";
167         if (QFile::exists(fname1)) {
168                 emit found(QImage(fname1));
169                 emit foundPath(fname1);
170                 return true;
171         } else if (QFile::exists(fname2)) {
172                 emit found(QImage(fname2));
173                 emit foundPath(fname2);
174                 return true;
175         }
176         return false;
177 }
178
179 bool CoverFinder::_malfind(Track track) {
180         QString path = QFileInfo(track.source()).absolutePath()+"/.mediaartlocal/"+track.mediaArtLocal();
181         if (QFile::exists(path)) {
182                 emit found(QImage(path));
183                 emit foundPath(path);
184                 return true;
185         }
186         path = QDir::homePath()+"/.cache/media-art/"+track.mediaArtLocal();
187         if (QFile::exists(path)) {
188                 emit found(QImage(path));
189                 emit foundPath(path);
190                 return true;
191         }
192         return false;
193 }