Beispiel #1
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;
}
bool RecordingRule::SameTimeslot(RecordingRule &rule) const
{
  time_t starttime = StartTime();
  time_t rStarttime = rule.StartTime();

  switch (rule.Type())
  {
  case MythTimer::NotRecording:
  case MythTimer::SingleRecord:
  case MythTimer::OverrideRecord:
  case MythTimer::DontRecord:
    return rStarttime == starttime && rule.EndTime() == EndTime() && rule.ChannelID() == ChannelID();
  case MythTimer::FindDailyRecord:
  case MythTimer::FindWeeklyRecord:
  case MythTimer::FindOneRecord:
    return rule.Title(false) == Title(false);
  case MythTimer::TimeslotRecord:
    return rule.Title(false) == Title(false) && daytime(&starttime) == daytime(&rStarttime) &&  rule.ChannelID() == ChannelID();
  case MythTimer::ChannelRecord:
    return rule.Title(false) == Title(false) && rule.ChannelID() == ChannelID(); //TODO: dup
  case MythTimer::AllRecord:
    return rule.Title(false) == Title(false); //TODO: dup
  case MythTimer::WeekslotRecord:
    return rule.Title(false) == Title(false) && daytime(&starttime) == daytime(&rStarttime) && weekday(&starttime) == weekday(&rStarttime) && rule.ChannelID() == ChannelID();
  }
  return false;
}
Beispiel #3
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;
}
Beispiel #4
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;
}
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;
}
Beispiel #6
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;
}
Beispiel #7
0
void CustomEdit::recordClicked(void)
{
    if (!checkSyntax())
        return;

    RecordingRule *record = new RecordingRule();

    MythUIButtonListItem* item = m_ruleList->GetItemCurrent();
    CustomRuleInfo rule = qVariantValue<CustomRuleInfo>(item->GetData());

    int cur_recid = rule.recordid.toInt();
    if (cur_recid > 0)
    {
        record->ModifyPowerSearchByID(cur_recid, m_titleEdit->GetText(),
                                      evaluate(m_descriptionEdit->GetText()),
                                      m_subtitleEdit->GetText());
    }
    else
    {
        record->LoadBySearch(kPowerSearch, m_titleEdit->GetText(),
                             evaluate(m_descriptionEdit->GetText()),
                             m_subtitleEdit->GetText(),
                             m_pginfo->GetTitle().isEmpty() ? NULL : m_pginfo);
    }

    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;
}
Beispiel #8
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->m_recordID = pginfo->GetRecordingRuleID();
            rule->Load();
            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;
    }
}
/**
*  \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;
}
Beispiel #10
0
bool Dvr::RemoveRecordSchedule ( uint nRecordId )
{
    bool bResult = false;

    if (nRecordId <= 0 )
        throw( QString("Record ID appears invalid."));

    RecordingRule *pRule = new RecordingRule();
    pRule->m_recordID = nRecordId;

    bResult = pRule->Delete();

    return bResult;
}
Beispiel #11
0
bool Dvr::RemoveRecordSchedule ( uint nRecordId )
{
    bool bResult = false;

    if (nRecordId <= 0 )
        throw QString("Record ID does not exist.");

    RecordingRule pRule;
    pRule.m_recordID = nRecordId;

    bResult = pRule.Delete();

    return bResult;
}
Beispiel #12
0
DTC::RecRule* Dvr::GetRecordSchedule( uint nRecordId )
{
    if (nRecordId <= 0 )
        throw( QString("Record ID appears invalid."));

    RecordingRule *pRule = new RecordingRule();
    pRule->m_recordID = nRecordId;
    pRule->Load();

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

    return pRecRule;
}
Beispiel #13
0
LookupType GuessLookupType(ProgramInfo *pginfo)
{
    LookupType ret = GuessLookupType(pginfo->GetInetRef());

    if (ret != kUnknownVideo)
        return ret;

    ProgramInfo::CategoryType catType = pginfo->GetCategoryType();
    if (catType == ProgramInfo::kCategoryNone)
        catType = pginfo->QueryCategoryType();

    if ((!pginfo->GetSubtitle().isEmpty() || pginfo->GetEpisode() > 0) &&
       (catType == ProgramInfo::kCategorySeries ||
        catType == ProgramInfo::kCategoryTVShow))
        ret = kProbableTelevision;
    else if (catType == ProgramInfo::kCategoryMovie)
        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->m_recordID = pginfo->GetRecordingRuleID();
        rule->Load();
        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;
}
Beispiel #14
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;
}
Beispiel #15
0
bool Dvr::DisableRecordSchedule( uint nRecordId )
{
    bool bResult = false;

    if (nRecordId <= 0 )
        throw QString("Record ID appears invalid.");

    RecordingRule pRule;
    pRule.m_recordID = nRecordId;
    pRule.Load();

    if (pRule.IsLoaded())
    {
        pRule.m_isInactive = true;
        bResult = pRule.Save();
    }

    return bResult;
}
Beispiel #16
0
DTC::RecRule* Dvr::GetRecordSchedule( uint      nRecordId,
                                      QString   sTemplate,
                                      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 (nChanId > 0 && dStartTime.isValid())
    {
        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;
}
Beispiel #17
0
bool Dvr::DisableRecordSchedule( uint nRecordId )
{
    bool bResult = false;

    if (nRecordId <= 0 )
        throw( QString("Record ID appears invalid."));

    RecordingRule *pRule = new RecordingRule();
    pRule->m_recordID = nRecordId;
    pRule->Load();

    if (pRule->IsLoaded())
    {
        pRule->m_isInactive = true;
        pRule->Save();
        bResult = true;
    }

    return bResult;
}
Beispiel #18
0
void ProgramRecPriority::remove(void)
{
    MythUIButtonListItem *item = m_programList->GetItemCurrent();
    if (!item)
        return;

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

    if (!pgRecInfo ||
        (pgRecInfo->recType == kTemplateRecord &&
         pgRecInfo->GetCategory()
         .compare("Default", Qt::CaseInsensitive) == 0))
    {
        return;
    }

    RecordingRule *record = new RecordingRule();
    record->m_recordID = pgRecInfo->GetRecordingRuleID();
    if (!record->Load())
    {
        delete record;
        return;
    }

    QString message = tr("Delete '%1' %2 rule?").arg(record->m_title)
        .arg(toString(pgRecInfo->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;
}
Beispiel #19
0
void ProgramRecPriority::newTemplate(QString category)
{
    category = category.trimmed();
    if (category.isEmpty())
        return;

    // Try to find an existing template and use it.
    QMap<int, ProgramRecPriorityInfo>::Iterator it;
    for (it = m_programData.begin(); it != m_programData.end(); ++it)
    {
        ProgramRecPriorityInfo *progInfo = &(*it);
        if (progInfo->GetRecordingRuleType() == kTemplateRecord &&
            category.compare(progInfo->GetCategory(),
                             Qt::CaseInsensitive) == 0)
        {
            m_programList->SetValueByData(qVariantFromValue(progInfo));
            edit(m_programList->GetItemCurrent());
            return;
        }
    }

    RecordingRule *record = new RecordingRule();
    if (!record)
        return;
    record->MakeTemplate(category);

    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;
}
Beispiel #20
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;
}
Beispiel #21
0
void ProgLister::customEvent(QEvent *event)
{
    bool needUpdate = false;

    if (event->type() == DialogCompletionEvent::kEventType)
    {
        DialogCompletionEvent *dce = (DialogCompletionEvent*)(event);

        QString resultid   = dce->GetId();
        QString resulttext = dce->GetResultText();
        int     buttonnum  = dce->GetResult();

        if (resultid == "sortmenu")
        {
            switch (buttonnum)
            {
                case 0:
                    m_reverseSort = !m_reverseSort;
                    needUpdate    = true;
                    break;
                case 1:
                    m_titleSort   = true;
                    m_reverseSort = false;
                    needUpdate    = true;
                    break;
                case 2:
                    m_titleSort   = false;
                    m_reverseSort = (m_type == plPreviouslyRecorded);
                    needUpdate    = true;
                    break;
            }
        }
        else if (resultid == "deletemenu")
        {
            switch (buttonnum)
            {
                case 0:
                {
                    ProgramInfo *pi = GetCurrent();
                    if (pi)
                    {
                        RecordingInfo ri(*pi);
                        if (ri.IsDuplicate())
                            ri.ForgetHistory();
                        else
                            ri.SetDupHistory();
                        *pi = ri;
                    }
                    break;
                }
                case 1:
                    ShowDeleteOldEpisodeMenu();
                    break;
                case 2:
                    ShowDeleteOldSeriesMenu();
                    break;
            }
        }
        else if (resultid == "deleterule")
        {
            RecordingRule *record =
                qVariantValue<RecordingRule *>(dce->GetData());
            if (record && buttonnum > 0 && !record->Delete())
            {
                LOG(VB_GENERAL, LOG_ERR, LOC +
                    "Failed to delete recording rule");
            }
            if (record)
                delete record;
        }
        else
        {
            ScheduleCommon::customEvent(event);
        }
    }
    else if (event->type() == ScreenLoadCompletionEvent::kEventType)
    {
        ScreenLoadCompletionEvent *slce = (ScreenLoadCompletionEvent*)(event);
        QString id = slce->GetId();

        if (id == objectName())
        {
            CloseBusyPopup(); // opened by LoadInBackground()
            UpdateDisplay();

            if (m_curView < 0 && m_type != plPreviouslyRecorded)
                ShowChooseViewMenu();
        }
    }
    else if ((MythEvent::Type)(event->type()) == MythEvent::MythEventMessage)
    {
        MythEvent *me = (MythEvent *)event;
        QString message = me->Message();

        if (message == "CHOOSE_VIEW")
            ShowChooseViewMenu();
        else if (message == "SCHEDULE_CHANGE")
            needUpdate = true;
    }

    if (needUpdate)
        FillItemList(true);
}
Beispiel #22
0
void ViewScheduled::customEvent(QEvent *event)
{
    if ((MythEvent::Type)(event->type()) == MythEvent::MythEventMessage)
    {
        MythEvent *me = (MythEvent *)event;
        QString message = me->Message();

        if (message != "SCHEDULE_CHANGE")
            return;

        m_needFill = true;

        if (m_inEvent)
            return;

        m_inEvent = true;

        LoadList();

        m_inEvent = false;
    }
    else if (event->type() == DialogCompletionEvent::kEventType)
    {
        DialogCompletionEvent *dce = (DialogCompletionEvent*)(event);

        QString resultid   = dce->GetId();
        QString resulttext = dce->GetResultText();
        int     buttonnum  = dce->GetResult();

        if (resultid == "deleterule")
        {
            RecordingRule *record = qVariantValue<RecordingRule *>(dce->GetData());
            if (record)
            {
                if (buttonnum > 0)
                {
                    if (!record->Delete())
                        VERBOSE(VB_IMPORTANT, "Failed to delete recording rule");
                }
                delete record;
            }

            EmbedTVWindow();
        }
        else if (resultid == "menu")
        {
            if (resulttext == tr("Show Important"))
            {
                setShowAll(false);
            }
            else if (resulttext == tr("Show All"))
            {
                setShowAll(true);
            }
            else if (resulttext == tr("Program Details"))
            {
                details();
            }
            else if (resulttext == tr("Upcoming by title"))
            {
                upcoming();
            }
            else if (resulttext == tr("Upcoming scheduled"))
            {
                upcomingScheduled();
            }
            else if (resulttext == tr("Custom Edit"))
            {
                customEdit();
            }
            else if (resulttext == tr("Delete Rule"))
            {
                deleteRule();
            }
            else if (resulttext == tr("Show Cards"))
            {
                viewCards();
            }
            else if (resulttext == tr("Show Inputs"))
            {
                viewInputs();
            }

            if (m_needFill)
                LoadList();
        }
        else
            ScheduleCommon::customEvent(event);
    }
}
Beispiel #23
0
void ProgramRecPriority::customEvent(QEvent *event)
{
    if (event->type() == DialogCompletionEvent::kEventType)
    {
        DialogCompletionEvent *dce = (DialogCompletionEvent*)(event);

        QString resultid   = dce->GetId();
        QString resulttext = dce->GetResultText();
        int     buttonnum  = dce->GetResult();

        if (resultid == "menu")
        {
            if (resulttext == tr("Increase Priority"))
            {
                changeRecPriority(1);
            }
            else if (resulttext == tr("Decrease Priority"))
            {
                changeRecPriority(-1);
            }
            else if (resulttext == tr("Sort"))
            {
                showSortMenu();
            }
            else if (resulttext == tr("Program Details"))
            {
                ShowDetails();
            }
            else if (resulttext == tr("Upcoming"))
            {
                saveRecPriority();
                ShowUpcoming();
            }
            else if (resulttext == tr("Custom Edit"))
            {
                saveRecPriority();
                EditCustom();
            }
            else if (resulttext == tr("Delete Rule"))
            {
                saveRecPriority();
                remove();
            }
            else if (resulttext == tr("New Template"))
            {
                MythScreenStack *popupStack =
                    GetMythMainWindow()->GetStack("popup stack");
                MythTextInputDialog *textInput =
                    new MythTextInputDialog(popupStack,
                                            tr("Template Name"));
                if (textInput->Create())
                {
                    textInput->SetReturnEvent(this, "templatecat");
                    popupStack->AddScreen(textInput);
                }
            }
        }
        else if (resultid == "sortmenu")
        {
            if (resulttext == tr("Reverse Sort Order"))
            {
                m_reverseSort = !m_reverseSort;
                SortList();
            }
            else if (resulttext == tr("Sort By Title"))
            {
                if (m_sortType != byTitle)
                {
                    m_sortType = byTitle;
                    m_reverseSort = false;
                }
                else
                    m_reverseSort = !m_reverseSort;
                SortList();
            }
            else if (resulttext == tr("Sort By Priority"))
            {
                if (m_sortType != byRecPriority)
                {
                    m_sortType = byRecPriority;
                    m_reverseSort = false;
                }
                else
                    m_reverseSort = !m_reverseSort;
                SortList();
            }
            else if (resulttext == tr("Sort By Type"))
            {
                if (m_sortType != byRecType)
                {
                    m_sortType = byRecType;
                    m_reverseSort = false;
                }
                else
                    m_reverseSort = !m_reverseSort;
                SortList();
            }
            else if (resulttext == tr("Sort By Count"))
            {
                if (m_sortType != byCount)
                {
                    m_sortType = byCount;
                    m_reverseSort = false;
                }
                else
                {
                    m_reverseSort = !m_reverseSort;
                }
                SortList();
            }
            else if (resulttext == tr("Sort By Record Count"))
            {
                if (m_sortType != byRecCount)
                {
                    m_sortType = byRecCount;
                    m_reverseSort = false;
                }
                else
                    m_reverseSort = !m_reverseSort;
                SortList();
            }
            else if (resulttext == tr("Sort By Last Recorded"))
            {
                if (m_sortType != byLastRecord)
                {
                    m_sortType = byLastRecord;
                    m_reverseSort = false;
                }
                else
                    m_reverseSort = !m_reverseSort;
                SortList();
            }
            else if (resulttext == tr("Sort By Average Delay"))
            {
                if (m_sortType != byAvgDelay)
                {
                    m_sortType = byAvgDelay;
                    m_reverseSort = false;
                }
                else
                    m_reverseSort = !m_reverseSort;
                SortList();
            }
        }
        else if (resultid == "deleterule")
        {
            RecordingRule *record =
                dce->GetData().value<RecordingRule *>();
            if (record)
            {
                if (buttonnum > 0)
                {
                    MythUIButtonListItem *item =
                        m_programList->GetItemCurrent();

                    if (record->Delete() && item)
                        RemoveItemFromList(item);
                    else
                        LOG(VB_GENERAL, LOG_ERR,
                            "Failed to delete recording rule");
                }
                delete record;
            }
        }
        else if (resultid == "templatecat")
        {
            newTemplate(resulttext);
        }
        else
            ScheduleCommon::customEvent(event);
    }
}
Beispiel #24
0
void ViewScheduled::customEvent(QEvent *event)
{
    if (event->type() == MythEvent::MythEventMessage)
    {
        MythEvent *me = static_cast<MythEvent *>(event);
        QString message = me->Message();

        if (message != "SCHEDULE_CHANGE")
            return;

        m_needFill = true;

        if (m_inEvent)
            return;

        m_inEvent = true;

        LoadList();

        m_inEvent = false;
    }
    else if (event->type() == DialogCompletionEvent::kEventType)
    {
        DialogCompletionEvent *dce = (DialogCompletionEvent*)(event);

        QString resultid   = dce->GetId();
        QString resulttext = dce->GetResultText();
        int     buttonnum  = dce->GetResult();

        if (resultid == "deleterule")
        {
            RecordingRule *record =
                dce->GetData().value<RecordingRule *>();
            if (record)
            {
                if (buttonnum > 0)
                {
                    if (!record->Delete())
                        LOG(VB_GENERAL, LOG_ERR,
                            "Failed to delete recording rule");
                }
                delete record;
            }

            EmbedTVWindow();
        }
        else if (resultid == "menu")
        {
            if (resulttext == tr("Show Important"))
            {
                setShowAll(false);
            }
            else if (resulttext == tr("Show All"))
            {
                setShowAll(true);
            }
            else if (resulttext == tr("Program Details"))
            {
                ShowDetails();
            }
            else if (resulttext == tr("Program Guide"))
            {
                ShowGuide();
            }
            else if (resulttext == tr("Channel Search"))
            {
                ShowChannelSearch();
            }
            else if (resulttext == tr("Upcoming by title"))
            {
                ShowUpcoming();
            }
            else if (resulttext == tr("Upcoming scheduled"))
            {
                ShowUpcomingScheduled();
            }
            else if (resulttext == tr("Previously Recorded"))
            {
                ShowPrevious();
            }
            else if (resulttext == tr("Custom Edit"))
            {
                EditCustom();
            }
            else if (resulttext == tr("Delete Rule"))
            {
                deleteRule();
            }
            else if (resulttext == tr("Show Inputs"))
            {
                viewInputs();
            }

            if (m_needFill)
                LoadList();
        }
        else
            ScheduleCommon::customEvent(event);
    }
}
Beispiel #25
0
void ProgramRecPriority::scheduleChanged(int recid)
{
    // Assumes that the current item didn't change, which isn't guaranteed
    MythUIButtonListItem *item = m_programList->GetItemCurrent();
    ProgramRecPriorityInfo *pgRecInfo = NULL;
    if (item)
        pgRecInfo = item->GetData().value<ProgramRecPriorityInfo*>();

    // If the recording id doesn't match, the user created a new
    // template.
    if (!pgRecInfo || recid != pgRecInfo->getRecordID())
    {
        RecordingRule record;
        record.m_recordID = recid;
        if (!record.Load() || record.m_type == kNotRecording)
            return;

        ProgramRecPriorityInfo progInfo;
        progInfo.SetRecordingRuleID(record.m_recordID);
        progInfo.SetRecordingRuleType(record.m_type);
        progInfo.SetTitle(record.m_title);
        progInfo.SetCategory(record.m_category);
        progInfo.SetRecordingPriority(record.m_recPriority);
        progInfo.recType = record.m_type;
        progInfo.sortTitle = record.m_title;
        progInfo.recstatus = record.m_isInactive ?
            RecStatus::Inactive : RecStatus::Unknown;
        progInfo.profile = record.m_recProfile;
        progInfo.last_record = record.m_lastRecorded;

        m_programData[recid] = progInfo;
        m_origRecPriorityData[record.m_recordID] =
            record.m_recPriority;
        SortList(&m_programData[recid]);

        return;
    }

    // We need to refetch the recording priority values since the Advanced
    // Recording Options page could've been used to change them

    MSqlQuery query(MSqlQuery::InitCon());
    query.prepare("SELECT recpriority, type, inactive "
                    "FROM record "
                    "WHERE recordid = :RECORDID");
    query.bindValue(":RECORDID", recid);
    if (!query.exec())
    {
        MythDB::DBError("Get new recording priority query", query);
    }
    else if (query.next())
    {
        int recPriority = query.value(0).toInt();
        int rectype = query.value(1).toInt();
        int inactive = query.value(2).toInt();

        // set the recording priorities of that program
        pgRecInfo->SetRecordingPriority(recPriority);
        pgRecInfo->recType = (RecordingType)rectype;
        // also set the m_origRecPriorityData with new recording
        // priority so we don't save to db again when we exit
        m_origRecPriorityData[pgRecInfo->GetRecordingRuleID()] =
            pgRecInfo->GetRecordingPriority();
        // also set the active/inactive state
        pgRecInfo->recstatus = inactive ? RecStatus::Inactive : RecStatus::Unknown;

        SortList();
    }
    else
    {
        RemoveItemFromList(item);
    }

    countMatches();
}
Beispiel #26
0
uint Dvr::AddRecordSchedule   (
                               QString   sTitle,
                               QString   sSubtitle,
                               QString   sDescription,
                               QString   sCategory,
                               QDateTime recstarttsRaw,
                               QDateTime recendtsRaw,
                               QString   sSeriesId,
                               QString   sProgramId,
                               int       nChanId,
                               QString   sStation,
                               int       nFindDay,
                               QTime     tFindTime,
                               int       nParentId,
                               bool      bInactive,
                               uint      nSeason,
                               uint      nEpisode,
                               QString   sInetref,
                               QString   sType,
                               QString   sSearchType,
                               int       nRecPriority,
                               uint      nPreferredInput,
                               int       nStartOffset,
                               int       nEndOffset,
                               QString   sDupMethod,
                               QString   sDupIn,
                               uint      nFilter,
                               QString   sRecProfile,
                               QString   sRecGroup,
                               QString   sStorageGroup,
                               QString   sPlayGroup,
                               bool      bAutoExpire,
                               int       nMaxEpisodes,
                               bool      bMaxNewest,
                               bool      bAutoCommflag,
                               bool      bAutoTranscode,
                               bool      bAutoMetaLookup,
                               bool      bAutoUserJob1,
                               bool      bAutoUserJob2,
                               bool      bAutoUserJob3,
                               bool      bAutoUserJob4,
                               int       nTranscoder)
{
    QDateTime recstartts = recstarttsRaw.toUTC();
    QDateTime recendts = recendtsRaw.toUTC();
    RecordingRule rule;
    rule.LoadTemplate("Default");

    if (sType.isEmpty())
        sType = "single";

    if (sSearchType.isEmpty())
        sSearchType = "none";

    if (sDupMethod.isEmpty())
        sDupMethod = "subtitleanddescription";

    if (sDupIn.isEmpty())
        sDupIn = "all";

    rule.m_title = sTitle;
    rule.m_subtitle = sSubtitle;
    rule.m_description = sDescription;

    rule.m_startdate = recstartts.date();
    rule.m_starttime = recstartts.time();
    rule.m_enddate = recendts.date();
    rule.m_endtime = recendts.time();

    rule.m_type = recTypeFromString(sType);
    rule.m_searchType = searchTypeFromString(sSearchType);
    rule.m_dupMethod = dupMethodFromString(sDupMethod);
    rule.m_dupIn = dupInFromString(sDupIn);

    if (sRecProfile.isEmpty())
        sRecProfile = "Default";

    if (sRecGroup.isEmpty())
        sRecGroup = "Default";

    if (sStorageGroup.isEmpty())
        sStorageGroup = "Default";

    if (sPlayGroup.isEmpty())
        sPlayGroup = "Default";

    rule.m_category = sCategory;
    rule.m_seriesid = sSeriesId;
    rule.m_programid = sProgramId;

    rule.m_channelid = nChanId;
    rule.m_station = sStation;

    rule.m_findday = nFindDay;
    rule.m_findtime = tFindTime;

    rule.m_recProfile = sRecProfile;
    rule.m_recGroupID = RecordingInfo::GetRecgroupID(sRecGroup) > 0 ? RecordingInfo::GetRecgroupID(sRecGroup) :
                                                                      RecordingInfo::kDefaultRecGroup;
    rule.m_storageGroup = sStorageGroup;
    rule.m_playGroup = sPlayGroup;

    rule.m_parentRecID = nParentId;
    rule.m_isInactive = bInactive;

    rule.m_season = nSeason;
    rule.m_episode = nEpisode;
    rule.m_inetref = sInetref;

    rule.m_recPriority = nRecPriority;
    rule.m_prefInput = nPreferredInput;
    rule.m_startOffset = nStartOffset;
    rule.m_endOffset = nEndOffset;
    rule.m_filter = nFilter;

    rule.m_autoExpire = bAutoExpire;
    rule.m_maxEpisodes = nMaxEpisodes;
    rule.m_maxNewest = bMaxNewest;

    rule.m_autoCommFlag = bAutoCommflag;
    rule.m_autoTranscode = bAutoTranscode;
    rule.m_autoMetadataLookup = bAutoMetaLookup;

    rule.m_autoUserJob1 = bAutoUserJob1;
    rule.m_autoUserJob2 = bAutoUserJob2;
    rule.m_autoUserJob3 = bAutoUserJob3;
    rule.m_autoUserJob4 = bAutoUserJob4;

    rule.m_transcoder = nTranscoder;

    QString msg;
    if (!rule.IsValid(msg))
        throw msg;

    rule.Save();

    uint recid = rule.m_recordID;

    return recid;
}
Beispiel #27
0
bool Dvr::UpdateRecordSchedule ( uint      nRecordId,
                                 QString   sTitle,
                                 QString   sSubtitle,
                                 QString   sDescription,
                                 QString   sCategory,
                                 QDateTime dStartTimeRaw,
                                 QDateTime dEndTimeRaw,
                                 QString   sSeriesId,
                                 QString   sProgramId,
                                 int       nChanId,
                                 QString   sStation,
                                 int       nFindDay,
                                 QTime     tFindTime,
                                 bool      bInactive,
                                 uint      nSeason,
                                 uint      nEpisode,
                                 QString   sInetref,
                                 QString   sType,
                                 QString   sSearchType,
                                 int       nRecPriority,
                                 uint      nPreferredInput,
                                 int       nStartOffset,
                                 int       nEndOffset,
                                 QString   sDupMethod,
                                 QString   sDupIn,
                                 uint      nFilter,
                                 QString   sRecProfile,
                                 QString   sRecGroup,
                                 QString   sStorageGroup,
                                 QString   sPlayGroup,
                                 bool      bAutoExpire,
                                 int       nMaxEpisodes,
                                 bool      bMaxNewest,
                                 bool      bAutoCommflag,
                                 bool      bAutoTranscode,
                                 bool      bAutoMetaLookup,
                                 bool      bAutoUserJob1,
                                 bool      bAutoUserJob2,
                                 bool      bAutoUserJob3,
                                 bool      bAutoUserJob4,
                                 int       nTranscoder)
{
    if (nRecordId <= 0 )
        throw QString("Record ID is invalid.");

    RecordingRule pRule;
    pRule.m_recordID = nRecordId;
    pRule.Load();

    if (!pRule.IsLoaded())
        throw QString("Record ID does not exist.");

    QDateTime recstartts = dStartTimeRaw.toUTC();
    QDateTime recendts = dEndTimeRaw.toUTC();

    pRule.m_isInactive = bInactive;
    if (sType.isEmpty())
        sType = "single";

    if (sSearchType.isEmpty())
        sSearchType = "none";

    if (sDupMethod.isEmpty())
        sDupMethod = "subtitleanddescription";

    if (sDupIn.isEmpty())
        sDupIn = "all";

    pRule.m_type = recTypeFromString(sType);
    pRule.m_searchType = searchTypeFromString(sSearchType);
    pRule.m_dupMethod = dupMethodFromString(sDupMethod);
    pRule.m_dupIn = dupInFromString(sDupIn);

    if (sRecProfile.isEmpty())
        sRecProfile = "Default";

    if (sRecGroup.isEmpty())
        sRecGroup = "Default";

    if (sStorageGroup.isEmpty())
        sStorageGroup = "Default";

    if (sPlayGroup.isEmpty())
        sPlayGroup = "Default";

    if (!sTitle.isEmpty())
        pRule.m_title = sTitle;

    if (!sSubtitle.isEmpty())
        pRule.m_subtitle = sSubtitle;

    if(!sDescription.isEmpty())
        pRule.m_description = sDescription;

    if (!sCategory.isEmpty())
        pRule.m_category = sCategory;

    if (!sSeriesId.isEmpty())
        pRule.m_seriesid = sSeriesId;

    if (!sProgramId.isEmpty())
        pRule.m_programid = sProgramId;

    if (nChanId)
        pRule.m_channelid = nChanId;
    if (!sStation.isEmpty())
        pRule.m_station = sStation;

    pRule.m_startdate = recstartts.date();
    pRule.m_starttime = recstartts.time();
    pRule.m_enddate = recendts.date();
    pRule.m_endtime = recendts.time();

    pRule.m_findday = nFindDay;
    pRule.m_findtime = tFindTime;

    pRule.m_recProfile = sRecProfile;
    pRule.m_recGroupID = RecordingInfo::GetRecgroupID(sRecGroup) > 0 ? RecordingInfo::GetRecgroupID(sRecGroup) :
                                                                      RecordingInfo::kDefaultRecGroup;
    pRule.m_storageGroup = sStorageGroup;
    pRule.m_playGroup = sPlayGroup;

    pRule.m_isInactive = bInactive;

    pRule.m_season = nSeason;
    pRule.m_episode = nEpisode;
    pRule.m_inetref = sInetref;

    pRule.m_recPriority = nRecPriority;
    pRule.m_prefInput = nPreferredInput;
    pRule.m_startOffset = nStartOffset;
    pRule.m_endOffset = nEndOffset;
    pRule.m_filter = nFilter;

    pRule.m_autoExpire = bAutoExpire;
    pRule.m_maxEpisodes = nMaxEpisodes;
    pRule.m_maxNewest = bMaxNewest;

    pRule.m_autoCommFlag = bAutoCommflag;
    pRule.m_autoTranscode = bAutoTranscode;
    pRule.m_autoMetadataLookup = bAutoMetaLookup;

    pRule.m_autoUserJob1 = bAutoUserJob1;
    pRule.m_autoUserJob2 = bAutoUserJob2;
    pRule.m_autoUserJob3 = bAutoUserJob3;
    pRule.m_autoUserJob4 = bAutoUserJob4;

    pRule.m_transcoder = nTranscoder;

    QString msg;
    if (!pRule.IsValid(msg))
        throw msg;

    bool bResult = pRule.Save();

    return bResult;
}
Beispiel #28
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;
}
Beispiel #29
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);
    }
}
Beispiel #30
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;
}