Go to the documentation of this file.
25 #define LOC QString("UPnpCDSVideo: ")
26 #define LOC_WARN QString("UPnpCDSVideo, Warning: ")
27 #define LOC_ERR QString("UPnpCDSVideo, Error: ")
31 "object.item.videoItem" )
60 pRequest->m_nRequestedCount = 0;
69 QObject::tr(
"All Videos"),
83 QObject::tr(
"Movies"),
97 QObject::tr(
"Series"),
120 QObject::tr(
"Genre"),
194 LOG(VB_UPNP, LOG_INFO,
195 "UPnpCDSVideo::IsBrowseRequestForUs - Not sure... Calling base class.");
260 const IDTokenMap& tokens,
const QString& currentToken)
262 if (currentToken.isEmpty())
264 LOG(VB_GENERAL, LOG_ERR, QString(
"UPnpCDSTV::LoadMetadata: Final "
265 "token missing from id: %1")
271 if (tokens[currentToken].isEmpty())
282 pResults->
Add(container);
286 LOG(VB_GENERAL, LOG_ERR, QString(
"UPnpCDSTV::LoadMetadata: Requested "
287 "object cannot be found: %1")
290 else if (currentToken ==
"series")
292 return LoadSeries(pRequest, pResults, tokens);
294 else if (currentToken ==
"season")
298 else if (currentToken ==
"genre")
300 return LoadGenres(pRequest, pResults, tokens);
302 else if (currentToken ==
"movie")
304 return LoadMovies(pRequest, pResults, tokens);
306 else if (currentToken ==
"video")
308 return LoadVideos(pRequest, pResults, tokens);
312 LOG(VB_GENERAL, LOG_ERR,
313 QString(
"UPnpCDSVideo::LoadMetadata(): "
314 "Unhandled metadata request for '%1'.").arg(currentToken));
326 const IDTokenMap& tokens,
const QString& currentToken)
328 if (currentToken.isEmpty() || currentToken ==
m_sExtensionId.toLower())
335 if (currentToken ==
"series")
337 if (!tokens[
"series"].isEmpty())
339 return LoadSeries(pRequest, pResults, tokens);
341 if (currentToken ==
"season")
343 if (!tokens[
"season"].isEmpty() && tokens[
"season"].toInt() >= 0)
344 return LoadVideos(pRequest, pResults, tokens);
347 if (currentToken ==
"genre")
349 if (!tokens[
"genre"].isEmpty())
350 return LoadVideos(pRequest, pResults, tokens);
351 return LoadGenres(pRequest, pResults, tokens);
353 if (currentToken ==
"movie")
355 return LoadMovies(pRequest, pResults, tokens);
357 if (currentToken ==
"video")
359 return LoadVideos(pRequest, pResults, tokens);
361 LOG(VB_GENERAL, LOG_ERR,
362 QString(
"UPnpCDSVideo::LoadChildren(): "
363 "Unhandled metadata request for '%1'.").arg(currentToken));
385 QString sql =
"SELECT SQL_CALC_FOUND_ROWS "
386 "v.title, COUNT(DISTINCT v.season), v.intid "
387 "FROM videometadata v "
391 "LIMIT :OFFSET,:COUNT ";
394 clauses.append(
"contenttype='TELEVISION'");
397 query.
prepare(sql.arg(whereString));
409 QString sTitle = query.
value(0).toString();
410 int nSeasonCount = query.
value(1).toInt();
411 int nVidID = query.
value(2).toInt();
418 pContainer->
SetPropValue(
"description", QObject::tr(
"%n Seasons",
"", nSeasonCount));
419 pContainer->
SetPropValue(
"longdescription", QObject::tr(
"%n Seasons",
"", nSeasonCount));
427 pResults->
Add(pContainer);
433 if (query.
size() >= 0)
437 query.
prepare(
"SELECT FOUND_ROWS()");
461 QString sql =
"SELECT SQL_CALC_FOUND_ROWS "
462 "v.season, COUNT(DISTINCT v.intid), v.intid "
463 "FROM videometadata v "
467 "LIMIT :OFFSET,:COUNT ";
472 query.
prepare(sql.arg(whereString));
484 int nSeason = query.
value(0).toInt();
485 int nVideoCount = query.
value(1).toInt();
486 int nVidID = query.
value(2).toInt();
488 QString sTitle = QObject::tr(
"Season %1").arg(nSeason);
495 pContainer->
SetPropValue(
"description", QObject::tr(
"%n Episode(s)",
"", nVideoCount));
496 pContainer->
SetPropValue(
"longdescription", QObject::tr(
"%n Episode(s)",
"", nVideoCount));
504 pResults->
Add(pContainer);
510 if (query.
size() >= 0)
514 query.
prepare(
"SELECT FOUND_ROWS()");
529 tokens[
"type"] =
"MOVIE";
531 return LoadVideos(pRequest, pResults, tokens);
551 QString sql =
"SELECT SQL_CALC_FOUND_ROWS "
552 "v.category, g.genre, COUNT(DISTINCT v.intid) "
553 "FROM videometadata v "
554 "LEFT JOIN videogenre g ON g.intid=v.category "
558 "LIMIT :OFFSET,:COUNT ";
561 clauses.append(
"v.category != 0");
564 query.
prepare(sql.arg(whereString));
576 int nGenreID = query.
value(0).toInt();
577 QString sName = query.
value(1).toString();
578 int nVideoCount = query.
value(2).toInt();
589 pResults->
Add(pContainer);
595 if (query.
size() >= 0)
599 query.
prepare(
"SELECT FOUND_ROWS()");
623 QString sql =
"SELECT SQL_CALC_FOUND_ROWS "
624 "v.intid, title, subtitle, filename, director, plot, "
625 "rating, year, userrating, length, "
626 "season, episode, coverfile, insertdate, host, "
627 "g.genre, studio, collectionref, contenttype "
628 "FROM videometadata v "
629 "LEFT JOIN videogenre g ON g.intid=v.category "
631 "ORDER BY title, season, episode "
632 "LIMIT :OFFSET,:COUNT ";
637 query.
prepare(sql.arg(whereString));
650 int nVidID = query.
value( 0).toInt();
651 QString sTitle = query.
value( 1).toString();
652 QString sSubtitle = query.
value( 2).toString();
653 QString sFilePath = query.
value( 3).toString();
654 QString sDirector = query.
value( 4).toString();
655 QString sPlot = query.
value( 5).toString();
657 int nYear = query.
value( 7).toInt();
660 auto nLength = std::chrono::minutes(query.
value( 9).toUInt());
662 int nSeason = query.
value(10).toInt();
663 int nEpisode = query.
value(11).toInt();
664 QString sCoverArt = query.
value(12).toString();
665 QDateTime dtInsertDate =
667 QString sHostName = query.
value(14).toString();
668 QString sGenre = query.
value(15).toString();
671 QString sContentType = query.
value(18).toString();
683 if (sHostName.isEmpty())
697 if (sHostName.isEmpty())
714 QString sName = sTitle;
715 if( !sSubtitle.isEmpty() )
717 sName +=
" - " + sSubtitle;
721 URIBase.setScheme(
"http");
726 if (sContentType ==
"MOVIE")
739 if (!sSubtitle.isEmpty())
742 pItem->
SetPropValue(
"description", sPlot.left(128).append(
" ..."));
746 if (nEpisode > 0 || nSeason > 0)
750 pItem->
SetPropValue(
"episodeNumber" , QString::number(nEpisode));
755 if (nYear > 1830 && nYear < 9999)
780 QString sRefId = QString(
"%1=%2")
791 QString sFullFileName = sFilePath;
795 sFullFileName = sgroup.
FindFile( sFullFileName );
797 QFileInfo fInfo( sFullFileName );
812 QUrl resURI = URIBase;
814 resURI.setPath(
"/Content/GetVideo");
815 resQuery.addQueryItem(
"Id", QString::number(nVidID));
816 resURI.setQuery(resQuery);
821 if (sMimeType ==
"video/mp2t" || sMimeType ==
"video/mp2p")
822 sMimeType =
"video/mpeg";
828 pRes->
AddAttribute(
"size" , QString(
"%1").arg(fInfo.size()) );
834 if (!sCoverArt.isEmpty() && (sCoverArt !=
"No Cover"))
839 pResults->
Add( pItem );
845 if (query.
size() >= 0)
849 query.
prepare(
"SELECT FOUND_ROWS()");
859 QUrl artURI = URIBase;
860 artURI.setPath(
"/Content/GetVideoArtwork");
862 artQuery.addQueryItem(
"Id", QString::number(nVidID));
863 artURI.setQuery(artQuery);
877 QUrl thumbURI = artURI;
878 QUrlQuery thumbQuery(thumbURI.query());
879 if (pItem->
m_sClass ==
"object.item.videoItem")
880 thumbQuery.addQueryItem(
"Type",
"screenshot");
882 thumbQuery.addQueryItem(
"Type",
"coverart");
883 thumbQuery.addQueryItem(
"Width",
"160");
884 thumbQuery.addQueryItem(
"Height",
"160");
885 thumbURI.setQuery(thumbQuery);
889 QUrl smallURI = artURI;
890 QUrlQuery smallQuery(smallURI.query());
891 smallQuery.addQueryItem(
"Type",
"coverart");
892 smallQuery.addQueryItem(
"Width",
"640");
893 smallQuery.addQueryItem(
"Height",
"480");
894 smallURI.setQuery(smallQuery);
898 QUrl mediumURI = artURI;
899 QUrlQuery mediumQuery(mediumURI.query());
900 mediumQuery.addQueryItem(
"Type",
"coverart");
901 mediumQuery.addQueryItem(
"Width",
"1024");
902 mediumQuery.addQueryItem(
"Height",
"768");
903 mediumURI.setQuery(mediumQuery);
908 QUrl largeURI = artURI;
909 QUrlQuery largeQuery(largeURI.query());
910 largeQuery.addQueryItem(
"Type",
"fanart");
911 largeURI.setQuery(largeQuery);
913 QList<Property*> propList = pItem->
GetProperties(
"albumArtURI");
914 if (propList.size() >= 4)
919 pProp->
SetValue(mediumURI.toEncoded());
921 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
924 pProp = propList.at(1);
928 pProp->
SetValue(thumbURI.toEncoded());
930 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
933 pProp = propList.at(2);
936 pProp->
SetValue(smallURI.toEncoded());
938 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
941 pProp = propList.at(3);
944 pProp->
SetValue(largeURI.toEncoded());
946 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
950 if (pItem->
m_sClass.startsWith(
"object.item.videoItem"))
955 "image/jpeg", QSize(1024, 768));
956 pItem->
AddResource( sProtocol, mediumURI.toEncoded());
959 "image/jpeg", QSize(160, 160));
960 pItem->
AddResource( sProtocol, thumbURI.toEncoded());
963 "image/jpeg", QSize(640, 480));
964 pItem->
AddResource( sProtocol, smallURI.toEncoded());
967 "image/jpeg", QSize(1920, 1080));
968 pItem->
AddResource( sProtocol, largeURI.toEncoded());
974 if (tokens[
"video"].toInt() > 0)
975 clauses.append(
"v.intid=:VIDEO_ID");
976 if (!tokens[
"series"].isEmpty())
977 clauses.append(
"v.title=:TITLE");
978 if (!tokens[
"season"].isEmpty() && tokens[
"season"].toInt() >= 0)
979 clauses.append(
"v.season=:SEASON");
980 if (!tokens[
"type"].isEmpty())
981 clauses.append(
"v.contenttype=:TYPE");
982 if (tokens[
"genre"].toInt() > 0)
983 clauses.append(
"v.category=:GENRE_ID");
986 if (!clauses.isEmpty())
988 whereString =
" WHERE ";
989 whereString.append(clauses.join(
" AND "));
997 if (tokens[
"video"].toInt() > 0)
998 query.
bindValue(
":VIDEO_ID", tokens[
"video"]);
999 if (!tokens[
"series"].isEmpty())
1000 query.
bindValue(
":TITLE", tokens[
"series"]);
1001 if (!tokens[
"season"].isEmpty() && tokens[
"season"].toInt() >= 0)
1002 query.
bindValue(
":SEASON", tokens[
"season"]);
1003 if (!tokens[
"type"].isEmpty())
1004 query.
bindValue(
":TYPE", tokens[
"type"]);
1005 if (tokens[
"genre"].toInt() > 0)
1006 query.
bindValue(
":GENRE_ID", tokens[
"genre"]);
QList< Property * > GetProperties(const QString &sName)
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
QSqlQuery wrapper that fetches a DB connection from the connection pool.
uint16_t m_nRequestedCount
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
QDateTime as_utc(const QDateTime &old_dt)
Returns copy of QDateTime with TimeSpec set to UTC.
bool LoadSeries(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
QString DateTimeFormat(const QDateTime &dateTime)
Date-Time Format.
QString FindFile(const QString &filename)
int GetBackendStatusPort(void)
Returns the locally defined backend status port.
static QString CreateIDString(const QString &RequestId, const QString &Name, int Value)
QVariant value(int i) const
void CreateRoot() override
uint16_t m_nStartingIndex
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
virtual bool IsSearchRequestForUs(UPnpCDSRequest *pRequest)
void AddAttribute(const QString &sName, const QString &sValue)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
static CDSObject * CreateMovie(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
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.
bool IsBrowseRequestForUs(UPnpCDSRequest *pRequest) override
bool LoadVideos(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
static void PopulateArtworkURIS(CDSObject *pItem, int nVidID, const QUrl &URIBase)
void SetChildContainerCount(uint32_t nCount)
Allows the caller to set childContainerCount without having to load children.
void AddAttribute(const QString &sName, const QString &sValue)
CDSShortCutList m_shortcuts
void SetValue(const QString &value)
static QString GetMimeType(const QString &sFileExtension)
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
bool LoadMovies(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, IDTokenMap tokens)
void Add(CDSObject *pObject)
void SetPropValue(const QString &sName, const QString &sValue, const QString &type="")
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
bool IsSearchRequestForUs(UPnpCDSRequest *pRequest) override
void SetChildCount(uint32_t nCount)
Allows the caller to set childCount without having to load children.
bool LoadSeasons(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
static bool LoadGenres(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
CDSObject * AddChild(CDSObject *pChild)
QMap< QString, int > m_mapBackendPort
virtual CDSObject * GetRoot()
Resource * AddResource(const QString &sProtocol, const QString &sURI)
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
QMap< QString, QString > IDTokenMap
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)
static CDSObject * CreateVideoItem(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
static CDSObject * CreateContainer(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
static CDSObject * CreateAlbum(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
bool LoadChildren(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens, const QString ¤tToken) override
Fetch the children of the container identified in the request.
uint32_t GetChildCount(void) const
Return the number of children in this container.
CDSObject * GetChild(const QString &sID)
static CDSObject * CreateMovieGenre(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
QStringMap m_mapBackendIp
QString resDurationFormat(std::chrono::milliseconds msec)
res@duration Format B.2.1.4 res@duration - UPnP ContentDirectory Service 2008, 2013
static QString BuildWhereClause(QStringList clauses, IDTokenMap tokens)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
virtual bool IsBrowseRequestForUs(UPnpCDSRequest *pRequest)