Ticket #3334: importicons.cpp

File importicons.cpp, 18.0 KB (added by anonymous, 17 years ago)
Line 
1extern "C"
2{
3#include <stdlib.h>
4#include <sys/stat.h>
5#include <sys/types.h>
6};
7
8#include <httpcomms.h>
9#include "mythcontext.h"
10#include "mythdbcon.h"
11#include "mythwidgets.h"
12
13#include <qapplication.h>
14#include <qregexp.h>
15#include <qlayout.h>
16#include <qdialog.h>
17#include <qcursor.h>
18#include <qbuffer.h>
19#include <qfileinfo.h>
20
21#include <mythwidgets.h>
22#include <mythdialogs.h>
23#include <mythwizard.h>
24
25#include "importicons.h"
26
27ImportIconsWizard::ImportIconsWizard(bool fRefresh)
28{
29    m_fResult = false;
30    addChild(m_ImportIcons = new ImportIcons(fRefresh));
31}
32
33bool ImportIconsWizard::initialise()
34{
35    if (m_ImportIcons->initialise())
36        return true;
37    else
38        return false;
39}
40
41MythDialog *ImportIconsWizard::dialogWidget(MythMainWindow *parent,
42                                     const char *widgetName)
43{
44    MythWizard *ret = (MythWizard*)ConfigurationWizard::dialogWidget(parent,widgetName);
45    connect(ret->finishButton(), SIGNAL(pressed()), this, SLOT(finishButtonPressed()));
46    return (MythDialog*)ret;
47}
48
49int ImportIconsWizard::exec()
50{
51    while ((ConfigurationDialog::exec() == QDialog::Accepted) && !m_fResult) {}
52    return QDialog::Rejected;
53}
54
55void ImportIconsWizard::finishButtonPressed()
56{
57    m_fResult = true;
58}
59
60const QString ImportIcons::url="http://services.mythtv.org/channel-icon/";
61
62ImportIcons::ImportIcons(bool fRefresh)
63{
64    m_fRefresh = fRefresh;
65
66    addChild(m_editName = new TransLineEditSetting(false));
67    m_editName->setLabel(QObject::tr("Name"));
68    m_editName->setHelpText(QObject::tr("Name of the icon file"));
69
70    addChild(m_listIcons = new TransListBoxSetting());
71    m_listIcons->setHelpText(QObject::tr("List of possible icon files"));
72
73    m_editManual = new TransLineEditSetting();
74    //m_editManual->setLabel(QObject::tr("Enter text"));
75    m_editManual->setHelpText(QObject::tr("Enter text here for the manual search"));
76
77    m_buttonManual = new TransButtonSetting();
78    m_buttonManual->setLabel(QObject::tr("&Search"));
79    m_buttonManual->setHelpText(QObject::tr("Manually search for the text"));
80
81    m_buttonSkip = new TransButtonSetting();
82    m_buttonSkip->setLabel(QObject::tr("S&kip"));
83    m_buttonSkip->setHelpText(QObject::tr("Skip this icon"));
84
85    HorizontalConfigurationGroup *hrz1 =
86        new HorizontalConfigurationGroup(false, false, true, true);
87
88    hrz1->addChild(m_editManual);
89    hrz1->addChild(m_buttonManual);
90    hrz1->addChild(m_buttonSkip);
91    addChild(hrz1);
92
93    connect(m_editManual, SIGNAL(valueChanged( const QString&)),
94            this, SLOT(  enableControls()));
95    connect(m_buttonManual, SIGNAL(pressed()), this, SLOT(manualSearch()));
96    connect(m_buttonSkip, SIGNAL(pressed()), this, SLOT(skip()));
97    connect(m_listIcons,SIGNAL(accepted(int)),this,
98             SLOT(menuSelection(int)));
99
100    enableControls();
101}
102
103bool ImportIcons::initialise()
104{
105    m_strChannelDir =  MythContext::GetConfDir()+ "/channels";
106    mkdir(MythContext::GetConfDir(),0776);
107    mkdir(m_strChannelDir,0776);
108    m_strChannelDir+="/";
109
110    if (!ping())
111    {
112        MythPopupBox::showOkPopup(gContext->GetMainWindow(),
113                                  tr("Bad Host"),
114                                  tr("Failed to connect to the remote server"));
115        return false;
116    }
117
118    if (initialLoad() > 0)
119    {
120        doLoad();
121        return true;
122    }
123    else
124        return false;
125}
126
127void ImportIcons::enableControls()
128{
129   if (m_editManual->getValue().length())
130       m_buttonManual->setEnabled(true);
131   else
132       m_buttonManual->setEnabled(false);
133   if (m_nCount < m_nMaxCount)
134   {
135       m_buttonSkip->setEnabled(true);
136       m_editName->setEnabled(true);
137       m_listIcons->setEnabled(true);
138       m_editManual->setEnabled(true);
139   }
140   else
141   {
142       m_buttonSkip->setEnabled(false);
143       m_editName->setEnabled(false);
144       m_listIcons->setEnabled(false);
145       m_editManual->setEnabled(false);
146       m_buttonManual->setEnabled(false);
147   }
148}
149
150void ImportIcons::manualSearch()
151{
152    QString str = m_editManual->getValue();
153    search(escape_csv(str));   
154}
155
156void ImportIcons::skip()
157{
158    doLoad();
159    enableControls();
160}
161
162void ImportIcons::menuSelection(int nIndex)
163{
164    SearchEntry entry = *(m_listSearch.at(nIndex));
165    if (!isBlocked((*m_iter).strIconCSV) &&
166         checkAndDownload(entry.strLogo))
167    {
168        CSVEntry entry2 = (*m_iter);
169        m_strMatches += QString("%1,%2,%3,%4,%5,%6,%7,%8,%9\n").
170                              arg(escape_csv(entry.strID)).
171                              arg(escape_csv(entry2.strName)).
172                              arg(escape_csv(entry2.strXmlTvId)).
173                              arg(escape_csv(entry2.strCallsign)).
174                              arg(escape_csv(entry2.strTransportId)).
175                              arg(escape_csv(entry2.strAtscMajorChan)).
176                              arg(escape_csv(entry2.strAtscMinorChan)).
177                              arg(escape_csv(entry2.strNetworkId)).
178                              arg(escape_csv(entry2.strServiceId));
179        m_nCount++;
180        m_iter++;
181        doLoad();
182        enableControls();
183    }
184    else
185        MythPopupBox::showOkPopup(gContext->GetMainWindow(),
186                            QObject::tr("Error downloading"),
187                            QObject::tr("Failed to download the icon file"));       
188   
189}
190
191unsigned ImportIcons::initialLoad()
192{
193    MSqlQuery query(MSqlQuery::InitCon());
194    query.prepare("SELECT chanid,name,xmltvid,callsign,"
195                  "dtv_multiplex.transportid, "
196                  "atsc_major_chan,atsc_minor_chan,dtv_multiplex.networkid,"
197                  "channel.serviceid, "
198                  "channel.mplexid, dtv_multiplex.mplexid,"
199                  "channel.icon, channel.visible FROM "
200                  "channel, dtv_multiplex WHERE "
201                  "channel.visible && "
202                  "channel.mplexid=dtv_multiplex.mplexid ORDER BY name");
203       
204    m_listEntries.clear();           
205    m_nCount=0;
206    m_nMaxCount=0;
207    if (query.exec() && query.isActive() && query.size() > 0)
208    {
209        while(query.next())
210        {
211            CSVEntry entry;
212
213            if (m_fRefresh)
214            {
215                QFileInfo file(query.value(11).toString());
216                if (file.exists())
217                    continue;
218            }
219           
220            entry.strChanId=query.value(0).toString();
221            entry.strName=query.value(1).toString();
222            entry.strXmlTvId=query.value(2).toString();
223            entry.strCallsign=query.value(3).toString();
224            entry.strTransportId=query.value(4).toString();
225            entry.strAtscMajorChan=query.value(5).toString();
226            entry.strAtscMinorChan=query.value(6).toString();
227            entry.strNetworkId=query.value(7).toString();
228            entry.strServiceId=query.value(8).toString();
229            entry.strIconCSV= QString("%1,%2,%3,%4,%5,%6,%7,%8,%9\n").
230                              arg(escape_csv(entry.strChanId)).
231                              arg(escape_csv(entry.strName)).
232                              arg(escape_csv(entry.strXmlTvId)).
233                              arg(escape_csv(entry.strCallsign)).
234                              arg(escape_csv(entry.strTransportId)).
235                              arg(escape_csv(entry.strAtscMajorChan)).
236                              arg(escape_csv(entry.strAtscMinorChan)).
237                              arg(escape_csv(entry.strNetworkId)).
238                              arg(escape_csv(entry.strServiceId));
239            entry.strNameCSV=escape_csv(entry.strName);
240//            VERBOSE(VB_IMPORTANT,QString("chanid %1").arg(entry.strIconCSV));
241
242            m_listEntries.append(entry);
243            m_nMaxCount++;
244        }
245    }
246    m_iter = m_listEntries.begin();
247    return m_nMaxCount;
248}
249
250bool ImportIcons::doLoad()
251{
252    bool fFinish = false;
253    m_listIcons->clearSelections();
254    m_editName->setValue("");
255    while (!fFinish && (m_iter != m_listEntries.end()))
256    {
257        if (findmissing((*m_iter).strIconCSV))
258        {
259            m_nCount++;
260            m_iter++;   
261        }
262        else
263        {
264            m_editName->setValue((*m_iter).strName);
265            search((*m_iter).strNameCSV);
266            fFinish = true;
267        }
268    }
269    if (m_iter==m_listEntries.end())
270    {
271        if (!m_strMatches.isEmpty())
272        {
273            int nVal = MythPopupBox::showOkCancelPopup(
274                            gContext->GetMainWindow(),
275                            QObject::tr("Submit information"),
276                            QObject::tr("You now have the opportunity to "
277                                        "transmit your choices  back to "
278                                        "mythtv.org so that others can "
279                                        "benefit from your selections."),
280                          true);       
281             if (nVal == 1)
282                 submit(m_strMatches);
283        }
284        return false;
285    }
286    else
287        return true;
288}
289
290QString ImportIcons::escape_csv(const QString& str)
291{
292    QRegExp rxDblForEscape("\"");
293    QString str2 = str;
294    str2.replace(rxDblForEscape,"\\\"");
295    return "\""+str2+"\"";
296}
297
298QStringList ImportIcons::extract_csv(const QString& strLine)
299{
300    QStringList ret;
301    //Clean up the line and split out the fields
302    QString str = strLine;
303
304    unsigned int pos = 0;
305    bool fFinish = false;
306    while(!fFinish)
307    {
308        str=str.stripWhiteSpace();
309        while(!fFinish)
310        {
311            QString strLeft;
312            switch (str.at(pos).unicode())
313            {
314            case '\\':
315                if (pos>=1)
316                    str.left(pos-1)+str.mid(pos+1);
317                else
318                    str=str.mid(pos+1);
319                pos+=2;
320                if (pos > str.length())
321                {
322                    strLeft = str.left(pos);
323                    if (strLeft.startsWith("\"") && strLeft.endsWith("\""))
324                        strLeft=strLeft.mid(1,strLeft.length()-2);
325                    ret.append(strLeft);
326                    fFinish = true;
327                }
328                break;
329            case ',':
330                strLeft = str.left(pos);
331                if (strLeft.startsWith("\"") && strLeft.endsWith("\""))
332                    strLeft=strLeft.mid(1,strLeft.length()-2);
333                ret.append(strLeft);
334                if ((pos+1) > str.length())
335                   fFinish = true;
336                str=str.mid(pos+1);
337                pos=0;
338                break;
339            default:
340                pos++;
341                if (pos > str.length())
342                {
343                    strLeft = str.left(pos);
344                    if (strLeft.startsWith("\"") && strLeft.endsWith("\""))
345                        strLeft=strLeft.mid(1,strLeft.length()-2);
346                    ret.append(strLeft);
347                    fFinish = true;
348                }
349            }
350        }
351    }
352    return ret;
353}
354
355
356QString ImportIcons::wget(QUrl& url,const QString& strParam )
357{
358    QByteArray raw;
359    QTextStream rawStream(raw,IO_WriteOnly);
360    rawStream << strParam;
361
362    QBuffer data(raw);
363    QHttpRequestHeader header;
364
365    header.setContentType(QString("application/x-www-form-urlencoded"));
366    header.setContentLength(raw.size());
367
368    header.setValue("User-Agent", "MythTV Channel Icon lookup bot");
369
370    QString str = HttpComms::postHttp(url,&header,&data);
371
372    return str;
373}
374
375bool ImportIcons::checkAndDownload(const QString& str)
376{
377    int iIndex = str.findRev('/');
378    QString str2;
379    if (iIndex < 0)
380        str2=str;
381    else
382        str2=str.mid(iIndex+1);
383
384    QString str3 = str;
385    QFileInfo file(m_strChannelDir+str2);
386
387    bool fRet;
388    if (!file.exists())
389        fRet = HttpComms::getHttpFile(m_strChannelDir+str2,str3);   
390    else
391        fRet = true;
392
393    if (fRet)
394    {
395        MSqlQuery query(MSqlQuery::InitCon());
396        QString  qstr = "UPDATE channel SET icon = :ICON "
397                        "WHERE chanid = :CHANID";
398
399        query.prepare(qstr);
400        query.bindValue(":ICON", m_strChannelDir+str2);
401        query.bindValue(":CHANID", (*m_iter).strChanId);
402
403        if (!query.exec())
404        {
405            MythContext::DBError("Error inserting channel icon", query);
406            return false;
407        }
408 
409    }
410    return fRet;
411}
412
413bool ImportIcons::ping()
414{
415    QString ping= ImportIcons::url+"/ping";
416    QString str = HttpComms::getHttp(ping);
417    QRegExp pattern("^\\d+\\n$");
418
419    if (str.isEmpty() || (pattern.search(str) >= 0))
420    {
421//        VERBOSE(VB_IMPORTANT, QString("Working ping"));
422        return true;
423    }
424    else
425    {
426        VERBOSE(VB_IMPORTANT, QString("Error from  : %1").arg(str));
427        return true;
428    }
429}
430
431bool ImportIcons::isBlocked(const QString& strParam)
432{
433    QString strParam1 = strParam;
434    QUrl::encode(strParam1);
435    QUrl url(ImportIcons::url+"/checkblock");
436    QString str = wget(url,"csv="+strParam1);
437    if (str.startsWith("Error",false))
438    {
439//        VERBOSE(VB_IMPORTANT, QString("Error from isBlocked : %1").arg(str));
440        return true;
441    }
442    else if (str.isEmpty() || str.startsWith("\r\n"))
443        return false;
444    else
445    {
446//        VERBOSE(VB_IMPORTANT, QString("Working isBlocked"));
447        int nVal = MythPopupBox::showOkCancelPopup(gContext->GetMainWindow(),
448                            QObject::tr("Icon is blocked"),
449                            QObject::tr("This combination of channel and icon "
450                                        "has been blocked by the MythTV "
451                                        "admins. The most common reason for "
452                                        "this is that there is a better match "
453                                        "available.\n "
454                                        "Are you still sure that you want to "
455                                        "use this icon?"),
456                          true);       
457        if (nVal == 0)
458            return false;
459        else
460            return true;
461    }
462}
463
464
465bool ImportIcons::lookup(const QString& strParam)
466{
467    HttpComms http;
468    QString strParam1 = "callsign="+strParam;
469    QUrl::encode(strParam1);
470    QUrl url(ImportIcons::url+"/lookup");
471
472    QString str = wget(url,strParam1);
473    if (str.isEmpty() || str.startsWith("Error",false))
474    {
475//        VERBOSE(VB_IMPORTANT, QString("Error from lookup : %1").arg(str));
476        return true;
477    }
478    else
479    {
480//        VERBOSE(VB_IMPORTANT, QString("Working lookup : %1").arg(str));
481        return false;
482    }
483}
484
485bool ImportIcons::search(const QString& strParam)
486{
487    HttpComms http;
488    QString strParam1 = strParam;
489    QUrl::encode(strParam1);
490    QUrl url(ImportIcons::url+"/search");
491
492    m_listSearch.clear();
493    m_listIcons->clearSelections();
494    QString str = wget(url,"s="+strParam1);
495    if (str.isEmpty() || str.startsWith("#") || str.startsWith("Error",false))
496    {
497//        VERBOSE(VB_IMPORTANT, QString("Error from search : %1").arg(str));
498        return false;
499    }
500    else
501    {
502//        VERBOSE(VB_IMPORTANT, QString("Working search : %1").arg(str));
503        QStringList strSplit=QStringList::split("\n",str);
504        for (QStringList::iterator begin=strSplit.begin();
505             begin!=strSplit.end();begin++)
506        {
507            if (*begin != "#" )
508            {
509                QStringList ret = extract_csv(*begin);
510//                VERBOSE(VB_IMPORTANT, QString(" search : %1 %2 %3").arg(ret[0]).arg(ret[1]).arg(ret[2]));
511                SearchEntry entry;
512                entry.strID=ret[0];
513                entry.strName=ret[1];
514                entry.strLogo=ret[2];
515                m_listSearch.append(entry);
516                m_listIcons->addSelection(entry.strName);
517            }
518        }
519        return true;
520    }
521}
522
523bool ImportIcons::findmissing(const QString& strParam)
524{
525    HttpComms http;
526    QString strParam1 = strParam;
527    QUrl::encode(strParam1);
528    QUrl url(ImportIcons::url+"/findmissing");
529
530    QString str = wget(url,"csv="+strParam1);
531    if (str.isEmpty() || str.startsWith("#") || str.startsWith("Error",false))
532    {
533//        VERBOSE(VB_IMPORTANT, QString("Error from findmissing : %1").arg(str));
534        return false;
535    }
536    else
537    {
538//        VERBOSE(VB_IMPORTANT, QString("Working findmissing : %1").arg(str));
539        QStringList strSplit=QStringList::split("\n",str);
540        for (QStringList::iterator begin=strSplit.begin();
541             begin!=strSplit.end();begin++)
542        {
543            if (*begin != "#" )
544            {
545                QStringList ret = extract_csv(*begin);
546//                VERBOSE(VB_IMPORTANT, QString(" findmissing : %1 %2 %3 %4 %5").arg(ret[0]).arg(ret[1]).arg(ret[2]).arg(ret[3]).arg(ret[4]));
547                checkAndDownload(ret[4]);
548            }
549        }
550        return true;
551    }
552}
553
554bool ImportIcons::submit(const QString& strParam)
555{
556    HttpComms http;
557    QString strParam1 = strParam;
558    QUrl::encode(strParam1);
559    QUrl url(ImportIcons::url+"/submit");
560
561    QString str = wget(url,"csv="+strParam1);
562    if (str.isEmpty() || str.startsWith("#") || str.startsWith("Error",false))
563    {
564//        VERBOSE(VB_IMPORTANT, QString("Error from submit : %1").arg(str));
565        return false;
566    }
567    else
568    {
569//        VERBOSE(VB_IMPORTANT, QString("Working submit : %1").arg(str));
570        QStringList strSplit=QStringList::split("\n",str);
571        unsigned atsc =0, dvb =0, callsign =0, tv =0, xmltvid=0;
572        for (QStringList::iterator begin=strSplit.begin();
573             begin!=strSplit.end();begin++)
574        {
575            if (*begin != "#" )
576            {
577                QStringList strSplit2=QStringList::split(":",*begin);
578                QString s=strSplit2[0].stripWhiteSpace();
579                if (s=="a")
580                     atsc=strSplit2[1].toUInt();
581                else if (s=="c")
582                     callsign=strSplit2[1].toUInt();
583                else if (s=="d")
584                     dvb=strSplit2[1].toUInt();
585                else if (s=="t")
586                     tv=strSplit2[1].toUInt();
587                else if (s=="x")
588                     xmltvid=strSplit2[1].toUInt();
589            }
590        }
591//        VERBOSE(VB_IMPORTANT, QString("working submit : atsc=%1 callsign=%2 dvb=%3 tv=%4 xmltvid=%5").arg(atsc).arg(callsign).arg(dvb).arg(tv).arg(xmltvid));
592        return true;
593    }
594}