Exemplo n.º 1
0
void ManualSchedule::recordClicked(void)
{
    QDateTime endts = m_startDateTime
                      .addSecs(max(m_durationSpin->GetIntValue() * 60, 60));

    if (m_channelList->GetCurrentPos() >= m_chanids.size())
    {
        LOG(VB_GENERAL, LOG_ERR, "Channel out of range.");
        return; // this can happen if there are no channels..
    }

    ProgramInfo p(m_titleEdit->GetText().trimmed(),
                  m_chanids[m_channelList->GetCurrentPos()],
                  m_startDateTime, endts);

    RecordingRule *record = new RecordingRule();
    record->LoadByProgram(&p);
    record->m_searchType = kManualSearch;

    MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
    ScheduleEditor *schededit = new ScheduleEditor(mainStack, record);
    if (schededit->Create())
    {
        mainStack->AddScreen(schededit);
        connect(schededit, SIGNAL(ruleSaved(int)), SLOT(scheduleCreated(int)));
    }
    else
        delete schededit;
}
Exemplo n.º 2
0
void ViewScheduled::deleteRule()
{
    MythUIButtonListItem *item = m_schedulesList->GetItemCurrent();

    if (!item)
        return;

    ProgramInfo *pginfo = qVariantValue<ProgramInfo*>(item->GetData());
    if (!pginfo)
        return;

    RecordingRule *record = new RecordingRule();
    if (!record->LoadByProgram(pginfo))
    {
        delete record;
        return;
    }

    QString message = tr("Delete '%1' %2 rule?").arg(record->m_title)
        .arg(toString(pginfo->GetRecordingRuleType()));

    MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");

    MythConfirmationDialog *okPopup = new MythConfirmationDialog(popupStack,
                                                                 message, true);

    okPopup->SetReturnEvent(this, "deleterule");
    okPopup->SetData(qVariantFromValue(record));

    if (okPopup->Create())
        popupStack->AddScreen(okPopup);
    else
        delete okPopup;
}
Exemplo n.º 3
0
void LookerUpper::CopyRuleInetrefsToRecordings()
{
    QMap< QString, ProgramInfo* > recMap;
    QMap< QString, uint32_t > inUseMap = ProgramInfo::QueryInUseMap();
    QMap< QString, bool > isJobRunning = ProgramInfo::QueryJobsRunning(JOB_COMMFLAG);

    ProgramList progList;

    LoadFromRecorded( progList, false, inUseMap, isJobRunning, recMap, -1 );

    for( int n = 0; n < (int)progList.size(); n++)
    {
        ProgramInfo *pginfo = new ProgramInfo(*(progList[n]));
        if (pginfo && pginfo->GetInetRef().isEmpty())
        {
            RecordingRule *rule = new RecordingRule();
            rule->LoadByProgram(pginfo);
            if (!rule->m_inetref.isEmpty())
            {
                QString msg = QString("%1").arg(pginfo->GetTitle());
                if (!pginfo->GetSubtitle().isEmpty())
                    msg += QString(": %1").arg(pginfo->GetSubtitle());
                msg += " has no inetref, but its recording rule does. Copying...";
                LOG(VB_GENERAL, LOG_INFO, msg);
                pginfo->SaveInetRef(rule->m_inetref);
                delete rule;
            }
            delete pginfo;
        }
    }
}
Exemplo n.º 4
0
void ProgramRecPriority::edit(MythUIButtonListItem *item)
{
    if (!item)
        return;

    ProgramRecPriorityInfo *pgRecInfo =
                        item->GetData().value<ProgramRecPriorityInfo*>();

    if (!pgRecInfo)
        return;

    RecordingRule *record = new RecordingRule();
    record->m_recordID = pgRecInfo->GetRecordingRuleID();
    if (record->m_searchType == kNoSearch)
        record->LoadByProgram(pgRecInfo);

    MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
    ScheduleEditor *schededit = new ScheduleEditor(mainStack, record);
    if (schededit->Create())
    {
        mainStack->AddScreen(schededit);
        connect(schededit, SIGNAL(ruleSaved(int)), SLOT(scheduleChanged(int)));
        connect(schededit, SIGNAL(ruleDeleted(int)), SLOT(scheduleChanged(int)));
    }
    else
        delete schededit;
}
Exemplo n.º 5
0
/**
*  \brief Creates a dialog for editing an override recording schedule
*/
void ScheduleCommon::MakeOverride(RecordingInfo *recinfo)
{
    if (!recinfo || !recinfo->GetRecordingRuleID())
        return;

    RecordingRule *recrule = new RecordingRule();

    if (!recrule->LoadByProgram(static_cast<ProgramInfo*>(recinfo)))
        LOG(VB_GENERAL, LOG_ERR, "Failed to load by program info");

    if (!recrule->MakeOverride())
    {
        LOG(VB_GENERAL, LOG_ERR, "Failed to make Override");
        delete recrule;
        return;
    }
    recrule->m_type = kOverrideRecord;

    MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
    ScheduleEditor *schededit = new ScheduleEditor(mainStack, recrule);
    if (schededit->Create())
        mainStack->AddScreen(schededit);
    else
        delete schededit;
}
Exemplo n.º 6
0
void ProgLister::ShowDeleteRuleMenu(void)
{
    ProgramInfo *pi = GetCurrent();

    if (!pi || !pi->GetRecordingRuleID())
        return;

    RecordingRule *record = new RecordingRule();
    if (!record->LoadByProgram(pi))
    {
        delete record;
        return;
    }

    QString message = tr("Delete '%1' %2 rule?").arg(record->m_title)
        .arg(toString(pi->GetRecordingRuleType()));

    MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");

    MythConfirmationDialog *okPopup = new MythConfirmationDialog(
        popupStack, message, true);

    okPopup->SetReturnEvent(this, "deleterule");
    okPopup->SetData(qVariantFromValue(record));

    if (okPopup->Create())
        popupStack->AddScreen(okPopup);
    else
        delete okPopup;
}
Exemplo n.º 7
0
/**
*  \brief Creates a dialog for editing an override recording schedule
*/
void ScheduleCommon::MakeOverride(RecordingInfo *recinfo, bool startActive)
{
    if (!recinfo || !recinfo->GetRecordingRuleID())
        return;

    RecordingRule *recrule = new RecordingRule();
    
    if (!recrule->LoadByProgram(static_cast<ProgramInfo*>(recinfo)))
        VERBOSE(VB_IMPORTANT, QString("Failed to load by program info"));
    
    if (!recrule->MakeOverride())
    {
        VERBOSE(VB_IMPORTANT, QString("Failed to make Override"));
        delete recrule;
        return;
    }
    if (startActive)
        recrule->m_type = kOverrideRecord;

    MythScreenStack *mainStack = GetMythMainWindow()->GetMainStack();
    ScheduleEditor *schededit = new ScheduleEditor(mainStack, recrule);
    if (schededit->Create())
        mainStack->AddScreen(schededit);
    else
        delete schededit;
}
Exemplo n.º 8
0
DTC::RecRule* Dvr::GetRecordSchedule( uint      nRecordId,
                                      QString   sTemplate,
                                      int       nRecordedId,
                                      int       nChanId,
                                      QDateTime dStartTimeRaw,
                                      bool      bMakeOverride )
{
    RecordingRule rule;
    QDateTime dStartTime = dStartTimeRaw.toUTC();

    if (nRecordId > 0)
    {
        rule.m_recordID = nRecordId;
        if (!rule.Load())
            throw QString("Record ID does not exist.");
    }
    else if (!sTemplate.isEmpty())
    {
        if (!rule.LoadTemplate(sTemplate))
            throw QString("Template does not exist.");
    }
    else if (nRecordedId > 0) // Loads from the Recorded/Recorded Program Table
    {
        // Despite the use of ProgramInfo, this only applies to Recordings.
        ProgramInfo recInfo(nRecordedId);
        if (!rule.LoadByProgram(&recInfo))
            throw QString("Recording does not exist");
    }
    else if (nChanId > 0 && dStartTime.isValid()) // Loads from Program Table, should NOT be used with recordings
    {
        // Despite the use of RecordingInfo, this only applies to programs in the
        // present or future, not to recordings? Confused yet?
        RecordingInfo::LoadStatus status;
        RecordingInfo info(nChanId, dStartTime, false, 0, &status);
        if (status != RecordingInfo::kFoundProgram)
            throw QString("Program does not exist.");
        RecordingRule *pRule = info.GetRecordingRule();
        if (bMakeOverride && rule.m_type != kSingleRecord &&
            rule.m_type != kOverrideRecord && rule.m_type != kDontRecord)
            pRule->MakeOverride();
        rule = *pRule;
    }
    else
    {
        throw QString("Invalid request.");
    }

    DTC::RecRule *pRecRule = new DTC::RecRule();
    FillRecRuleInfo( pRecRule, &rule );

    return pRecRule;
}
Exemplo n.º 9
0
DTC::RecRuleList* Dvr::GetRecordScheduleList( int nStartIndex,
                                              int nCount      )
{
    vector<ProgramInfo *> infoList;
    RemoteGetAllScheduledRecordings(infoList);

    // ----------------------------------------------------------------------
    // Build Response
    // ----------------------------------------------------------------------

    DTC::RecRuleList *pRecRules = new DTC::RecRuleList();

    nStartIndex   = min( nStartIndex, (int)infoList.size() );
    nCount        = (nCount > 0) ? min( nCount, (int)infoList.size() ) : infoList.size();
    int nEndIndex = min((nStartIndex + nCount), (int)infoList.size() );

    for( int n = nStartIndex; n < nEndIndex; n++)
    {
        RecordingRule *rule = new RecordingRule();
        ProgramInfo *pInfo = infoList[ n ];
        rule->LoadByProgram(pInfo);

        if (pInfo != NULL)
        {
            DTC::RecRule *pRecRule = pRecRules->AddNewRecRule();

            FillRecRuleInfo( pRecRule, rule );

            delete rule;
            delete pInfo;
        }
    }

    // ----------------------------------------------------------------------

    pRecRules->setStartIndex    ( nStartIndex     );
    pRecRules->setCount         ( nCount          );
    pRecRules->setTotalAvailable( infoList.size() );
    pRecRules->setAsOf          ( QDateTime::currentDateTime() );
    pRecRules->setVersion       ( MYTH_BINARY_VERSION );
    pRecRules->setProtoVer      ( MYTH_PROTO_VERSION  );

    return pRecRules;
}
Exemplo n.º 10
0
LookupType GuessLookupType(ProgramInfo *pginfo)
{
    LookupType ret = kUnknownVideo;

    QString catType = pginfo->GetCategoryType();
    if (catType.isEmpty())
        catType = pginfo->QueryCategoryType();

    if ((!pginfo->GetSubtitle().isEmpty() || pginfo->GetEpisode() > 0) &&
       (catType == "series" || catType == "tvshow" ||
        catType == "show"))
        ret = kProbableTelevision;
    else if (catType == "movie" || catType == "film")
        ret = kProbableMovie;
    else if (pginfo->GetSeason() > 0 || pginfo->GetEpisode() > 0 ||
        !pginfo->GetSubtitle().isEmpty())
        ret = kProbableTelevision;
    else
    {
        // Before committing to something being a movie, we
        // want to check its rule.  If the rule has a season
        // or episode number, but the recording doesn't,
        // and the rec doesn't have a subtitle, this is a
        // generic recording. If neither the rule nor the
        // recording have an inetref, season, episode, or
        // subtitle, it's *probably* a movie.  If it's some
        // weird combination of both, we've got to try everything.
        RecordingRule *rule = new RecordingRule();
        rule->LoadByProgram(pginfo);
        int ruleepisode = rule->m_episode;
        delete rule;

        if (ruleepisode == 0 && pginfo->GetEpisode() == 0 &&
            pginfo->GetSubtitle().isEmpty())
            ret = kProbableMovie;
        else if (ruleepisode > 0 && pginfo->GetSubtitle().isEmpty())
            ret = kProbableGenericTelevision;
        else
            ret = kUnknownVideo;
    }

    return ret;
}
Exemplo n.º 11
0
void LookerUpper::customEvent(QEvent *levent)
{
    if (levent->type() == MetadataFactoryMultiResult::kEventType)
    {
        MetadataFactoryMultiResult *mfmr = dynamic_cast<MetadataFactoryMultiResult*>(levent);

        if (!mfmr)
            return;

        MetadataLookupList list = mfmr->results;

        if (list.count() > 1)
        {
            int yearindex = -1;

            for (int p = 0; p != list.size(); ++p)
            {
                ProgramInfo *pginfo = list[p]->GetData().value<ProgramInfo *>();

                if (pginfo && !pginfo->GetSeriesID().isEmpty() &&
                    pginfo->GetSeriesID() == (list[p])->GetTMSref())
                {
                    MetadataLookup *lookup = list[p];
                    if (lookup->GetSubtype() != kProbableGenericTelevision)
                        pginfo->SaveSeasonEpisode(lookup->GetSeason(), lookup->GetEpisode());
                    pginfo->SaveInetRef(lookup->GetInetref());
                    m_busyRecList.removeAll(pginfo);
                    return;
                }
                else if (pginfo && pginfo->GetYearOfInitialRelease() != 0 &&
                         (list[p])->GetYear() != 0 &&
                         pginfo->GetYearOfInitialRelease() == (list[p])->GetYear())
                {
                    if (yearindex != -1)
                    {
                        LOG(VB_GENERAL, LOG_INFO, "Multiple results matched on year. No definite "
                                      "match could be found.");
                        m_busyRecList.removeAll(pginfo);
                        return;
                    }
                    else
                    {
                        LOG(VB_GENERAL, LOG_INFO, "Matched from multiple results based on year. ");
                        yearindex = p;
                    }
                }
            }

            if (yearindex > -1)
            {
                MetadataLookup *lookup = list[yearindex];
                ProgramInfo *pginfo = lookup->GetData().value<ProgramInfo *>();
                if (lookup->GetSubtype() != kProbableGenericTelevision)
                    pginfo->SaveSeasonEpisode(lookup->GetSeason(), lookup->GetEpisode());
                pginfo->SaveInetRef(lookup->GetInetref());
                m_busyRecList.removeAll(pginfo);
                return;
            }

            LOG(VB_GENERAL, LOG_INFO, "Unable to match this title, too many possible matches. "
                                      "You may wish to manually set the season, episode, and "
                                      "inetref in the 'Watch Recordings' screen.");

            ProgramInfo *pginfo = list[0]->GetData().value<ProgramInfo *>();

            if (pginfo)
            {
                m_busyRecList.removeAll(pginfo);
            }
        }
    }
    else if (levent->type() == MetadataFactorySingleResult::kEventType)
    {
        MetadataFactorySingleResult *mfsr =
            dynamic_cast<MetadataFactorySingleResult*>(levent);

        if (!mfsr)
            return;

        MetadataLookup *lookup = mfsr->result;

        if (!lookup)
            return;

        ProgramInfo *pginfo = lookup->GetData().value<ProgramInfo *>();

        // This null check could hang us as this pginfo would then never be
        // removed
        if (!pginfo)
            return;

        LOG(VB_GENERAL, LOG_DEBUG, "I found the following data:");
        LOG(VB_GENERAL, LOG_DEBUG,
            QString("        Input Title: %1").arg(pginfo->GetTitle()));
        LOG(VB_GENERAL, LOG_DEBUG,
            QString("        Input Sub:   %1").arg(pginfo->GetSubtitle()));
        LOG(VB_GENERAL, LOG_DEBUG,
            QString("        Title:       %1").arg(lookup->GetTitle()));
        LOG(VB_GENERAL, LOG_DEBUG,
            QString("        Subtitle:    %1").arg(lookup->GetSubtitle()));
        LOG(VB_GENERAL, LOG_DEBUG,
            QString("        Season:      %1").arg(lookup->GetSeason()));
        LOG(VB_GENERAL, LOG_DEBUG,
            QString("        Episode:     %1").arg(lookup->GetEpisode()));
        LOG(VB_GENERAL, LOG_DEBUG,
            QString("        Inetref:     %1").arg(lookup->GetInetref()));
        LOG(VB_GENERAL, LOG_DEBUG,
            QString("        User Rating: %1").arg(lookup->GetUserRating()));

        if (lookup->GetSubtype() != kProbableGenericTelevision)
            pginfo->SaveSeasonEpisode(lookup->GetSeason(), lookup->GetEpisode());
        pginfo->SaveInetRef(lookup->GetInetref());

        if (m_updaterules)
        {
            RecordingRule *rule = new RecordingRule();
            if (rule)
            {
                rule->LoadByProgram(pginfo);
                if (rule->m_inetref.isEmpty() &&
                    (rule->m_searchType == kNoSearch))
                {
                    rule->m_inetref = lookup->GetInetref();
                }
                rule->m_season = lookup->GetSeason();
                rule->m_episode = lookup->GetEpisode();
                rule->Save();

                delete rule;
            }
        }

        if (m_updateartwork)
        {
            ArtworkMap map = lookup->GetDownloads();
            SetArtwork(lookup->GetInetref(),
                       lookup->GetIsCollection() ? 0 : lookup->GetSeason(),
                       gCoreContext->GetMasterHostName(), map);
        }

        m_busyRecList.removeAll(pginfo);
    }
    else if (levent->type() == MetadataFactoryNoResult::kEventType)
    {
        MetadataFactoryNoResult *mfnr = dynamic_cast<MetadataFactoryNoResult*>(levent);

        if (!mfnr)
            return;

        MetadataLookup *lookup = mfnr->result;

        if (!lookup)
            return;

        ProgramInfo *pginfo = lookup->GetData().value<ProgramInfo *>();

        // This null check could hang us as this pginfo would then never be removed
        if (!pginfo)
            return;

        m_busyRecList.removeAll(pginfo);
    }
}
Exemplo n.º 12
0
void ProgDetails::loadPage(void)
{
    MSqlQuery query(MSqlQuery::InitCon());
    QString category_type, showtype, year, syndicatedEpisodeNum;
    QString rating, colorcode, title_pronounce;
    float stars = 0.0;
    int partnumber = 0, parttotal = 0;
    int audioprop = 0, videoprop = 0, subtype = 0, generic = 0;
    bool recorded = false;

    RecordingRule* record = nullptr;
    if (m_progInfo.GetRecordingRuleID())
    {
        record = new RecordingRule();
        record->LoadByProgram(&m_progInfo);
    }

    if (m_progInfo.GetFilesize())
        recorded = true;

    QString ptable = recorded ? "recordedprogram" : "program";

    if (m_progInfo.GetScheduledEndTime() != m_progInfo.GetScheduledStartTime())
    {
        query.prepare(QString("SELECT category_type, airdate, stars,"
                      " partnumber, parttotal, audioprop+0, videoprop+0,"
                      " subtitletypes+0, syndicatedepisodenumber, generic,"
                      " showtype, colorcode, title_pronounce"
                      " FROM %1 WHERE chanid = :CHANID AND"
                      " starttime = :STARTTIME ;").arg(ptable));

        query.bindValue(":CHANID",    m_progInfo.GetChanID());
        query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());

        if (query.exec() && query.next())
        {
            category_type = query.value(0).toString();
            year = query.value(1).toString();
            stars = query.value(2).toFloat();
            partnumber = query.value(3).toInt();
            parttotal = query.value(4).toInt();
            audioprop = query.value(5).toInt();
            videoprop = query.value(6).toInt();
            subtype = query.value(7).toInt();
            syndicatedEpisodeNum = query.value(8).toString();
            generic = query.value(9).toInt();
            showtype = query.value(10).toString();
            colorcode = query.value(11).toString();
            title_pronounce = query.value(12).toString();
        }
        else if (!query.isActive())
            MythDB::DBError("ProgDetails", query);

        rating = getRatings(
            recorded, m_progInfo.GetChanID(),
            m_progInfo.GetScheduledStartTime());
    }

    if (category_type.isEmpty() && !m_progInfo.GetProgramID().isEmpty())
    {
        QString prefix = m_progInfo.GetProgramID().left(2);

        if (prefix == "MV")
           category_type = "movie";
        else if (prefix == "EP")
           category_type = "series";
        else if (prefix == "SP")
           category_type = "sports";
        else if (prefix == "SH")
           category_type = "tvshow";
    }

    addItem(tr("Title"),
            m_progInfo.toString(ProgramInfo::kTitleSubtitle, " - "),
            ProgInfoList::kLevel1);

    addItem(tr("Title Pronounce"), title_pronounce, ProgInfoList::kLevel2);

    QString s = m_progInfo.GetDescription();

    QString attr;

    if (partnumber > 0)
        attr += tr("Part %1 of %2, ").arg(partnumber).arg(parttotal);

    if (!rating.isEmpty() && rating != "NR")
        attr += rating + ", ";
    if (category_type == "movie")
    {
        if (!year.isEmpty())
            attr += year + ", ";

    /* see #7810, was hardcoded to 4 star system, when every theme
     * uses 10 stars / 5 stars with half stars
     */
        if (stars > 0.0f)
            attr += tr("%n star(s)", "", roundf(stars * 10.0f)) + ", ";
    }
    if (!colorcode.isEmpty())
        attr += colorcode + ", ";

    if (audioprop & AUD_MONO)
        attr += tr("Mono") + ", ";
    if (audioprop & AUD_STEREO)
        attr += tr("Stereo") + ", ";
    if (audioprop & AUD_SURROUND)
        attr += tr("Surround Sound") + ", ";
    if (audioprop & AUD_DOLBY)
        attr += tr("Dolby Sound") + ", ";
    if (audioprop & AUD_HARDHEAR)
        attr += tr("Audio for Hearing Impaired") + ", ";
    if (audioprop & AUD_VISUALIMPAIR)
        attr += tr("Audio for Visually Impaired") + ", ";

    if (videoprop & VID_HDTV)
        attr += tr("HDTV") + ", ";
    if  (videoprop & VID_WIDESCREEN)
        attr += tr("Widescreen") + ", ";
    if  (videoprop & VID_AVC)
        attr += tr("AVC/H.264") + ", ";
    if  (videoprop & VID_720)
        attr += tr("720p Resolution") + ", ";
    if  (videoprop & VID_1080)
        attr += tr("1080i/p Resolution") + ", ";
    if  (videoprop & VID_DAMAGED)
        attr += tr("Damaged") + ", ";

    if (subtype & SUB_HARDHEAR)
        attr += tr("CC","Closed Captioned") + ", ";
    if (subtype & SUB_NORMAL)
        attr += tr("Subtitles Available") + ", ";
    if (subtype & SUB_ONSCREEN)
        attr += tr("Subtitled") + ", ";
    if (subtype & SUB_SIGNED)
        attr += tr("Deaf Signing") + ", ";

    if (generic && category_type == "series")
        attr += tr("Unidentified Episode") + ", ";
    else if (m_progInfo.IsRepeat())
        attr += tr("Repeat") + ", ";

    if (!attr.isEmpty())
    {
        attr.truncate(attr.lastIndexOf(','));
        s += " (" + attr + ")";
    }

    addItem(tr("Description"), s, ProgInfoList::kLevel1);

    QString actors, directors, producers, execProducers;
    QString writers, guestStars, hosts, adapters;
    QString presenters, commentators, guests;

    if (m_progInfo.GetScheduledEndTime() != m_progInfo.GetScheduledStartTime())
    {
        if (recorded)
            query.prepare("SELECT role,people.name FROM recordedcredits"
                          " AS credits"
                          " LEFT JOIN people ON credits.person = people.person"
                          " WHERE credits.chanid = :CHANID"
                          " AND credits.starttime = :STARTTIME"
                          " ORDER BY role;");
        else
            query.prepare("SELECT role,people.name FROM credits"
                          " LEFT JOIN people ON credits.person = people.person"
                          " WHERE credits.chanid = :CHANID"
                          " AND credits.starttime = :STARTTIME"
                          " ORDER BY role;");
        query.bindValue(":CHANID",    m_progInfo.GetChanID());
        query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());

        if (query.exec() && query.size() > 0)
        {
            QStringList plist;
            QString rstr, role, pname;

            while(query.next())
            {
                role = query.value(0).toString();
                /* The people.name column uses utf8_bin collation.
                 * Qt-MySQL drivers use QVariant::ByteArray for string-type
                 * MySQL fields marked with the BINARY attribute (those using a
                 * *_bin collation) and QVariant::String for all others.
                 * Since QVariant::toString() uses QString::fromAscii()
                 * (through QVariant::convert()) when the QVariant's type is
                 * QVariant::ByteArray, we have to use QString::fromUtf8()
                 * explicitly to prevent corrupting characters.
                 * The following code should be changed to use the simpler
                 * toString() approach, as above, if we do a DB update to
                 * coalesce the people.name values that differ only in case and
                 * change the collation to utf8_general_ci, to match the
                 * majority of other columns, or we'll have the same problem in
                 * reverse.
                 */
                pname = QString::fromUtf8(query.value(1)
                                          .toByteArray().constData());

                if (rstr != role)
                {
                    if (rstr == "actor")
                        actors = plist.join(", ");
                    else if (rstr == "director")
                        directors = plist.join(", ");
                    else if (rstr == "producer")
                        producers = plist.join(", ");
                    else if (rstr == "executive_producer")
                        execProducers = plist.join(", ");
                    else if (rstr == "writer")
                        writers = plist.join(", ");
                    else if (rstr == "guest_star")
                        guestStars = plist.join(", ");
                    else if (rstr == "host")
                        hosts = plist.join(", ");
                    else if (rstr == "adapter")
                        adapters = plist.join(", ");
                    else if (rstr == "presenter")
                        presenters = plist.join(", ");
                    else if (rstr == "commentator")
                        commentators = plist.join(", ");
                    else if (rstr == "guest")
                        guests =  plist.join(", ");

                    rstr = role;
                    plist.clear();
                }

                plist.append(pname);
            }
            if (rstr == "actor")
                actors = plist.join(", ");
            else if (rstr == "director")
                directors = plist.join(", ");
            else if (rstr == "producer")
                producers = plist.join(", ");
            else if (rstr == "executive_producer")
                execProducers = plist.join(", ");
            else if (rstr == "writer")
                writers = plist.join(", ");
            else if (rstr == "guest_star")
                guestStars = plist.join(", ");
            else if (rstr == "host")
                hosts = plist.join(", ");
            else if (rstr == "adapter")
                adapters = plist.join(", ");
            else if (rstr == "presenter")
                presenters = plist.join(", ");
            else if (rstr == "commentator")
                commentators = plist.join(", ");
            else if (rstr == "guest")
                guests =  plist.join(", ");
        }
    }
    addItem(tr("Actors"), actors, ProgInfoList::kLevel1);
    addItem(tr("Guest Star"), guestStars, ProgInfoList::kLevel1);
    addItem(tr("Guest"), guests, ProgInfoList::kLevel1);
    addItem(tr("Host"), hosts, ProgInfoList::kLevel1);
    addItem(tr("Presenter"), presenters, ProgInfoList::kLevel1);
    addItem(tr("Commentator"), commentators, ProgInfoList::kLevel1);
    addItem(tr("Director"), directors, ProgInfoList::kLevel1);
    addItem(tr("Producer"), producers, ProgInfoList::kLevel2);
    addItem(tr("Executive Producer"), execProducers, ProgInfoList::kLevel2);
    addItem(tr("Writer"), writers, ProgInfoList::kLevel2);
    addItem(tr("Adapter"), adapters, ProgInfoList::kLevel2);

    addItem(tr("Category"), m_progInfo.GetCategory(), ProgInfoList::kLevel1);

    query.prepare("SELECT genre FROM programgenres "
                  "WHERE chanid = :CHANID AND starttime = :STARTTIME "
                  "AND relevance > 0 ORDER BY relevance;");
    query.bindValue(":CHANID",    m_progInfo.GetChanID());
    query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());

    if (query.exec())
    {
        s.clear();
        while (query.next())
        {
            if (!s.isEmpty())
                s += ", ";
            s += query.value(0).toString();
        }
        addItem(tr("Genre"), s, ProgInfoList::kLevel1);
    }

    s.clear();
    if (!category_type.isEmpty())
    {
        s = category_type;
        if (!m_progInfo.GetSeriesID().isEmpty())
            s += "  (" + m_progInfo.GetSeriesID() + ")";
        if (!showtype.isEmpty())
            s += "  " + showtype;
    }
    addItem(tr("Type", "category_type"), s, ProgInfoList::kLevel1);

    s.clear();
    if (m_progInfo.GetSeason() > 0)
        s = QString::number(m_progInfo.GetSeason());
    addItem(tr("Season"), s, ProgInfoList::kLevel1);

    s.clear();
    if (m_progInfo.GetEpisode() > 0)
    {
        if (m_progInfo.GetEpisodeTotal() > 0)
            s = tr("%1 of %2").arg(m_progInfo.GetEpisode())
                              .arg(m_progInfo.GetEpisodeTotal());
        else
            s = QString::number(m_progInfo.GetEpisode());

    }
    addItem(tr("Episode"), s, ProgInfoList::kLevel1);

    addItem(tr("Syndicated Episode Number"), syndicatedEpisodeNum,
            ProgInfoList::kLevel1);

    s.clear();
    if (m_progInfo.GetOriginalAirDate().isValid() &&
        category_type != "movie")
    {
        s = MythDate::toString(m_progInfo.GetOriginalAirDate(),
                               MythDate::kDateFull | MythDate::kAddYear);
    }
    addItem(tr("Original Airdate"), s, ProgInfoList::kLevel1);

    addItem(tr("Program ID"), m_progInfo.GetProgramID(), ProgInfoList::kLevel1);

    // Begin MythTV information not found in the listings info
    QDateTime statusDate;
    if (m_progInfo.GetRecordingStatus() == RecStatus::WillRecord ||
        m_progInfo.GetRecordingStatus() == RecStatus::Pending)
        statusDate = m_progInfo.GetScheduledStartTime();

    RecordingType rectype = kSingleRecord; // avoid kNotRecording
    RecStatus::Type recstatus = m_progInfo.GetRecordingStatus();

    if (recstatus == RecStatus::PreviousRecording ||
        recstatus == RecStatus::NeverRecord ||
        recstatus == RecStatus::Unknown)
    {
        query.prepare("SELECT recstatus, starttime "
                      "FROM oldrecorded WHERE duplicate > 0 AND "
                      "future = 0 AND "
                      "((programid <> '' AND programid = :PROGRAMID) OR "
                      " (title <> '' AND title = :TITLE AND "
                      "  subtitle <> '' AND subtitle = :SUBTITLE AND "
                      "  description <> '' AND description = :DECRIPTION));");

        query.bindValue(":PROGRAMID",  m_progInfo.GetProgramID());
        query.bindValue(":TITLE",      m_progInfo.GetTitle());
        query.bindValue(":SUBTITLE",   m_progInfo.GetSubtitle());
        query.bindValue(":DECRIPTION", m_progInfo.GetDescription());

        if (!query.exec())
        {
            MythDB::DBError("showDetails", query);
        }
        else if (query.next())
        {
            if (recstatus == RecStatus::Unknown)
                recstatus = RecStatus::Type(query.value(0).toInt());

            if (recstatus == RecStatus::PreviousRecording ||
                recstatus == RecStatus::NeverRecord ||
                recstatus == RecStatus::Recorded)
            {
                statusDate = MythDate::as_utc(query.value(1).toDateTime());
            }
        }
    }

    if (recstatus == RecStatus::Unknown)
    {
        if (recorded)
        {
            recstatus = RecStatus::Recorded;
            statusDate = m_progInfo.GetScheduledStartTime();
        }
        else
        {
            // re-enable "Not Recording" status text
            rectype = m_progInfo.GetRecordingRuleType();
        }
    }

    s = RecStatus::toString(recstatus, rectype);

    if (statusDate.isValid())
        s += " " + MythDate::toString(statusDate, MythDate::kDateFull |
                                      MythDate::kAddYear);

    addItem(tr("MythTV Status"), s, ProgInfoList::kLevel1);

    QString recordingRule;
    QString lastRecorded;
    QString nextRecording;
    QString averageTimeShift;
    QString watchListScore;
    QString watchListStatus;
    QString searchPhrase;

    if (m_progInfo.GetRecordingRuleID())
    {
        recordingRule = QString("%1, ").arg(m_progInfo.GetRecordingRuleID());
        if (m_progInfo.GetRecordingRuleType() != kNotRecording)
            recordingRule += toString(m_progInfo.GetRecordingRuleType());
        if (!(record->m_title.isEmpty()))
            recordingRule += QString(" \"%2\"").arg(record->m_title);

        query.prepare("SELECT last_record, next_record, avg_delay "
                      "FROM record WHERE recordid = :RECORDID");
        query.bindValue(":RECORDID", m_progInfo.GetRecordingRuleID());

        if (query.exec() && query.next())
        {
            if (query.value(0).toDateTime().isValid())
                lastRecorded = MythDate::toString(
                    MythDate::as_utc(query.value(0).toDateTime()),
                    MythDate::kDateFull | MythDate::kAddYear);
            if (query.value(1).toDateTime().isValid())
                nextRecording = MythDate::toString(
                    MythDate::as_utc(query.value(1).toDateTime()),
                    MythDate::kDateFull | MythDate::kAddYear);
            if (query.value(2).toInt() > 0)
                averageTimeShift = tr("%n hour(s)", "",
                                                query.value(2).toInt());
        }
        if (recorded)
        {
            if (m_progInfo.GetRecordingPriority2() > 0)
                watchListScore =
                    QString::number(m_progInfo.GetRecordingPriority2());

            if (m_progInfo.GetRecordingPriority2() < 0)
            {
                switch (m_progInfo.GetRecordingPriority2())
                {
                    case wlExpireOff:
                        watchListStatus = tr("Auto-expire off");
                        break;
                    case wlWatched:
                        watchListStatus = tr("Marked as 'watched'");
                        break;
                    case wlEarlier:
                        watchListStatus = tr("Not the earliest episode");
                        break;
                    case wlDeleted:
                        watchListStatus = tr("Recently deleted episode");
                        break;
                }
            }
        }
        if (record->m_searchType != kManualSearch &&
            record->m_description != m_progInfo.GetDescription())
            searchPhrase = record->m_description;
    }
    addItem(tr("Recording Rule"), recordingRule, ProgInfoList::kLevel1);
    addItem(tr("Search Phrase"), searchPhrase, ProgInfoList::kLevel1);

    s.clear();
    if (m_progInfo.GetFindID())
    {
        QDateTime fdate(QDate(1970, 1, 1),QTime(12,0,0));
        fdate = fdate.addDays((int)m_progInfo.GetFindID() - 719528);
        s = QString("%1 (%2)").arg(m_progInfo.GetFindID())
            .arg(MythDate::toString(
                     fdate, MythDate::kDateFull | MythDate::kAddYear));
    }
    addItem(tr("Find ID"), s, ProgInfoList::kLevel2);

    addItem(tr("Last Recorded"), lastRecorded, ProgInfoList::kLevel2);
    addItem(tr("Next Recording"), nextRecording, ProgInfoList::kLevel2);
    addItem(tr("Average Time Shift"), averageTimeShift, ProgInfoList::kLevel2);
    addItem(tr("Watch List Score"), watchListScore, ProgInfoList::kLevel2);
    addItem(tr("Watch List Status"), watchListStatus, ProgInfoList::kLevel2);

    QString recordingHost;
    QString recordingInput;
    QString recordedFilename;
    QString recordedFileSize;
    QString recordingGroup;
    QString storageGroup;
    QString playbackGroup;
    QString recordingProfile;

    recordingHost = m_progInfo.GetHostname();
    recordingInput = m_progInfo.GetInputName();

    if (recorded)
    {
        recordedFilename = m_progInfo.GetBasename();
        recordedFileSize = QString("%1 ")
            .arg(m_progInfo.GetFilesize()/((double)(1<<30)),0,'f',2);
        recordedFileSize += tr("GB", "GigaBytes");

        query.prepare("SELECT profile FROM recorded"
                      " WHERE chanid = :CHANID"
                      " AND starttime = :STARTTIME;");
        query.bindValue(":CHANID",    m_progInfo.GetChanID());
        query.bindValue(":STARTTIME", m_progInfo.GetRecordingStartTime());

        if (query.exec() && query.next())
        {
            recordingProfile = m_progInfo.i18n(query.value(0).toString());
        }
        recordingGroup = m_progInfo.i18n(m_progInfo.GetRecordingGroup());
        storageGroup   = m_progInfo.i18n(m_progInfo.GetStorageGroup());
        playbackGroup  = m_progInfo.i18n(m_progInfo.GetPlaybackGroup());
    }
    else if (m_progInfo.GetRecordingRuleID())
    {
        recordingProfile =  record->m_recProfile;
    }
    addItem(tr("Recording Host"), recordingHost, ProgInfoList::kLevel2);
    addItem(tr("Recording Input"), recordingInput, ProgInfoList::kLevel2);
    addItem(tr("Recorded File Name"), recordedFilename, ProgInfoList::kLevel1);
    addItem(tr("Recorded File Size"), recordedFileSize, ProgInfoList::kLevel1);
    addItem(tr("Recording Profile"), recordingProfile, ProgInfoList::kLevel2);
    addItem(tr("Recording Group"), recordingGroup, ProgInfoList::kLevel1);
    addItem(tr("Storage Group"), storageGroup, ProgInfoList::kLevel2);
    addItem(tr("Playback Group"),  playbackGroup, ProgInfoList::kLevel2);

    PowerPriorities(ptable);

    delete record;
}
Exemplo n.º 13
0
void ProgDetails::loadPage(void)
{
    loadHTML();

    MSqlQuery query(MSqlQuery::InitCon());
    QString fullDateFormat = gCoreContext->GetSetting("DateFormat", "M/d/yyyy");
    if (!fullDateFormat.contains("yyyy"))
        fullDateFormat += " yyyy";
    QString category_type, showtype, year, epinum, rating, colorcode,
            title_pronounce;
    float stars = 0.0;
    int partnumber = 0, parttotal = 0;
    int audioprop = 0, videoprop = 0, subtype = 0, generic = 0;
    bool recorded = false;

    RecordingRule* record = NULL;
    if (m_progInfo.GetRecordingRuleID())
    {
        record = new RecordingRule();
        record->LoadByProgram(&m_progInfo);
    }

    if (m_progInfo.GetFilesize())
        recorded = true;

    if (m_progInfo.GetScheduledEndTime() != m_progInfo.GetScheduledStartTime())
    {
        QString ptable = "program";
        if (recorded)
            ptable = "recordedprogram";

        query.prepare(QString("SELECT category_type, airdate, stars,"
                      " partnumber, parttotal, audioprop+0, videoprop+0,"
                      " subtitletypes+0, syndicatedepisodenumber, generic,"
                      " showtype, colorcode, title_pronounce"
                      " FROM %1 WHERE chanid = :CHANID AND"
                      " starttime = :STARTTIME ;").arg(ptable));

        query.bindValue(":CHANID",    m_progInfo.GetChanID());
        query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());

        if (query.exec() && query.next())
        {
            category_type = query.value(0).toString();
            year = query.value(1).toString();
            stars = query.value(2).toDouble();
            partnumber = query.value(3).toInt();
            parttotal = query.value(4).toInt();
            audioprop = query.value(5).toInt();
            videoprop = query.value(6).toInt();
            subtype = query.value(7).toInt();
            epinum = query.value(8).toString();
            generic = query.value(9).toInt();
            showtype = query.value(10).toString();
            colorcode = query.value(11).toString();
            title_pronounce = query.value(12).toString();
        }
        else if (!query.isActive())
            MythDB::DBError("ProgDetails", query);

        rating = getRatings(
            recorded, m_progInfo.GetChanID(),
            m_progInfo.GetScheduledStartTime());
    }

    if (category_type.isEmpty() && !m_progInfo.GetProgramID().isEmpty())
    {
        QString prefix = m_progInfo.GetProgramID().left(2);

        if (prefix == "MV")
           category_type = "movie";
        else if (prefix == "EP")
           category_type = "series";
        else if (prefix == "SP")
           category_type = "sports";
        else if (prefix == "SH")
           category_type = "tvshow";
    }

    addItem("TITLE", tr("Title"),
            m_progInfo.toString(ProgramInfo::kTitleSubtitle, " - "));

    addItem("TITLE_PRONOUNCE", tr("Title Pronounce"), title_pronounce);

    QString s = m_progInfo.GetDescription();

    QString attr;

    if (partnumber > 0)
        attr += QString(tr("Part %1 of %2, ")).arg(partnumber).arg(parttotal);

    if (!rating.isEmpty() && rating != "NR")
        attr += rating + ", ";
    if (category_type == "movie")
    {
        if (!year.isEmpty())
            attr += year + ", ";

        if (stars > 0.0)
            attr += tr("%n star(s)", "", (int) (stars * 4.0)) + ", ";
    }
    if (!colorcode.isEmpty())
        attr += colorcode + ", ";

    if (audioprop & AUD_MONO)
        attr += tr("Mono") + ", ";
    if (audioprop & AUD_STEREO)
        attr += tr("Stereo") + ", ";
    if (audioprop & AUD_SURROUND)
        attr += tr("Surround Sound") + ", ";
    if (audioprop & AUD_DOLBY)
        attr += tr("Dolby Sound") + ", ";
    if (audioprop & AUD_HARDHEAR)
        attr += tr("Audio for Hearing Impaired") + ", ";
    if (audioprop & AUD_VISUALIMPAIR)
        attr += tr("Audio for Visually Impaired") + ", ";

    if (videoprop & VID_HDTV)
        attr += tr("HDTV") + ", ";
    if  (videoprop & VID_WIDESCREEN)
        attr += tr("Widescreen") + ", ";
    if  (videoprop & VID_AVC)
        attr += tr("AVC/H.264") + ", ";
    if  (videoprop & VID_720)
        attr += tr("720p Resolution") + ", ";
    if  (videoprop & VID_1080)
        attr += tr("1080i/p Resolution") + ", ";

    if (subtype & SUB_HARDHEAR)
        attr += tr("CC","Closed Captioned") + ", ";
    if (subtype & SUB_NORMAL)
        attr += tr("Subtitles Available") + ", ";
    if (subtype & SUB_ONSCREEN)
        attr += tr("Subtitled") + ", ";
    if (subtype & SUB_SIGNED)
        attr += tr("Deaf Signing") + ", ";

    if (generic && category_type == "series")
        attr += tr("Unidentified Episode") + ", ";
    else if (m_progInfo.IsRepeat())
        attr += tr("Repeat") + ", ";

    if (!attr.isEmpty())
    {
        attr.truncate(attr.lastIndexOf(','));
        s += " (" + attr + ")";
    }

    addItem("DESCRIPTION", tr("Description"), s);

    s.clear();
    if (!m_progInfo.GetCategory().isEmpty())
    {
        s = m_progInfo.GetCategory();

        query.prepare("SELECT genre FROM programgenres "
                      "WHERE chanid = :CHANID AND starttime = :STARTTIME "
                      "AND relevance > 0 ORDER BY relevance;");

        query.bindValue(":CHANID",    m_progInfo.GetChanID());
        query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());

        if (query.exec())
        {
            while (query.next())
                s += ", " + query.value(0).toString();
        }
    }
    addItem("CATEGORY", tr("Category"), s);

    s.clear();
    if (!category_type.isEmpty())
    {
        s = category_type;
        if (!m_progInfo.GetSeriesID().isEmpty())
            s += "  (" + m_progInfo.GetSeriesID() + ")";
        if (!showtype.isEmpty())
            s += "  " + showtype;
    }
    addItem("CATEGORY_TYPE", tr("Type", "category_type"), s);

    addItem("EPISODE", tr("Episode Number"), epinum);

    s.clear();
    if (m_progInfo.GetOriginalAirDate().isValid() &&
        category_type != "movie")
    {
        s = m_progInfo.GetOriginalAirDate().toString(fullDateFormat);
    }
    addItem("ORIGINAL_AIRDATE", tr("Original Airdate"), s);

    addItem("PROGRAMID", tr("Program ID"), m_progInfo.GetProgramID());

    QString actors, directors, producers, execProducers;
    QString writers, guestStars, hosts, adapters;
    QString presenters, commentators, guests;

    if (m_progInfo.GetScheduledEndTime() != m_progInfo.GetScheduledStartTime())
    {
        if (recorded)
            query.prepare("SELECT role,people.name FROM recordedcredits"
                          " AS credits"
                          " LEFT JOIN people ON credits.person = people.person"
                          " WHERE credits.chanid = :CHANID"
                          " AND credits.starttime = :STARTTIME"
                          " ORDER BY role;");
        else
            query.prepare("SELECT role,people.name FROM credits"
                          " LEFT JOIN people ON credits.person = people.person"
                          " WHERE credits.chanid = :CHANID"
                          " AND credits.starttime = :STARTTIME"
                          " ORDER BY role;");
        query.bindValue(":CHANID",    m_progInfo.GetChanID());
        query.bindValue(":STARTTIME", m_progInfo.GetScheduledStartTime());

        if (query.exec() && query.size() > 0)
        {
            QStringList plist;
            QString rstr, role, pname;

            while(query.next())
            {
                role = query.value(0).toString();
                /* The people.name column uses utf8_bin collation.
                 * Qt-MySQL drivers use QVariant::ByteArray for string-type
                 * MySQL fields marked with the BINARY attribute (those using a
                 * *_bin collation) and QVariant::String for all others.
                 * Since QVariant::toString() uses QString::fromAscii()
                 * (through QVariant::convert()) when the QVariant's type is
                 * QVariant::ByteArray, we have to use QString::fromUtf8()
                 * explicitly to prevent corrupting characters.
                 * The following code should be changed to use the simpler
                 * toString() approach, as above, if we do a DB update to
                 * coalesce the people.name values that differ only in case and
                 * change the collation to utf8_general_ci, to match the
                 * majority of other columns, or we'll have the same problem in
                 * reverse.
                 */
                pname = QString::fromUtf8(query.value(1)
                                          .toByteArray().constData());

                if (rstr != role)
                {
                    if (rstr == "actor")
                        actors = plist.join(", ");
                    else if (rstr == "director")
                        directors = plist.join(", ");
                    else if (rstr == "producer")
                        producers = plist.join(", ");
                    else if (rstr == "executive_producer")
                        execProducers = plist.join(", ");
                    else if (rstr == "writer")
                        writers = plist.join(", ");
                    else if (rstr == "guest_star")
                        guestStars = plist.join(", ");
                    else if (rstr == "host")
                        hosts = plist.join(", ");
                    else if (rstr == "adapter")
                        adapters = plist.join(", ");
                    else if (rstr == "presenter")
                        presenters = plist.join(", ");
                    else if (rstr == "commentator")
                        commentators = plist.join(", ");
                    else if (rstr == "guest")
                        guests =  plist.join(", ");

                    rstr = role;
                    plist.clear();
                }

                plist.append(pname);
            }
            if (rstr == "actor")
                actors = plist.join(", ");
            else if (rstr == "director")
                directors = plist.join(", ");
            else if (rstr == "producer")
                producers = plist.join(", ");
            else if (rstr == "executive_producer")
                execProducers = plist.join(", ");
            else if (rstr == "writer")
                writers = plist.join(", ");
            else if (rstr == "guest_star")
                guestStars = plist.join(", ");
            else if (rstr == "host")
                hosts = plist.join(", ");
            else if (rstr == "adapter")
                adapters = plist.join(", ");
            else if (rstr == "presenter")
                presenters = plist.join(", ");
            else if (rstr == "commentator")
                commentators = plist.join(", ");
            else if (rstr == "guest")
                guests =  plist.join(", ");
        }
    }
    addItem("ACTORS", tr("Actors"), actors);
    addItem("DIRECTOR", tr("Director"), directors);
    addItem("PRODUCER", tr("Producer"), producers);
    addItem("EXECUTIVE_PRODUCER", tr("Executive Producer"), execProducers);
    addItem("WRITER", tr("Writer"), writers);
    addItem("GUEST_STAR", tr("Guest Star"), guestStars);
    addItem("HOST", tr("Host"), hosts);
    addItem("ADAPTER", tr("Adapter"), adapters);
    addItem("PRESENTER", tr("Presenter"), presenters);
    addItem("COMMENTATOR", tr("Commentator"), commentators);
    addItem("GUEST", tr("Guest"), guests);

    // Begin MythTV information not found in the listings info
//    msg += "<br>";
    QDateTime statusDate;
    if (m_progInfo.GetRecordingStatus() == rsWillRecord)
        statusDate = m_progInfo.GetScheduledStartTime();

    RecordingType rectype = kSingleRecord; // avoid kNotRecording
    RecStatusType recstatus = m_progInfo.GetRecordingStatus();

    if (recstatus == rsPreviousRecording || recstatus == rsNeverRecord ||
        recstatus == rsUnknown)
    {
        query.prepare("SELECT recstatus, starttime "
                      "FROM oldrecorded WHERE duplicate > 0 AND "
                      "future = 0 AND "
                      "((programid <> '' AND programid = :PROGRAMID) OR "
                      " (title <> '' AND title = :TITLE AND "
                      "  subtitle <> '' AND subtitle = :SUBTITLE AND "
                      "  description <> '' AND description = :DECRIPTION));");

        query.bindValue(":PROGRAMID",  m_progInfo.GetProgramID());
        query.bindValue(":TITLE",      m_progInfo.GetTitle());
        query.bindValue(":SUBTITLE",   m_progInfo.GetSubtitle());
        query.bindValue(":DECRIPTION", m_progInfo.GetDescription());

        if (!query.exec())
        {
            MythDB::DBError("showDetails", query);
        }
        else if (query.next())
        {
            if (recstatus == rsUnknown)
                recstatus = RecStatusType(query.value(0).toInt());

            if (recstatus == rsPreviousRecording ||
                recstatus == rsNeverRecord ||
                recstatus == rsRecorded)
            {
                statusDate = query.value(1).toDateTime();
            }
        }
    }

    if (recstatus == rsUnknown)
    {
        if (recorded)
        {
            recstatus = rsRecorded;
            statusDate = m_progInfo.GetScheduledStartTime();
        }
        else
        {
            // re-enable "Not Recording" status text
            rectype = m_progInfo.GetRecordingRuleType();
        }
    }

    s = toString(recstatus, rectype);

    if (statusDate.isValid())
        s += " " + statusDate.toString(fullDateFormat);

    addItem("MYTHTV_STATUS", QString("MythTV " + tr("Status")), s);

    QString recordingRule;
    QString lastRecorded;
    QString nextRecording;
    QString averageTimeShift;
    QString watchListScore;
    QString watchListStatus;
    QString searchPhrase;

    if (m_progInfo.GetRecordingRuleID())
    {
        recordingRule = QString("%1, ").arg(m_progInfo.GetRecordingRuleID());
        if (m_progInfo.GetRecordingRuleType() != kNotRecording)
            recordingRule += toString(m_progInfo.GetRecordingRuleType());
        if (!(record->m_title.isEmpty()))
            recordingRule += QString(" \"%2\"").arg(record->m_title);

        query.prepare("SELECT last_record, next_record, avg_delay "
                      "FROM record WHERE recordid = :RECORDID");
        query.bindValue(":RECORDID", m_progInfo.GetRecordingRuleID());

        if (query.exec() && query.next())
        {
            if (query.value(0).toDateTime().isValid())
                lastRecorded = query.value(0).toDateTime().toString(fullDateFormat);
            if (query.value(1).toDateTime().isValid())
                nextRecording = query.value(1).toDateTime().toString(fullDateFormat);
            if (query.value(2).toInt() > 0)
                averageTimeShift = tr("%n hour(s)", "",
                                                query.value(2).toInt());
        }
        if (recorded)
        {
            if (m_progInfo.GetRecordingPriority2() > 0)
                watchListScore =
                    QString::number(m_progInfo.GetRecordingPriority2());

            if (m_progInfo.GetRecordingPriority2() < 0)
            {
                switch (m_progInfo.GetRecordingPriority2())
                {
                    case wlExpireOff:
                        watchListStatus = tr("Auto-expire off");
                        break;
                    case wlWatched:
                        watchListStatus = tr("Marked as 'watched'");
                        break;
                    case wlEarlier:
                        watchListStatus = tr("Not the earliest episode");
                        break;
                    case wlDeleted:
                        watchListStatus = tr("Recently deleted episode");
                        break;
                }
            }
        }
        if (record->m_searchType != kManualSearch &&
            record->m_description != m_progInfo.GetDescription())
        {
            searchPhrase = record->m_description
                .replace("<", "&lt;").replace(">", "&gt;").replace("\n", " ");
        }
    }
    addItem("RECORDING_RULE", tr("Recording Rule"), recordingRule);
    addItem("LAST_RECORDED", tr("Last Recorded"), lastRecorded);
    addItem("NEXT_RECORDING", tr("Next Recording"), nextRecording);
    addItem("AVERAGE_TIME_SHIFT", tr("Average Time Shift"), averageTimeShift);
    addItem("WATCH_LIST_SCORE", tr("Watch List Score"), watchListScore);
    addItem("WATCH_LIST_STATUS", tr("Watch List Status"), watchListStatus);
    addItem("SEARCH_PHRASE", tr("Search Phrase"), searchPhrase);

    s.clear();
    if (m_progInfo.GetFindID())
    {
        QDate fdate(1970, 1, 1);
        fdate = fdate.addDays((int)m_progInfo.GetFindID() - 719528);
        s = QString("%1 (%2)").arg(m_progInfo.GetFindID())
            .arg(fdate.toString(fullDateFormat));
    }
    addItem("FINDID", tr("Find ID"), s);

    QString recordingHost;
    QString recordedFilename;
    QString recordedFileSize;
    QString recordingGroup;
    QString storageGroup;
    QString playbackGroup;
    QString recordingProfile;

    if (recorded)
    {
        recordingHost = m_progInfo.GetHostname();
        recordedFilename = m_progInfo.GetBasename();
        recordedFileSize = QString("%1 ")
            .arg(m_progInfo.GetFilesize()/((double)(1<<30)),0,'f',2);
        recordedFileSize += tr("GB", "GigaBytes");

        query.prepare("SELECT profile FROM recorded"
                      " WHERE chanid = :CHANID"
                      " AND starttime = :STARTTIME;");
        query.bindValue(":CHANID",    m_progInfo.GetChanID());
        query.bindValue(":STARTTIME", m_progInfo.GetRecordingStartTime());

        if (query.exec() && query.next())
        {
            recordingProfile = m_progInfo.i18n(query.value(0).toString());
        }
        recordingGroup = m_progInfo.i18n(m_progInfo.GetRecordingGroup());
        storageGroup   = m_progInfo.i18n(m_progInfo.GetStorageGroup());
        playbackGroup  = m_progInfo.i18n(m_progInfo.GetPlaybackGroup());
    }
    else if (m_progInfo.GetRecordingRuleID())
    {
        recordingProfile =  record->m_recProfile;
    }
    addItem("RECORDING_HOST", tr("Recording Host"), recordingHost);
    addItem("RECORDED_FILE_NAME", tr("Recorded File Name"), recordedFilename);
    addItem("RECORDED_FILE_SIZE", tr("Recorded File Size"), recordedFileSize);
    addItem("RECORDING_PROFILE", tr("Recording Profile"), recordingProfile);
    addItem("RECORDING_GROUP", tr("Recording Group"), recordingGroup);
    addItem("STORAGE_GROUP", tr("Storage Group"), storageGroup);
    addItem("PLAYBACK_GROUP", tr("Playback Group"),  playbackGroup);

    m_page[m_currentPage] = m_html.join("\n");

    delete record;
}