Ticket #5737: upnp-recordings-subfolders-2.diff

File upnp-recordings-subfolders-2.diff, 23.9 KB (added by Xavier Hervy <xavier.hervy@…>, 16 years ago)

No change apart for coding convention and update description on top of the cpp file

  • upnpcdstv.cpp

     
    2020
    2121
    2222/*
    23    Recordings                              RecTv
    24     - All Programs                         RecTv/All
    25       + <recording 1>                      RecTv/All/item?ChanId=1004&StartTime=2006-04-06T20:00:00
     23   Recordings                   RecTv
     24    - All Programs              RecTv/0
     25      + <recording 1>           RecTv/0/item?ChanId=1004&StartTime=2006-04-06T20:00:00
    2626      + <recording 2>
    2727      + <recording 3>
    28     - By Title                             RecTv/title
    29       - <title 1>                          RecTv/title/key=Stargate SG-1
    30         + <recording 1>                    RecTv/title/key=Stargate SG-1/item?ChanId=1004&StartTime=2006-04-06T20:00:00
    31         + <recording 2>
    32     - By Genre
    33     - By Date
    34     - By Channel
    35     - By Group
     28    - By Title                  RecTv/1
     29      - <title 1>               RecTv/1/key=Stargate SG-1
     30        - All Programs          RecTv/1/key=Stargate SG-1/0
     31            + <recording 1>     RecTv/1/key=Stargate SG-1/0/item?ChanId=1004&StartTime=2006-04-06T20:00:00
     32            + <recording 2>
     33        - By Genre              RecTv/1/key=Stargate SG-1/2
     34        - By Date               RecTv/1/key=Stargate SG-1/3
     35        - By Channel            RecTv/1/key=Stargate SG-1/4
     36        - By Group              RecTv/1/key=Stargate SG-1/5
     37    - By Genre                  RecTv/2/
     38    - By Date                   RecTv/3/
     39    - By Channel                RecTv/4/
     40    - By Group                  RecTv/5/
    3641*/
    3742
    3843
     
    390395
    391396}
    392397
     398/////////////////////////////////////////////////////////////////////////////
     399//
     400/////////////////////////////////////////////////////////////////////////////
     401
     402/**
     403 * Overide UPnpCDSExtension::Browse to allow multiple sub folders
     404 */
     405UPnpCDSExtensionResults *UPnpCDSTv::Browse( UPnpCDSRequest *pRequest )
     406{
     407    // -=>TODO: Need to add Sorting Support.
     408
     409    if (!IsBrowseRequestForUs( pRequest ))
     410        return( NULL );
     411
     412    // ----------------------------------------------------------------------
     413    // Parse out request object's path
     414    // ----------------------------------------------------------------------
     415
     416    QStringList idPath = QStringList::split( "/", pRequest->m_sObjectId);
     417
     418    QString key = pRequest->m_sObjectId.section('=',1);
     419
     420    if (idPath.count() == 0)
     421        return( NULL );
     422
     423    // ----------------------------------------------------------------------
     424    // Process based on location in hierarchy
     425    // ----------------------------------------------------------------------
     426
     427    UPnpCDSExtensionResults *pResults = new UPnpCDSExtensionResults();
     428
     429    if (pResults != NULL)
     430    {
     431        QString sLast = idPath.last();
     432
     433        pRequest->m_sParentId = pRequest->m_sObjectId;
     434
     435        if (sLast == m_sExtensionId || sLast.startsWith( "key" , true ))
     436            return( ProcessAvailableGroup   ( pRequest, pResults, idPath ));
     437        if (sLast == "0" )
     438            return( ProcessResult    ( pRequest, pResults, idPath ));
     439        if (sLast.startsWith( "item", true ))
     440            return( ProcessItem   ( pRequest, pResults, idPath )); }
     441
     442        return( ProcessGroup( pRequest, pResults, idPath ));
     443    }
     444
     445    return( pResults );   
     446}
     447
     448
     449/////////////////////////////////////////////////////////////////////////////
     450//
     451/////////////////////////////////////////////////////////////////////////////
     452/**
     453 * return the BrowseMetadata title according to the path
     454 * Ex:
     455 * idPath=RecTv
     456 *  => Recordings
     457 * idPath=RecTv/2/key=Animation
     458 *  => Animation
     459 * idPath=RecTv/2/key=Animation/1
     460 *  => Animation By Title
     461 * idPath=RecTv/2/key=Animation/1/key=Family Guy/0
     462 *  => Animation, Family Guy
     463 */
     464QString UPnpCDSTv::GetMetadataTitle(QStringList &idPath)
     465{
     466    QString sTitle ("");
     467                       
     468    UPnpCDSRootInfo *pInfo = NULL;
     469    for(QStringList::const_iterator it = idPath.begin() ; it != idPath.end() ;
     470                                                                        ++it )
     471    {
     472        if ((*it).startsWith("key"))
     473        {
     474            QString sKey = (*it).section( '=', 1, 1 );
     475            Q3Url::decode( sKey );
     476            if (pInfo==NULL)
     477            {
     478                VERBOSE( VB_UPNP, QString("UPnpCDSTv:: GetMetadataTitle Key '%1'"
     479                                          " without group, ignoring...")
     480                                          .arg(sKey));
     481            }
     482            else
     483            {
     484                if (sTitle.count() == 0 )
     485                    sTitle = QString ("%1").arg(sKey);
     486                else
     487                    sTitle = QString ("%1, %2").arg(sTitle).arg(sKey);
     488                pInfo=NULL;
     489            }   
     490        }
     491        else
     492        { //must be a group then
     493            bool ok;
     494            int nNodeIdx = (*it).toInt(&ok, 10);
     495            if (ok && nNodeIdx < GetRootCount() && nNodeIdx > 0)
     496            {
     497                pInfo = GetRootInfo( nNodeIdx );
     498            }
     499            else
     500            {
     501                VERBOSE( VB_UPNP, QString("UPnpCDSTv:: GetMetadataTitle can not"
     502                                          "find group '%1', ignoring...")
     503                                          .arg((*it)));
     504            }
     505        }
     506    }
     507    if (pInfo != NULL)
     508    {
     509        if (sTitle.count() == 0 )
     510        {
     511            sTitle = QString ("%1").arg(QObject::tr( pInfo->title ));
     512        }
     513        else
     514        {
     515            sTitle = QString ("%1 %2").arg(sTitle).arg(
     516                                                QObject::tr( pInfo->title ));
     517        }
     518    }
     519
     520    if (sTitle.count()==0) sTitle=m_sName;
     521    return sTitle;
     522}
     523
     524/////////////////////////////////////////////////////////////////////////////
     525//
     526/////////////////////////////////////////////////////////////////////////////
     527/** Build the clause Where according to idPath
     528 * Ex:
     529 * idPath=RecTv/2/key=Animation
     530 *  => WHERE genre="Animation"
     531 * idPath=RecTv/2/key=Animation/1/key=Family Guy
     532 *  => WHERE genre="Animation" AND title="Family Guy"
     533 */
     534QString UPnpCDSTv::buildWhereClause( QStringList idPath )
     535{
     536
     537    QString sWhere ("");
     538                       
     539    UPnpCDSRootInfo *pInfo = NULL;
     540    for(QStringList::const_iterator it = idPath.begin() ; it != idPath.end() ;
     541                                                                          ++it )
     542    {
     543        if ((*it).startsWith("key"))
     544        {
     545            QString sKey = (*it).section( '=', 1, 1 );
     546            Q3Url::decode( sKey );
     547            if (pInfo==NULL)
     548            {
     549                VERBOSE( VB_UPNP, QString("UPnpCDSTv:: buildWhereClause Key '%1'"
     550                                          " without group, ignoring...")
     551                                          .arg(sKey));
     552            }
     553            else
     554            {
     555                if (sWhere.count() == 0 )
     556                    sWhere = QString ("WHERE %1=\"%2\"")
     557                                    .arg(pInfo->column).arg(sKey);
     558                else
     559                    sWhere = QString ("%1 AND %2=\"%3\"")
     560                                    .arg(sWhere).arg(pInfo->column).arg(sKey);
     561                pInfo=NULL;
     562            }   
     563        }
     564        else
     565        { //must be a group then
     566            bool ok;
     567            int nNodeIdx = (*it).toInt(&ok, 10);
     568            if (ok && nNodeIdx < GetRootCount() && nNodeIdx > 0)
     569            {
     570                pInfo = GetRootInfo( nNodeIdx );
     571            }
     572            else
     573            {
     574                VERBOSE( VB_UPNP, QString("UPnpCDSTv:: buildWhereClause can not "
     575                                          "find group '%1', ignoring...")
     576                                          .arg((*it)));
     577            }
     578        }
     579    }
     580    return sWhere;
     581
     582}
     583
     584/////////////////////////////////////////////////////////////////////////////
     585//
     586/////////////////////////////////////////////////////////////////////////////
     587/** Count how many group are still available
     588 * Ex:
     589 * idPath = RecTv/2/key=Animation/ key 0 1 3 4 5 still available => 5
     590 */
     591int UPnpCDSTv::countAvailableGroup( QStringList idPath )
     592{
     593
     594    int count = GetRootCount();
     595               
     596    UPnpCDSRootInfo *pInfo = NULL;
     597    for(QStringList::const_iterator it = idPath.begin() ; it != idPath.end() ;
     598                                                                          ++it )
     599    {
     600        if ((*it).startsWith("key"))
     601        {
     602            QString sKey = (*it).section( '=', 1, 1 );
     603            Q3Url::decode( sKey );
     604            if (pInfo==NULL)
     605            {
     606                VERBOSE( VB_UPNP, QString("UPnpCDSTv:: countAvailableGroup Key "
     607                                          "'%1' without group, ignoring...")
     608                                          .arg(sKey));
     609            }
     610            else
     611            {
     612                count--;
     613                pInfo=NULL;
     614            }   
     615        }
     616        else
     617        { //must be a group then
     618            bool ok;
     619            int nNodeIdx = (*it).toInt(&ok, 10);
     620            if (ok && nNodeIdx < GetRootCount() && nNodeIdx > 0)
     621            {
     622                pInfo = GetRootInfo( nNodeIdx );
     623            }
     624            else
     625            {
     626                VERBOSE( VB_UPNP, QString("UPnpCDSTv:: countAvailableGroup can "
     627                                          " not find group '%1', ignoring...")
     628                                          .arg((*it)));
     629            }
     630        }
     631    }
     632    return count;
     633}
     634
     635/////////////////////////////////////////////////////////////////////////////
     636//
     637/////////////////////////////////////////////////////////////////////////////
     638
     639int UPnpCDSTv::GetDistinctCount( UPnpCDSRootInfo *pInfo, const QString &sWhere )
     640{
     641    int nCount = 0;
     642
     643    if ((pInfo == NULL) || (pInfo->column == NULL))
     644        return 0;
     645
     646    MSqlQuery query(MSqlQuery::InitCon());
     647
     648    if (query.isConnected())
     649    {
     650        // Note: Tried to use Bind, however it would not allow me to use it
     651        //       for column & table names
     652
     653        QString sSQL;
     654       
     655        if (strncmp( pInfo->column, "*", 1) == 0)
     656        {
     657            sSQL = QString( "SELECT count( %1 ) FROM %2 %3" )
     658                      .arg( pInfo->column )
     659                      .arg( GetTableName( pInfo->column ) )
     660                      .arg( sWhere );
     661        }
     662        else
     663        {
     664            sSQL = QString( "SELECT count( DISTINCT %1 ) FROM %2 %3" )
     665                      .arg( pInfo->column )
     666                      .arg( GetTableName( pInfo->column ) )
     667                      .arg( sWhere );
     668        }
     669
     670        query.prepare( sSQL );
     671       VERBOSE( VB_UPNP, QString("UPnpCDSTv::GetDistinctCount2 %1 %2")
     672                                                .arg(pInfo->column).arg(sSQL) );
     673        query.exec();
     674
     675        if (query.size() > 0)
     676        {
     677            query.next();
     678
     679            nCount = query.value(0).toInt();
     680        }
     681    }
     682
     683    return( nCount );
     684    return (1);
     685}
     686
     687/////////////////////////////////////////////////////////////////////////////
     688//
     689/////////////////////////////////////////////////////////////////////////////
     690/*
     691 * Ex :
     692 * idPath = RecTv/2
     693 * idPath = RecTv/2/key=Animation/1
     694 */
     695UPnpCDSExtensionResults *UPnpCDSTv::ProcessGroup( UPnpCDSRequest          *pRequest,
     696                                                  UPnpCDSExtensionResults *pResults,
     697                                                  QStringList             &idPath )
     698
     699{
     700    pResults->m_nUpdateID     = 1;
     701    pResults->m_nTotalMatches = 0;
     702
     703    QString sLast = idPath.last();
     704    int nNodeIdx = sLast.toInt();
     705
     706    if ((nNodeIdx < 0) && (nNodeIdx > GetRootCount()))
     707    {
     708        pResults->m_eErrorCode = UPnPResult_CDS_NoSuchObject;
     709        pResults->m_sErrorDesc = "";
     710        return pResults;
     711    }
     712
     713    UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx );
     714
     715
     716    VERBOSE( VB_UPNP, QString("UPnpCDSTv::ProcessGroup idPath %1")
     717                                .arg(idPath.join("|")) );
     718   
     719    QStringList idPathParent = QStringList(idPath);
     720    idPathParent.removeLast();
     721
     722    if (pInfo == NULL)
     723        return pResults;
     724
     725    switch( pRequest->m_eBrowseFlag )
     726    {
     727        case CDS_BrowseMetadata:
     728        {
     729            // --------------------------------------------------------------
     730            // Return Container Object Only
     731            // --------------------------------------------------------------
     732
     733            pResults->m_nTotalMatches   = 1;
     734            pResults->m_nUpdateID       = 1;
     735
     736            QStringList idParentPath = QStringList(idPath);
     737            idParentPath.removeLast();
     738            CDSObject *pItem = CreateContainer( pRequest->m_sObjectId, 
     739                                                GetMetadataTitle(idPath),
     740                                                idParentPath.join("/") );
     741
     742            QString sWhere = buildWhereClause(idPath);
     743            pItem->SetChildCount( GetDistinctCount2( pInfo,sWhere ));
     744
     745            pResults->Add( pItem );
     746
     747            break;
     748        }
     749
     750        case CDS_BrowseDirectChildren:
     751        {
     752            QString sWhere = buildWhereClause(idPath);
     753            pResults->m_nTotalMatches = GetDistinctCount2( pInfo,sWhere );
     754            pResults->m_nUpdateID     = 1;
     755
     756            if (pRequest->m_nRequestedCount == 0)
     757                pRequest->m_nRequestedCount = SHRT_MAX;
     758
     759            MSqlQuery query(MSqlQuery::InitCon());
     760
     761            if (query.isConnected())
     762            {
     763
     764                QString sSQL = pInfo->sql;
     765
     766                sSQL = sSQL.arg(buildWhereClause(idPath));
     767                sSQL += QString( " LIMIT %2, %3" )
     768                           .arg( pRequest->m_nStartingIndex  )
     769                           .arg( pRequest->m_nRequestedCount );
     770
     771                query.prepare( sSQL );
     772                query.exec();
     773
     774                if (query.isActive() && query.size() > 0)
     775                {
     776
     777                    while(query.next())
     778                    {
     779                        QString sKey   = query.value(0).toString();
     780                        QString sTitle = query.value(1).toString();
     781
     782                        if (sTitle.length() == 0)
     783                            sTitle = "(undefined)";
     784
     785                        QString sId = QString( "%1/key=%2" )
     786                                         .arg( pRequest->m_sParentId )
     787                                         .arg( sKey );
     788
     789                        CDSObject *pRoot = CreateContainer( sId, sTitle,
     790                                                        pRequest->m_sParentId );
     791                       
     792                        pRoot->SetChildCount( countAvailableGroup(idPath)-1 );
     793
     794                        pResults->Add( pRoot );
     795                    }
     796                }
     797            }
     798
     799            break;
     800        }
     801
     802        case CDS_BrowseUnknown:
     803            break;
     804
     805    }
     806
     807    return pResults;
     808}
     809
     810/////////////////////////////////////////////////////////////////////////////
     811//
     812/////////////////////////////////////////////////////////////////////////////
     813/*
     814 * Ex :
     815 * idPath = RecTv
     816 * idPath = RecTv/2/key=Animation
     817 */
     818UPnpCDSExtensionResults *UPnpCDSTv::ProcessAvailableGroup( UPnpCDSRequest          *pRequest,
     819                                                           UPnpCDSExtensionResults *pResults,
     820                                                           QStringList             &idPath )
     821{
     822    pResults->m_nTotalMatches   = 0;
     823    pResults->m_nUpdateID       = 1;
     824
     825    short nRootCount = GetRootCount();
     826
     827    switch( pRequest->m_eBrowseFlag )
     828    {
     829        case CDS_BrowseMetadata:
     830        {
     831            // --------------------------------------------------------------
     832            // Return Root Object Only
     833            // --------------------------------------------------------------
     834
     835            pResults->m_nTotalMatches   = 1;
     836            pResults->m_nUpdateID       = 1;
     837
     838            QStringList idParentPath = QStringList(idPath);
     839            idParentPath.removeLast();
     840            QString parentId = idParentPath.join("/");
     841            if (parentId.count()==0) parentId="0";
     842            CDSObject *pRoot = CreateContainer( pRequest->m_sObjectId, 
     843                                                GetMetadataTitle(idPath),
     844                                                parentId );
     845
     846            pRoot->SetChildCount( countAvailableGroup(idPath));
     847
     848            pResults->Add( pRoot );
     849
     850            break;
     851        }
     852
     853        case CDS_BrowseDirectChildren:
     854        {
     855            pResults->m_nUpdateID     = 1;
     856            pResults->m_nTotalMatches = countAvailableGroup(idPath);
     857           
     858            if ( pRequest->m_nRequestedCount == 0)
     859                pRequest->m_nRequestedCount = pResults->m_nTotalMatches;
     860
     861            short nStart = Max( pRequest->m_nStartingIndex, short( 0 ));
     862            short nEnd   = Min( nRootCount,
     863                                short( nStart + pRequest->m_nRequestedCount));
     864
     865            if (nStart < nRootCount)
     866            {
     867                for (short nIdx = nStart; nIdx < nEnd; nIdx++)
     868                {
     869                    QString strnIdx = QString("%1").arg(nIdx);
     870                    if (!idPath.contains(strnIdx,Qt::CaseSensitive))
     871                    {// Show unused key
     872                        UPnpCDSRootInfo *pInfo = GetRootInfo( nIdx );
     873                        if (pInfo != NULL)
     874                        {
     875
     876                            QString sId = QString( "%1/%2" )
     877                                                .arg( pRequest->m_sObjectId )
     878                                                .arg( nIdx );
     879
     880                            QStringList idPathParent = QStringList(idPath);
     881                            idPathParent.removeLast();
     882                            CDSObject *pItem = CreateContainer( sId, 
     883                                                                QObject::tr( pInfo->title ),
     884                                                                idPath.join("/"));
     885
     886                            QString sWhere = buildWhereClause(idPath);
     887                            if (nIdx == 0 )
     888                                pItem->SetChildCount(GetDistinctCount( pInfo,sWhere ) );
     889                            else
     890                                pItem->SetChildCount(countAvailableGroup(idPath)-1 );               
     891
     892                            pResults->Add( pItem );
     893                        }
     894                    }
     895                }
     896            }
     897        }
     898
     899        case CDS_BrowseUnknown:
     900        default:
     901            break;
     902    }
     903
     904    return pResults;
     905}
     906
     907/////////////////////////////////////////////////////////////////////////////
     908//
     909/////////////////////////////////////////////////////////////////////////////
     910
     911/**
     912 * process the result for path finishing by 0 i.e list of videoItem
     913 * Ex:
     914 * RecTv/0
     915 * RecTv/2/key=Animation/0
     916 * RecTv/2/key=Animation/1/key=Family Guy/0
     917 */
     918UPnpCDSExtensionResults *UPnpCDSTv::ProcessResult( UPnpCDSRequest          *pRequest,
     919                                                   UPnpCDSExtensionResults *pResults,
     920                                                   QStringList             &idPath )
     921{
     922
     923    pResults->m_nTotalMatches   = 0;
     924    pResults->m_nUpdateID       = 1;
     925
     926    // ----------------------------------------------------------------------
     927    //
     928    // ----------------------------------------------------------------------
     929   
     930
     931    switch( pRequest->m_eBrowseFlag )
     932    {
     933
     934        case CDS_BrowseMetadata:
     935        {             
     936            pResults->m_nTotalMatches   = 1;
     937            pResults->m_nUpdateID       = 1;
     938
     939            QStringList idParentPath = QStringList(idPath);
     940            idParentPath.removeLast();
     941            CDSObject *pItem = CreateContainer( pRequest->m_sObjectId, 
     942                                                GetMetadataTitle(idPath),
     943                                                idParentPath.join("/") );
     944
     945            QString sWhere = buildWhereClause(idPath);
     946            pItem->SetChildCount( GetDistinctCount( GetRootInfo( 0 ), sWhere ) );
     947
     948            pResults->Add( pItem );
     949            break;
     950        }
     951
     952        case CDS_BrowseDirectChildren:
     953        {
     954       
     955            {
     956                pResults->m_nTotalMatches = 0;
     957                pResults->m_nUpdateID     = 1;
     958
     959                QString sWhere = buildWhereClause(idPath);
     960                pResults->m_nTotalMatches =
     961                                    GetDistinctCount( GetRootInfo( 0 ), sWhere );
     962                pResults->m_nUpdateID     = 1;
     963
     964                if (pRequest->m_nRequestedCount == 0)
     965                    pRequest->m_nRequestedCount = SHRT_MAX;
     966
     967                MSqlQuery query(MSqlQuery::InitCon());
     968
     969                if (query.isConnected())
     970                {
     971                   
     972                    UPnpCDSRootInfo *pInfo = GetRootInfo( 0 );
     973                    QString sSQL = QString( "%1 %2 LIMIT %3, %4" )
     974                                        .arg( GetItemListSQL( pInfo->column )  )
     975                                        .arg( sWhere )
     976                                        .arg( pRequest->m_nStartingIndex  )
     977                                        .arg( pRequest->m_nRequestedCount );
     978                    VERBOSE( VB_UPNP, QString("UPnpCDSExtension::ProcessResult %1")
     979                                        .arg(sSQL) );
     980
     981                    query.prepare  ( sSQL );
     982
     983                    query.exec();
     984
     985                    if (query.isActive() && query.size() > 0)
     986                    {
     987                        while(query.next())
     988                            AddItem( pRequest->m_sObjectId, pResults,
     989                                     true, query );
     990                    }
     991                }
     992            }
     993
     994            break;
     995        }
     996
     997        case CDS_BrowseUnknown:
     998            default:
     999            break;
     1000    }
     1001
     1002    return pResults;                                                                   
     1003}
     1004
  • upnpcdstv.h

     
    2828        QStringMap             m_mapBackendIp;
    2929        QStringMap             m_mapBackendPort;
    3030
     31        QString                buildWhereClause   ( QStringList idPath );
     32        int                    countAvailableGroup( QStringList idPath );
     33        int                    GetDistinctCount  ( UPnpCDSRootInfo *pInfo,
     34                                                    const QString &sWhere );
     35
     36        QString GetMetadataTitle(QStringList &idPath);
     37
    3138    protected:
    3239
    3340        virtual bool             IsBrowseRequestForUs( UPnpCDSRequest *pRequest );
     
    4653                                          bool                     bAddRef,
    4754                                          MSqlQuery               &query );
    4855
     56        UPnpCDSExtensionResults *ProcessResult( UPnpCDSRequest          *pRequest,
     57                                                UPnpCDSExtensionResults *pResults,
     58                                                QStringList             &idPath );
     59
     60        //process group ie for Genre Animation, Comedy, ....
     61        UPnpCDSExtensionResults *ProcessGroup( UPnpCDSRequest          *pRequest,
     62                                               UPnpCDSExtensionResults *pResults,
     63                                               QStringList             &idPath );
     64        //process available group ie All Recording, By Genre ...
     65        UPnpCDSExtensionResults *ProcessAvailableGroup( UPnpCDSRequest          *pRequest,
     66                                                        UPnpCDSExtensionResults *pResults,
     67                                                        QStringList             &idPath );
    4968    public:
    5069
    5170        UPnpCDSTv( ) : UPnpCDSExtension( "Recordings", "RecTv",
     
    5473        }
    5574
    5675        virtual ~UPnpCDSTv() {}
     76        virtual UPnpCDSExtensionResults *Browse( UPnpCDSRequest *pRequest );
    5777};
    5878
    5979#endif