23 #define LOC QString("UPnpCDSVideo: ") 24 #define LOC_WARN QString("UPnpCDSVideo, Warning: ") 25 #define LOC_ERR QString("UPnpCDSVideo, Error: ") 29 "object.item.videoItem" )
58 pRequest->m_nRequestedCount = 0;
67 QObject::tr(
"All Videos"),
81 QObject::tr(
"Movies"),
95 QObject::tr(
"Series"),
118 QObject::tr(
"Genre"),
192 LOG(VB_UPNP, LOG_INFO,
193 "UPnpCDSVideo::IsBrowseRequestForUs - Not sure... Calling base class.");
258 const IDTokenMap& tokens,
const QString& currentToken)
260 if (currentToken.isEmpty())
262 LOG(VB_GENERAL, LOG_ERR, QString(
"UPnpCDSTV::LoadMetadata: Final " 263 "token missing from id: %1")
269 if (tokens[currentToken].isEmpty())
280 pResults->
Add(container);
284 LOG(VB_GENERAL, LOG_ERR, QString(
"UPnpCDSTV::LoadMetadata: Requested " 285 "object cannot be found: %1")
288 else if (currentToken ==
"series")
290 return LoadSeries(pRequest, pResults, tokens);
292 else if (currentToken ==
"season")
296 else if (currentToken ==
"genre")
298 return LoadGenres(pRequest, pResults, tokens);
300 else if (currentToken ==
"movie")
302 return LoadMovies(pRequest, pResults, tokens);
304 else if (currentToken ==
"video")
306 return LoadVideos(pRequest, pResults, tokens);
309 LOG(VB_GENERAL, LOG_ERR,
310 QString(
"UPnpCDSVideo::LoadMetadata(): " 311 "Unhandled metadata request for '%1'.").arg(currentToken));
322 const IDTokenMap& tokens,
const QString& currentToken)
324 if (currentToken.isEmpty() || currentToken ==
m_sExtensionId.toLower())
331 if (currentToken ==
"series")
333 if (!tokens[
"series"].isEmpty())
335 return LoadSeries(pRequest, pResults, tokens);
337 if (currentToken ==
"season")
339 if (!tokens[
"season"].isEmpty() && tokens[
"season"].toInt() >= 0)
340 return LoadVideos(pRequest, pResults, tokens);
343 if (currentToken ==
"genre")
345 if (!tokens[
"genre"].isEmpty())
346 return LoadVideos(pRequest, pResults, tokens);
347 return LoadGenres(pRequest, pResults, tokens);
349 if (currentToken ==
"movie")
351 return LoadMovies(pRequest, pResults, tokens);
353 if (currentToken ==
"video")
355 return LoadVideos(pRequest, pResults, tokens);
357 LOG(VB_GENERAL, LOG_ERR,
358 QString(
"UPnpCDSVideo::LoadChildren(): " 359 "Unhandled metadata request for '%1'.").arg(currentToken));
381 QString sql =
"SELECT SQL_CALC_FOUND_ROWS " 382 "v.title, COUNT(DISTINCT v.season), v.intid " 383 "FROM videometadata v " 387 "LIMIT :OFFSET,:COUNT ";
390 clauses.append(
"contenttype='TELEVISION'");
393 query.
prepare(sql.arg(whereString));
405 QString sTitle = query.
value(0).toString();
406 int nSeasonCount = query.
value(1).toInt();
407 int nVidID = query.
value(2).toInt();
414 pContainer->
SetPropValue(
"description", QObject::tr(
"%n Seasons",
"", nSeasonCount));
415 pContainer->
SetPropValue(
"longdescription", QObject::tr(
"%n Seasons",
"", nSeasonCount));
423 pResults->
Add(pContainer);
429 if (query.
size() >= 0)
433 query.
prepare(
"SELECT FOUND_ROWS()");
457 QString sql =
"SELECT SQL_CALC_FOUND_ROWS " 458 "v.season, COUNT(DISTINCT v.intid), v.intid " 459 "FROM videometadata v " 463 "LIMIT :OFFSET,:COUNT ";
468 query.
prepare(sql.arg(whereString));
480 int nSeason = query.
value(0).toInt();
481 int nVideoCount = query.
value(1).toInt();
482 int nVidID = query.
value(2).toInt();
484 QString sTitle = QObject::tr(
"Season %1").arg(nSeason);
491 pContainer->
SetPropValue(
"description", QObject::tr(
"%n Episode(s)",
"", nVideoCount));
492 pContainer->
SetPropValue(
"longdescription", QObject::tr(
"%n Episode(s)",
"", nVideoCount));
500 pResults->
Add(pContainer);
506 if (query.
size() >= 0)
510 query.
prepare(
"SELECT FOUND_ROWS()");
525 tokens[
"type"] =
"MOVIE";
527 return LoadVideos(pRequest, pResults, tokens);
547 QString sql =
"SELECT SQL_CALC_FOUND_ROWS " 548 "v.category, g.genre, COUNT(DISTINCT v.intid) " 549 "FROM videometadata v " 550 "LEFT JOIN videogenre g ON g.intid=v.category " 554 "LIMIT :OFFSET,:COUNT ";
557 clauses.append(
"v.category != 0");
560 query.
prepare(sql.arg(whereString));
572 int nGenreID = query.
value(0).toInt();
573 QString sName = query.
value(1).toString();
574 int nVideoCount = query.
value(2).toInt();
585 pResults->
Add(pContainer);
591 if (query.
size() >= 0)
595 query.
prepare(
"SELECT FOUND_ROWS()");
619 QString sql =
"SELECT SQL_CALC_FOUND_ROWS " 620 "v.intid, title, subtitle, filename, director, plot, " 621 "rating, year, userrating, length, " 622 "season, episode, coverfile, insertdate, host, " 623 "g.genre, studio, collectionref, contenttype " 624 "FROM videometadata v " 625 "LEFT JOIN videogenre g ON g.intid=v.category " 627 "ORDER BY title, season, episode " 628 "LIMIT :OFFSET,:COUNT ";
633 query.
prepare(sql.arg(whereString));
646 int nVidID = query.
value( 0).toInt();
647 QString sTitle = query.
value( 1).toString();
648 QString sSubtitle = query.
value( 2).toString();
649 QString sFilePath = query.
value( 3).toString();
650 QString sDirector = query.
value( 4).toString();
651 QString sPlot = query.
value( 5).toString();
653 int nYear = query.
value( 7).toInt();
656 uint32_t nLength = query.
value( 9).toUInt();
658 nLength = (nLength * 60 *1000);
660 int nSeason = query.
value(10).toInt();
661 int nEpisode = query.
value(11).toInt();
662 QString sCoverArt = query.
value(12).toString();
663 QDateTime dtInsertDate =
665 QString sHostName = query.
value(14).toString();
666 QString sGenre = query.
value(15).toString();
669 QString sContentType = query.
value(18).toString();
681 if (sHostName.isEmpty())
695 if (sHostName.isEmpty())
712 QString sName = sTitle;
713 if( !sSubtitle.isEmpty() )
715 sName +=
" - " + sSubtitle;
719 URIBase.setScheme(
"http");
724 if (sContentType ==
"MOVIE")
737 if (!sSubtitle.isEmpty())
740 pItem->
SetPropValue(
"description", sPlot.left(128).append(
" ..."));
744 if (nEpisode > 0 || nSeason > 0)
748 pItem->
SetPropValue(
"episodeNumber" , QString::number(nEpisode));
753 if (nYear > 1830 && nYear < 9999)
778 QString sRefId = QString(
"%1=%2")
789 QString sFullFileName = sFilePath;
790 if (!QFile::exists( sFullFileName ))
793 sFullFileName = sgroup.
FindFile( sFullFileName );
795 QFileInfo fInfo( sFullFileName );
810 QUrl resURI = URIBase;
812 resURI.setPath(
"/Content/GetVideo");
813 resQuery.addQueryItem(
"Id", QString::number(nVidID));
814 resURI.setQuery(resQuery);
819 if (sMimeType ==
"video/mp2t" || sMimeType ==
"video/mp2p")
820 sMimeType =
"video/mpeg";
826 pRes->
AddAttribute(
"size" , QString(
"%1").arg(fInfo.size()) );
832 if (!sCoverArt.isEmpty() && (sCoverArt !=
"No Cover"))
837 pResults->
Add( pItem );
843 if (query.
size() >= 0)
847 query.
prepare(
"SELECT FOUND_ROWS()");
857 QUrl artURI = URIBase;
858 artURI.setPath(
"/Content/GetVideoArtwork");
860 artQuery.addQueryItem(
"Id", QString::number(nVidID));
861 artURI.setQuery(artQuery);
875 QUrl thumbURI = artURI;
876 QUrlQuery thumbQuery(thumbURI.query());
877 if (pItem->
m_sClass ==
"object.item.videoItem")
878 thumbQuery.addQueryItem(
"Type",
"screenshot");
880 thumbQuery.addQueryItem(
"Type",
"coverart");
881 thumbQuery.addQueryItem(
"Width",
"160");
882 thumbQuery.addQueryItem(
"Height",
"160");
883 thumbURI.setQuery(thumbQuery);
887 QUrl smallURI = artURI;
888 QUrlQuery smallQuery(smallURI.query());
889 smallQuery.addQueryItem(
"Type",
"coverart");
890 smallQuery.addQueryItem(
"Width",
"640");
891 smallQuery.addQueryItem(
"Height",
"480");
892 smallURI.setQuery(smallQuery);
896 QUrl mediumURI = artURI;
897 QUrlQuery mediumQuery(mediumURI.query());
898 mediumQuery.addQueryItem(
"Type",
"coverart");
899 mediumQuery.addQueryItem(
"Width",
"1024");
900 mediumQuery.addQueryItem(
"Height",
"768");
901 mediumURI.setQuery(mediumQuery);
906 QUrl largeURI = artURI;
907 QUrlQuery largeQuery(largeURI.query());
908 largeQuery.addQueryItem(
"Type",
"fanart");
909 largeURI.setQuery(largeQuery);
911 QList<Property*> propList = pItem->
GetProperties(
"albumArtURI");
912 if (propList.size() >= 4)
917 pProp->
SetValue(mediumURI.toEncoded());
919 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
922 pProp = propList.at(1);
926 pProp->
SetValue(thumbURI.toEncoded());
928 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
931 pProp = propList.at(2);
934 pProp->
SetValue(smallURI.toEncoded());
936 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
939 pProp = propList.at(3);
942 pProp->
SetValue(largeURI.toEncoded());
944 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
948 if (pItem->
m_sClass.startsWith(
"object.item.videoItem"))
953 "image/jpeg", QSize(1024, 768));
954 pItem->
AddResource( sProtocol, mediumURI.toEncoded());
957 "image/jpeg", QSize(160, 160));
958 pItem->
AddResource( sProtocol, thumbURI.toEncoded());
961 "image/jpeg", QSize(640, 480));
962 pItem->
AddResource( sProtocol, smallURI.toEncoded());
965 "image/jpeg", QSize(1920, 1080));
966 pItem->
AddResource( sProtocol, largeURI.toEncoded());
972 if (tokens[
"video"].toInt() > 0)
973 clauses.append(
"v.intid=:VIDEO_ID");
974 if (!tokens[
"series"].isEmpty())
975 clauses.append(
"v.title=:TITLE");
976 if (!tokens[
"season"].isEmpty() && tokens[
"season"].toInt() >= 0)
977 clauses.append(
"v.season=:SEASON");
978 if (!tokens[
"type"].isEmpty())
979 clauses.append(
"v.contenttype=:TYPE");
980 if (tokens[
"genre"].toInt() > 0)
981 clauses.append(
"v.category=:GENRE_ID");
984 if (!clauses.isEmpty())
986 whereString =
" WHERE ";
987 whereString.append(clauses.join(
" AND "));
995 if (tokens[
"video"].toInt() > 0)
996 query.
bindValue(
":VIDEO_ID", tokens[
"video"]);
997 if (!tokens[
"series"].isEmpty())
998 query.
bindValue(
":TITLE", tokens[
"series"]);
999 if (!tokens[
"season"].isEmpty() && tokens[
"season"].toInt() >= 0)
1000 query.
bindValue(
":SEASON", tokens[
"season"]);
1001 if (!tokens[
"type"].isEmpty())
1002 query.
bindValue(
":TYPE", tokens[
"type"]);
1003 if (tokens[
"genre"].toInt() > 0)
1004 query.
bindValue(
":GENRE_ID", tokens[
"genre"]);
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
void SetValue(const QString &value)
Resource * AddResource(const QString &sProtocol, const QString &sURI)
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
static void PopulateArtworkURIS(CDSObject *pItem, int nVidID, const QUrl &URIBase)
virtual CDSObject * GetRoot()
QStringMap m_mapBackendIp
static CDSObject * CreateContainer(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
QString toString(MarkTypes type)
CDSObject * AddChild(CDSObject *pChild)
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
static CDSObject * CreateMovieGenre(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
void SetChildCount(uint32_t nCount)
Allows the caller to set childCount without having to load children.
QString resDurationFormat(uint32_t msec)
res@duration Format B.2.1.4 res@duration - UPnP ContentDirectory Service 2008, 2013
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static QString GetMimeType(const QString &sFileExtension)
bool LoadMovies(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, IDTokenMap tokens)
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
bool IsSearchRequestForUs(UPnpCDSRequest *pRequest) override
QString ProtocolInfoString(UPNPProtocol::TransferProtocol protocol, const QString &mimeType, const QSize &resolution, double videoFrameRate, const QString &container, const QString &videoCodec, const QString &audioCodec, bool isTranscoded)
Create a properly formatted string for the 4th field of res@protocolInfo.
uint16_t m_nRequestedCount
QVariant value(int i) const
void SetChildContainerCount(uint32_t nCount)
Allows the caller to set childContainerCount without having to load children.
void CreateRoot() override
bool IsBrowseRequestForUs(UPnpCDSRequest *pRequest) override
bool LoadVideos(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
QMap< QString, int > m_mapBackendPort
static CDSObject * CreateVideoItem(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
QString DateTimeFormat(const QDateTime &dateTime)
Date-Time Format.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
void AddAttribute(const QString &sName, const QString &sValue)
QList< Property * > GetProperties(const QString &sName)
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
void Add(CDSObject *pObject)
uint32_t GetChildCount(void) const
Return the number of children in this container.
void AddAttribute(const QString &sName, const QString &sValue)
CDSObject * GetChild(const QString &sID)
static QString CreateIDString(const QString &RequestId, const QString &Name, int Value)
virtual bool IsBrowseRequestForUs(UPnpCDSRequest *pRequest)
int GetBackendStatusPort(void)
Returns the locally defined backend status port.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
static CDSObject * CreateMovie(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
QMap< QString, QString > IDTokenMap
#define LOG(_MASK_, _LEVEL_, _STRING_)
static CDSObject * CreateAlbum(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
void SetPropValue(const QString &sName, const QString &sValue, const QString &type="")
QString FindFile(const QString &filename)
bool LoadChildren(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens, const QString ¤tToken) override
Fetch the children of the container identified in the request.
uint16_t m_nStartingIndex
static QString BuildWhereClause(QStringList clauses, IDTokenMap tokens)
bool LoadSeasons(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
static bool LoadGenres(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
CDSShortCutList m_shortcuts
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
virtual bool IsSearchRequestForUs(UPnpCDSRequest *pRequest)
bool LoadSeries(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
bool LoadMetadata(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens, const QString ¤tToken) override
Fetch just the metadata for the item identified in the request.
static void BindValues(MSqlQuery &query, IDTokenMap tokens)