Ticket #7195: 7195-v1.patch

File 7195-v1.patch, 20.8 KB (added by danielk, 15 years ago)

Not extensively tested yet. In particular this has not been tested with sbe's

  • libs/libmythtv/previewgenerator.cpp

     
    361361        else
    362362        {
    363363            VERBOSE(VB_IMPORTANT, LOC_ERR +
    364                     "Remote Preview failed due to an uknown error.");
     364                    "Remote Preview failed due to an unknown error.");
    365365        }
    366366        return false;
    367367    }
     
    372372        QDir remotecachedir(remotecachedirname);
    373373
    374374        if (!remotecachedir.exists())
    375             remotecachedir.mkdir(remotecachedirname);
     375        {
     376            if (!remotecachedir.mkdir(remotecachedirname))
     377            {
     378                VERBOSE(VB_IMPORTANT, LOC_ERR +
     379                        "Remote Preview failed because we could not create a "
     380                        "remote cache directory");
     381                return false;
     382            }
     383        }
    376384
    377385        QString filename = programInfo.pathname.section('/',-1) + ".png";
    378386        outFileName = QString("%1/%2").arg(remotecachedirname).arg(filename);
  • libs/libmyth/remoteutil.h

     
    5252MPUBLIC vector<uint> RemoteRequestFreeRecorderList(void);
    5353MPUBLIC void RemoteGeneratePreviewPixmap(const ProgramInfo *pginfo);
    5454MPUBLIC QDateTime RemoteGetPreviewLastModified(const ProgramInfo *pginfo);
     55MPUBLIC QDateTime RemoteGetPreviewIfModified(
     56    const ProgramInfo &pginfo, const QString &cachefile);
    5557MPUBLIC void RemoteFillProginfo(ProgramInfo *pginfo,
    5658                                const QString &playbackhostname);
    5759MPUBLIC QStringList RemoteRecordings(void);
  • libs/libmyth/remoteutil.cpp

     
    11#include <unistd.h>
    22
     3#include <QFileInfo>
    34#include <QFile>
     5#include <QDir>
    46
    57#include "remoteutil.h"
    68#include "programinfo.h"
     
    287289    return retdatetime;
    288290}
    289291
     292/// Download preview & get timestamp if newer than cachefile's
     293/// last modified time, otherwise just get the timestamp
     294QDateTime RemoteGetPreviewIfModified(
     295    const ProgramInfo &pginfo, const QString &cachefile)
     296{
     297    QString loc_err("RemoteGetPreviewIfModified, Error: ");
     298
     299    QDateTime cacheLastModified;
     300    QFileInfo cachefileinfo(cachefile);
     301    if (cachefileinfo.exists())
     302        cacheLastModified = cachefileinfo.lastModified();
     303
     304    QStringList strlist("QUERY_PIXMAP_GET_IF_MODIFIED");
     305    strlist << ((cacheLastModified.isValid()) ? // unix secs, UTC
     306                QString::number(cacheLastModified.toTime_t()) : QString("-1"));
     307    strlist << QString::number(200 * 1024); // max size of preview file
     308    pginfo.ToStringList(strlist);
     309
     310    if (!gContext->SendReceiveStringList(strlist) ||
     311        strlist.empty() || strlist[0] == "ERROR")
     312    {
     313        VERBOSE(VB_IMPORTANT, loc_err +
     314                QString("Remote error") +
     315                ((strlist.size() >= 2) ?
     316                 (QString(":\n\t\t\t") + strlist[1]) : QString("")));
     317
     318        return QDateTime();
     319    }
     320
     321    if (strlist[0] == "WARNING")
     322    {
     323        VERBOSE(VB_NETWORK, QString("RemoteGetPreviewIfModified, Warning: ") +
     324                QString("Remote warning") +
     325                ((strlist.size() >= 2) ?
     326                 (QString(":\n\t\t\t") + strlist[1]) : QString("")));
     327
     328        return QDateTime();
     329    }
     330
     331    QDateTime retdatetime;
     332    qlonglong timet = strlist[0].toLongLong();
     333    if (timet >= 0)
     334        retdatetime.setTime_t(timet);
     335
     336    if (strlist.size() < 4)
     337    {
     338        return retdatetime;
     339    }
     340
     341    size_t  length     = strlist[1].toLongLong();
     342    quint16 checksum16 = strlist[2].toUInt();
     343    QByteArray data = QByteArray::fromBase64(strlist[3].toAscii());
     344    if ((size_t) data.size() < length)
     345    { // (note data.size() may be up to 3 bytes longer after decoding
     346        VERBOSE(VB_IMPORTANT, loc_err +
     347                QString("Preview size check failed %1 < %2")
     348                .arg(data.size()).arg(length));
     349        return QDateTime();
     350    }
     351
     352    if (checksum16 != qChecksum(data.constData(), data.size()))
     353    {
     354        VERBOSE(VB_IMPORTANT, loc_err + "Preview checksum failed");
     355        return QDateTime();
     356    }
     357
     358    QString pdir(cachefile.section("/", 0, -2));
     359    QDir cfd(pdir);
     360    if (!cfd.exists() && !cfd.mkdir(pdir))
     361    {
     362        VERBOSE(VB_IMPORTANT, loc_err +
     363                QString("Unable to create remote cache directory '%1'")
     364                .arg(pdir));
     365
     366        return QDateTime();
     367    }
     368
     369    QFile file(cachefile);
     370    if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
     371    {
     372        VERBOSE(VB_IMPORTANT, loc_err +
     373                QString("Unable to open cached "
     374                        "preview file for writing '%1'")
     375                .arg(cachefile));
     376
     377        return QDateTime();
     378    }
     379
     380    off_t offset = 0;
     381    size_t remaining = length;
     382    uint failure_cnt = 0;
     383    while ((remaining > 0) && (failure_cnt < 5))
     384    {
     385        ssize_t written = file.write(data.data() + offset, remaining);
     386        if (written < 0)
     387        {
     388            failure_cnt++;
     389            usleep(50000);
     390            continue;
     391        }
     392
     393        failure_cnt  = 0;
     394        offset      += written;
     395        remaining   -= written;
     396    }
     397
     398    if (remaining)
     399    {
     400        VERBOSE(VB_IMPORTANT, loc_err +
     401                QString("Failed to write cached preview file '%1'")
     402                .arg(cachefile));
     403
     404        file.resize(0); // in case unlink fails..
     405        file.remove();  // closes fd
     406        return QDateTime();
     407    }
     408
     409    file.close();
     410
     411    return retdatetime;
     412}
     413
    290414void RemoteFillProginfo(ProgramInfo *pginfo, const QString &playbackhostname)
    291415{
    292416    QStringList strlist( "FILL_PROGRAM_INFO" );
  • programs/mythfrontend/playbackbox.cpp

     
    602602    QString oldimgfile = item->GetImage("preview");
    603603    QString imagefile;
    604604    if (oldimgfile.isEmpty() || force_preview_reload)
    605         imagefile = getPreviewImage(pginfo);
     605        imagefile = GetPreviewImage(pginfo);
    606606
    607607    if (!imagefile.isEmpty())
    608608        item->SetImage(imagefile, "preview", force_preview_reload);
     
    34983498    return false;
    34993499}
    35003500
    3501 QDateTime PlaybackBox::getPreviewLastModified(ProgramInfo *pginfo)
    3502 {
    3503     QDateTime datetime;
    3504     QString filename = pginfo->pathname + ".png";
    3505 
    3506     if (filename.left(7) != "myth://")
    3507     {
    3508         QFileInfo retfinfo(filename);
    3509         if (retfinfo.exists())
    3510             datetime = retfinfo.lastModified();
    3511     }
    3512     else
    3513     {
    3514         datetime = RemoteGetPreviewLastModified(pginfo);
    3515     }
    3516 
    3517     return datetime;
    3518 }
    3519 
    35203501void PlaybackBox::IncPreviewGeneratorPriority(const QString &xfn)
    35213502{
    35223503    QString fn = xfn.mid(qMax(xfn.lastIndexOf('/') + 1,0));
     
    36783659    UpdateProgramInfo(item, item == sel_item, true);
    36793660}
    36803661
    3681 bool check_lastmod(LastCheckedMap &elapsedtime, const QString &filename)
     3662QString PlaybackBox::GetPreviewImage(ProgramInfo *pginfo)
    36823663{
    3683     LastCheckedMap::iterator it = elapsedtime.find(filename);
     3664    if (!pginfo || pginfo->availableStatus == asPendingDelete)
     3665        return QString();
    36843666
    3685     if (it != elapsedtime.end() && ((*it).elapsed() < 300))
    3686         return false;
     3667    QString filename = pginfo->GetPlaybackURL() + ".png";
    36873668
    3688     elapsedtime[filename].restart();
    3689     return true;
    3690 }
     3669    // If someone is asking for this preview it must be on screen
     3670    // and hence higher priority than anything else we may have
     3671    // queued up recently....
     3672    IncPreviewGeneratorPriority(filename);
    36913673
    3692 QString PlaybackBox::getPreviewImage(ProgramInfo *pginfo)
    3693 {
    3694     QString filename;
     3674    QDateTime previewLastModified;
     3675    QString ret_file = filename;
     3676    bool streaming = filename.left(1) != "/";
     3677    bool locally_accessible = false;
     3678    bool bookmark_updated = false;
    36953679
    3696     if (!pginfo || pginfo->availableStatus == asPendingDelete)
    3697         return filename;
     3680    if (m_previewFromBookmark || streaming)
     3681    {
     3682        if (streaming)
     3683        {
     3684            ret_file = QString("%1/remotecache/%2")
     3685                .arg(GetConfDir()).arg(filename.section('/', -1));
    36983686
    3699     filename = pginfo->GetPlaybackURL() + ".png";
     3687            QFileInfo finfo(ret_file);
     3688            if (finfo.exists() &&
     3689                (!m_previewFromBookmark ||
     3690                 (finfo.lastModified() >= pginfo->lastmodified)))
     3691            {
     3692                // This is just an optimization to avoid
     3693                // hitting the backend if our cached copy
     3694                // is newer than the bookmark, or if we have
     3695                // a preview and do not update it when the
     3696                // bookmark changes.
     3697                previewLastModified = finfo.lastModified();
     3698            }
     3699            else
     3700            {
     3701                previewLastModified =
     3702                    RemoteGetPreviewIfModified(*pginfo, ret_file);
     3703            }
     3704        }
     3705        else
     3706        {
     3707            QFileInfo fi(filename);
     3708            if ((locally_accessible = fi.exists()))
     3709                previewLastModified = fi.lastModified();
     3710        }
    37003711
    3701     IncPreviewGeneratorPriority(filename);
     3712        bookmark_updated =
     3713            m_previewFromBookmark &&
     3714            ((!previewLastModified.isValid() ||
     3715              (previewLastModified <  pginfo->lastmodified &&
     3716               previewLastModified >= pginfo->recendts)));
     3717    }
     3718    else
     3719    {
     3720        locally_accessible = QFileInfo(filename).exists();
     3721    }
    37023722
    3703     bool check_date = check_lastmod(m_previewLastModifyCheck, filename);
     3723    bool up_to_date_preview_exists =
     3724        locally_accessible || previewLastModified.isValid();
    37043725
    3705     QDateTime previewLastModified;
     3726    if (0)
     3727    {
     3728        VERBOSE(VB_IMPORTANT,
     3729                QString("File  '%1' \n\t\t\tCache '%2'")
     3730                .arg(filename).arg(ret_file) +
     3731                QString("\n\t\t\tPreview Exists: %1, "
     3732                        "Bookmark Updated: %2, "
     3733                        "Need Preview: %3")
     3734                .arg(up_to_date_preview_exists).arg(bookmark_updated)
     3735                .arg((bookmark_updated || !up_to_date_preview_exists)));
     3736    }
    37063737
    3707     if (check_date)
    3708         previewLastModified = getPreviewLastModified(pginfo);
    3709 
    3710     if (m_previewFromBookmark && check_date &&
    3711         (!previewLastModified.isValid() ||
    3712          (previewLastModified <  pginfo->lastmodified &&
    3713           previewLastModified >= pginfo->recendts)) &&
    3714         !pginfo->IsEditing() &&
    3715         !JobQueue::IsJobRunning(JOB_COMMFLAG, pginfo) &&
     3738    if ((bookmark_updated || !up_to_date_preview_exists) &&
    37163739        !IsGeneratingPreview(filename))
    37173740    {
    37183741        uint attempts = IncPreviewGeneratorAttempts(filename);
     
    37293752                            "%2 times, giving up.")
    37303753                    .arg(filename).arg(PreviewGenState::maxAttempts));
    37313754        }
    3732 
    3733         if (attempts >= PreviewGenState::maxAttempts)
    3734             return filename;
    37353755    }
    37363756
    37373757    UpdatePreviewGeneratorThreads();
    37383758
    3739     // Image is local
    3740     if (QFileInfo(filename).exists())
    3741         return filename;
     3759    QString ret = (locally_accessible) ?
     3760        filename : (previewLastModified.isValid()) ?
     3761        ret_file : (QFileInfo(ret_file).exists()) ?
     3762        ret_file : QString();
    37423763
    3743     // If this is a remote frontend then check the remote file cache
    3744     QString cachefile = QString("%1/remotecache/%3").arg(GetConfDir())
    3745                                                 .arg(filename.section('/', -1));
    3746     QFileInfo cachefileinfo(cachefile);
    3747     if (!cachefileinfo.exists() ||
    3748         previewLastModified.addSecs(-60) > cachefileinfo.lastModified())
    3749     {
    3750         if (!IsGeneratingPreview(filename))
    3751         {
    3752             uint attempts = IncPreviewGeneratorAttempts(filename);
    3753             if (attempts < PreviewGenState::maxAttempts)
    3754             {
    3755                 SetPreviewGenerator(filename, new PreviewGenerator(pginfo,
    3756                                 (PreviewGenerator::Mode)m_previewGeneratorMode));
    3757             }
    3758             else if (attempts == PreviewGenState::maxAttempts)
    3759             {
    3760                 VERBOSE(VB_IMPORTANT, LOC_ERR +
    3761                         QString("Attempted to generate m_preview for '%1' "
    3762                                 "%2 times, giving up.")
    3763                         .arg(filename).arg(PreviewGenState::maxAttempts));
    3764             }
    3765         }
    3766     }
     3764    //VERBOSE(VB_IMPORTANT, QString("Returning: '%1'").arg(ret));
    37673765
    3768     if (cachefileinfo.exists())
    3769         return cachefile;
    3770 
    3771     return "";
     3766    return ret;
    37723767}
    37733768
    37743769void PlaybackBox::showIconHelp(void)
  • programs/mythfrontend/playbackbox.h

     
    5656typedef QMap<QString,ProgramList>       ProgramMap;
    5757typedef QMap<QString,QString>           Str2StrMap;
    5858typedef QMap<QString,PreviewGenState>   PreviewMap;
    59 typedef QMap<QString,MythTimer>         LastCheckedMap;
    6059
    6160class PlaybackBox : public ScheduleCommon
    6261{
     
    272271    void UpdateProgressBar(void);
    273272
    274273    QString cutDown(const QString &, QFont *, int);
    275     QDateTime getPreviewLastModified(ProgramInfo *);
    276     QString getPreviewImage(ProgramInfo *);
     274    QString GetPreviewImage(ProgramInfo *);
    277275
    278276    bool play(ProgramInfo *rec, bool inPlaylist = false);
    279277    void stop(ProgramInfo *);
     
    431429    // Preview Pixmap Variables ///////////////////////////////////////////////
    432430    bool                m_previewFromBookmark;
    433431    uint                m_previewGeneratorMode;
    434     LastCheckedMap      m_previewLastModifyCheck;
    435432    QMap<QString,QDateTime> m_previewFileTS;
    436433    bool                m_previewSuspend;
    437434    mutable QMutex      m_previewGeneratorLock;
  • programs/mythbackend/playbacksock.h

     
    5959                                 const QString     &outputFile,
    6060                                 const QSize       &outputSize);
    6161    QDateTime PixmapLastModified(const ProgramInfo *pginfo);
     62
    6263    bool CheckFile(ProgramInfo *pginfo);
    6364
    6465    bool IsBusy(int        capturecardnum,
     
    7879                                    const vector<uint> &excluded_cardids);
    7980    void CancelNextRecording(int capturecardnum, bool cancel);
    8081
     82    QStringList ForwardRequest(const QStringList&);
     83
    8184  private:
    8285    bool SendReceiveStringList(QStringList &strlist, uint min_reply_length = 0);
    8386
  • programs/mythbackend/mainserver.h

     
    128128    void HandleSetVerbose(QStringList &slist, PlaybackSock *pbs);
    129129    void HandleGenPreviewPixmap(QStringList &slist, PlaybackSock *pbs);
    130130    void HandlePixmapLastModified(QStringList &slist, PlaybackSock *pbs);
     131    void HandlePixmapGetIfModified(const QStringList &slist, PlaybackSock *pbs);
    131132    void HandleIsRecording(QStringList &slist, PlaybackSock *pbs);
    132133    void HandleCheckRecordingActive(QStringList &slist, PlaybackSock *pbs);
    133134    void HandleFillProgramInfo(QStringList &slist, PlaybackSock *pbs);
  • programs/mythbackend/playbacksock.cpp

     
    481481    SendReceiveStringList(strlist);
    482482}
    483483
     484QStringList PlaybackSock::ForwardRequest(const QStringList &slist)
     485{
     486    QStringList strlist = slist;
     487
     488    if (SendReceiveStringList(strlist))
     489        return strlist;
     490
     491    return QStringList();
     492}
     493
    484494/* vim: set expandtab tabstop=4 shiftwidth=4: */
  • programs/mythbackend/mainserver.cpp

     
    532532    {
    533533        HandlePixmapLastModified(listline, pbs);
    534534    }
     535    else if (command == "QUERY_PIXMAP_GET_IF_MODIFIED")
     536    {
     537        HandlePixmapGetIfModified(listline, pbs);
     538    }
    535539    else if (command == "QUERY_ISRECORDING")
    536540    {
    537541        HandleIsRecording(listline, pbs);
     
    46144618        strlist = QStringList( "BAD" );
    46154619
    46164620    SendResponse(pbssock, strlist);
    4617     delete pginfo;
    46184621}
    46194622
     4623void MainServer::HandlePixmapGetIfModified(
     4624    const QStringList &slist, PlaybackSock *pbs)
     4625{
     4626    QStringList strlist;
     4627
     4628    MythSocket *pbssock = pbs->getSocket();
     4629    if (slist.size() < (3 + NUMPROGRAMLINES))
     4630    {
     4631        strlist = QStringList("ERROR");
     4632        strlist += "1: Parameter list too short";
     4633        SendResponse(pbssock, strlist);
     4634        return;
     4635    }
     4636
     4637    QDateTime cachemodified;
     4638    if (slist[1].toInt() != -1)
     4639        cachemodified.setTime_t(slist[1].toInt());
     4640
     4641    int max_file_size = slist[2].toInt();
     4642
     4643    ProgramInfo pginfo;
     4644
     4645    if (!pginfo.FromStringList(slist, 3))
     4646    {
     4647        strlist = QStringList("ERROR");
     4648        strlist += "2: Invalid ProgramInfo";
     4649        SendResponse(pbssock, strlist);
     4650        return;
     4651    }
     4652
     4653    pginfo.pathname = GetPlaybackURL(&pginfo) + ".png";
     4654    if (pginfo.pathname.left(1) == "/")
     4655    {
     4656        QFileInfo finfo(pginfo.pathname);
     4657        if (finfo.exists())
     4658        {
     4659            size_t fsize = finfo.size();
     4660            QDateTime lastmodified = finfo.lastModified();
     4661            bool out_of_date = !cachemodified.isValid() ||
     4662                (lastmodified > cachemodified);
     4663
     4664            if (out_of_date && (fsize > 0) && ((ssize_t)fsize < max_file_size))
     4665            {
     4666                QByteArray data;
     4667                QFile file(pginfo.pathname);
     4668                bool open_ok = file.open(QIODevice::ReadOnly);
     4669                if (open_ok)
     4670                    data = file.readAll();
     4671
     4672                if (data.size())
     4673                {
     4674                    VERBOSE(VB_FILE, QString("Read preview file '%1'")
     4675                            .arg(pginfo.pathname));
     4676                    strlist += QString::number(lastmodified.toTime_t());
     4677                    strlist += QString::number(data.size());
     4678                    strlist += QString::number(
     4679                        qChecksum(data.constData(), data.size()));
     4680                    strlist += QString(data.toBase64());
     4681                }
     4682                else
     4683                {
     4684                    VERBOSE(VB_IMPORTANT,
     4685                            QString("Failed to read preview file '%1'")
     4686                            .arg(pginfo.pathname));
     4687
     4688                    strlist = QStringList("ERROR");
     4689                    strlist +=
     4690                        QString("3: Failed to read preview file '%1'%2")
     4691                        .arg(pginfo.pathname)
     4692                        .arg((open_ok) ? "" : " open failed");
     4693                }
     4694            }
     4695            else if (out_of_date && (max_file_size > 0))
     4696            {
     4697                if (fsize >= (size_t) max_file_size)
     4698                {
     4699                    strlist = QStringList("WARNING");
     4700                    strlist += QString("1: Preview file too big %1 > %2")
     4701                        .arg(fsize).arg(max_file_size);
     4702                }
     4703                else
     4704                {
     4705                    strlist = QStringList("ERROR");
     4706                    strlist += "4: Preview file is invalid";
     4707                }
     4708            }
     4709            else
     4710            {
     4711                strlist += QString::number(lastmodified.toTime_t());
     4712            }
     4713
     4714            SendResponse(pbssock, strlist);
     4715            return;
     4716        }
     4717    }
     4718
     4719    // handle remote ...
     4720    if (ismaster && pginfo.hostname != gContext->GetHostName())
     4721    {
     4722        PlaybackSock *slave = getSlaveByHostname(pginfo.hostname);
     4723        if (!slave)
     4724        {
     4725            strlist = QStringList("ERROR");
     4726            strlist +=
     4727                "5: Could not locate mythbackend that made this recording";
     4728            SendResponse(pbssock, strlist);
     4729            return;
     4730        }
     4731
     4732        strlist = slave->ForwardRequest(slist);
     4733
     4734        slave->DownRef(); slave = NULL;
     4735
     4736        if (!strlist.empty())
     4737        {
     4738            SendResponse(pbssock, strlist);
     4739            return;
     4740        }
     4741    }
     4742
     4743    strlist = QStringList("WARNING");
     4744    strlist += "2: Could not locate requested file";
     4745    SendResponse(pbssock, strlist);
     4746}
     4747
    46204748void MainServer::HandleBackendRefresh(MythSocket *socket)
    46214749{
    46224750    gContext->RefreshBackendConfig();