Initial commit
[maelyrica:maelyrica.git] / lyricsDownloader.cpp
1 /* File: lyricsDownloader.cpp
2  *
3  * This file is part of MaeLyrica.
4  *
5  * Copyright (C) 2012 Marcin Mielniczuk.
6  *
7  * MaeLyrica is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, version 3 of the License.
10  *
11  * MaeLyrica 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 MaeLyrica.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "lyricsDownloader.h"
21 #include <curl/curl.h>
22 #include <QDebug>
23 #include <iostream>
24 #include <QFile>
25 #include <QSettings>
26
27 using namespace std;
28
29 #define DEBUG 1
30
31 /////////////////////////////////////////////////////////////////////////////
32
33 const QString lyricsDownloader::OMPLyricsRoot("/home/user/.lyrics/");
34
35 inline void lyricsDownloader::setData(const string &a, const string &t)
36 {
37     artist = a;
38     track = t;
39 }
40
41 size_t lyricsDownloader::write_data_to_var(char *ptr, size_t size, size_t nmemb, void *userdata)
42 {
43     ostringstream * stream = (ostringstream*) userdata;
44     size_t count = size * nmemb;
45     stream->write(ptr, count);
46     return count;
47 }
48
49 QString lyricsDownloader::cleanQString(const QString qstr)
50 {
51     return qstr.toLower().replace("&", "and").remove(QRegExp("\\([^)]*\\)")).remove(QRegExp("[\\W_]"));
52 }
53
54 void lyricsDownloader::cleanTitle()
55 {
56     cleanedTitle = cleanQString( QString::fromStdString(artist));
57     cleanedTitle += "-";
58     cleanedTitle += cleanQString(QString::fromStdString(track));
59 }
60
61
62 bool lyricsDownloader::lyricsExistOMP()
63 {
64     lyricsUrlOMP = OMPLyricsRoot + cleanedTitle + ".txt";
65     return QFile(lyricsUrlOMP).exists();
66 }
67
68 bool lyricsDownloader::saveLyricsMaeLyrica()
69 {
70     QSettings("Marcin Mielniczuk", "MaeLyrica").setValue("lyrics/" + cleanedTitle, lyrics);
71     return true;
72 }
73
74 bool lyricsDownloader::saveLyricsOMP() // TODO finish
75 {
76     QFile lyricsFile(lyricsUrlOMP);
77     if (! lyricsFile.open(QIODevice::WriteOnly)) return false;
78     QTextStream lyricsFileStream(&lyricsFile);
79     lyricsFileStream << lyrics;
80     if (lyricsFileStream.status() == QTextStream::Ok) return true;
81     else return false;
82 }
83
84 ///////////////////////////////////////////////////////////////////////////////
85
86 const string AZLyricsDownloader::rooturl("http://azlyrics.com/lyrics");
87 const QRegExp AZLyricsDownloader::endcomment("<!-- end of lyrics -->");
88 const QRegExp AZLyricsDownloader::startcomment("<!-- start of lyrics -->");
89
90
91
92 short AZLyricsDownloader::perform()
93 {
94     CURLcode dlret; // return value from curl
95     dlret = download(); // it doesn't fail yet to recognize
96
97     if ( parse() != 0)
98     {
99         if ( dlret != 0 ) // Error while downloading and parsing means that the lyrics are just not there
100         {
101             qDebug() << "The lyrics are not present in AZLyrics.com database\n";
102             return 3;
103         }
104         qDebug() << "Error while parsing lyrics\n";
105         return 2;
106     }
107     if (dlret != 0) // just error while downloading
108     {
109         qDebug() << "Error while downloading lyrics\n";
110         return 1;
111     }
112
113     //qDebug() << lyrics();
114     emit lyricsChanged(lyrics);
115     return 0;
116 }
117
118 string AZLyricsDownloader::toProviderCode() const
119 {
120     string artist_www, track_www;
121     //overrides
122     if (artist == "The Offspring")
123         artist_www = "offspring";
124     else // normal case
125     {
126         int length = artist.length(); // lenght of a parameter
127         string::const_iterator cptr = artist.begin();
128         for (int i = 0; i < length; i++, cptr++)
129         {
130             if ( isalnum(*cptr) == false ) continue; // non alphanumeric, skip
131             artist_www += tolower(*cptr);
132         }
133     }
134     // here may be overrides for tracks
135         int length = track.length();
136         const char * cptr = &track[0];
137         for (int i = 0; i < length; i++, cptr++)
138         {
139             if ( isalnum(*cptr) == false ) continue;
140             track_www += tolower(*cptr);
141         }
142
143         return (rooturl + "/" + artist_www + "/" + track_www + ".html");
144
145 }
146
147 CURLcode AZLyricsDownloader::download()
148 {
149     CURL * handle;
150     CURLcode err;
151     ostringstream buff;
152     lyrics_str = new string;
153     handle = curl_easy_init();
154     if (! handle) return static_cast<CURLcode>(-1);
155     // set verbose if debug on
156     curl_easy_setopt( handle, CURLOPT_VERBOSE, DEBUG );
157     curl_easy_setopt( handle, CURLOPT_URL, toProviderCode().c_str() ); // set the download url to the generated one
158     curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buff);
159     curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &AZLyricsDownloader::write_data_to_var); // set the function writing to ostringstream
160     curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1); // curl needs to follow redirects with this provider
161     curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
162     err = curl_easy_perform(handle);
163     curl_easy_cleanup(handle);
164
165     // copy the contents to text variable
166     *lyrics_str = buff.str();
167     return err;
168 }
169
170 bool AZLyricsDownloader::parse()
171 {
172     istringstream temp(*lyrics_str);
173     delete lyrics_str;
174     lyrics_str = 0;
175     string line;
176     QString lyrics_QStr, line_QStr;
177     while ( temp.good() ) // looking for comment about starting lyrics
178     {
179         getline(temp, line);
180         if (QString::fromStdString(line).contains(startcomment)) break;
181     }
182     if ( !temp.good() ) return 1; // something went wrong, returning blank string
183     // get every line until endcomment
184     while ( temp.good() )
185     {
186         getline(temp, line);
187         if (( line_QStr = QString::fromStdString(line) ).contains(endcomment)) break;
188         lyrics_QStr += line_QStr.remove(QRegExp("<br[ ]{0,1}[/]{0,1}>"));
189         lyrics_QStr += "\n";
190     }
191     if ( !temp.good() ) return 1; // something went wrong, returning blank string
192
193     lyrics = lyrics_QStr; // after parsing all the work will be done using QStrings
194     return 0;
195 }