Go to the documentation of this file.
66 "object.item.audioItem.musicTrack" )
101 pRequest->m_nRequestedCount = 0;
110 QObject::tr(
"All Tracks"),
124 QObject::tr(
"Artist"),
138 QObject::tr(
"Album"),
152 QObject::tr(
"Genre"),
211 LOG(VB_UPNP, LOG_INFO,
212 "UPnpCDSMusic::IsBrowseRequestForUs - Not sure... Calling base class.");
256 LOG(VB_UPNP, LOG_INFO,
257 "UPnpCDSMusic::IsSearchRequestForUs.. Don't know, calling base class.");
268 const IDTokenMap& tokens,
const QString& currentToken)
270 if (currentToken.isEmpty())
272 LOG(VB_GENERAL, LOG_ERR, QString(
"UPnpCDSMusic::LoadMetadata: Final "
273 "token missing from id: %1")
279 if (tokens[currentToken].isEmpty())
290 pResults->
Add(container);
295 LOG(VB_GENERAL, LOG_ERR, QString(
"UPnpCDSMusic::LoadMetadata: Requested "
296 "object cannot be found: %1")
300 if (currentToken ==
"genre")
305 return LoadGenres(pRequest, pResults, tokens);
307 if (currentToken ==
"artist")
311 if (currentToken ==
"album")
313 return LoadAlbums(pRequest, pResults, tokens);
315 if (currentToken ==
"track")
317 return LoadTracks(pRequest, pResults, tokens);
320 LOG(VB_GENERAL, LOG_ERR,
321 QString(
"UPnpCDSMusic::LoadMetadata(): "
322 "Unhandled metadata request for '%1'.").arg(currentToken));
332 const IDTokenMap& tokens,
const QString& currentToken)
334 if (currentToken.isEmpty() || currentToken ==
m_sExtensionId.toLower())
341 if (currentToken ==
"track")
343 return LoadTracks(pRequest, pResults, tokens);
345 if (currentToken ==
"genre")
347 if (tokens[
"genre"].toInt() > 0)
349 return LoadGenres(pRequest, pResults, tokens);
351 if (currentToken ==
"artist")
353 if (tokens[
"artist"].toInt() > 0)
354 return LoadAlbums(pRequest, pResults, tokens);
357 if (currentToken ==
"album")
359 if (tokens[
"album"].toInt() > 0)
360 return LoadTracks(pRequest, pResults, tokens);
361 return LoadAlbums(pRequest, pResults, tokens);
363 LOG(VB_GENERAL, LOG_ERR,
364 QString(
"UPnpCDSMusic::LoadChildren(): "
365 "Unhandled metadata request for '%1'.").arg(currentToken));
377 artURI.setPath(
"/Content/GetAlbumArt");
379 artQuery.addQueryItem(
"Id", QString::number(nSongID));
380 artURI.setQuery(artQuery);
382 QList<Property*> propList = pItem->
GetProperties(
"albumArtURI");
383 if (propList.size() >= 4)
399 QUrl mediumURI = artURI;
400 QUrlQuery mediumQuery(mediumURI.query());
401 mediumQuery.addQueryItem(
"Width",
"1024");
402 mediumQuery.addQueryItem(
"Height",
"768");
403 mediumURI.setQuery(mediumQuery);
404 pProp->
SetValue(mediumURI.toEncoded());
406 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
410 pProp = propList.at(1);
415 QUrl thumbURI = artURI;
416 QUrlQuery thumbQuery(thumbURI.query());
417 thumbQuery.addQueryItem(
"Width",
"160");
418 thumbQuery.addQueryItem(
"Height",
"160");
419 thumbURI.setQuery(thumbQuery);
420 pProp->
SetValue(thumbURI.toEncoded());
422 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
426 pProp = propList.at(2);
430 QUrl smallURI = artURI;
431 QUrlQuery smallQuery(smallURI.query());
432 smallQuery.addQueryItem(
"Width",
"640");
433 smallQuery.addQueryItem(
"Height",
"480");
434 smallURI.setQuery(smallQuery);
435 pProp->
SetValue(smallURI.toEncoded());
437 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
441 pProp = propList.at(3);
446 pProp->
SetValue(artURI.toEncoded());
448 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
453 LOG(VB_GENERAL, LOG_ERR, QString(
"Unable to designate album artwork "
454 "for '%1' with class '%2' and id '%3'")
457 QString::number(nSongID)));
478 QString sql =
"SELECT SQL_CALC_FOUND_ROWS "
479 "a.album_id, a.album_name, t.artist_name, a.year, "
480 "a.compilation, s.song_id, g.genre, "
481 "COUNT(a.album_id), w.albumart_id "
482 "FROM music_albums a "
483 "LEFT JOIN music_artists t ON a.artist_id=t.artist_id "
484 "LEFT JOIN music_songs s ON a.album_id=s.album_id "
485 "LEFT JOIN music_genres g ON s.genre_id=g.genre_id "
486 "LEFT JOIN music_albumart w ON s.song_id=w.song_id "
488 "GROUP BY a.album_id "
489 "ORDER BY a.album_name "
490 "LIMIT :OFFSET,:COUNT";
495 query.
prepare(sql.arg(whereString));
507 int nAlbumID = query.
value(0).toInt();
508 QString sAlbumName = query.
value(1).toString();
509 QString sArtist = query.
value(2).toString();
510 QString sYear = query.
value(3).toString();
511 bool bCompilation = query.
value(4).toBool();
512 int nSongId = query.
value(5).toInt();
513 QString sGenre = query.
value(6).toString();
514 int nTrackCount = query.
value(7).toInt();
515 int nAlbumArtID = query.
value(8).toInt();
536 pResults->
Add(pContainer);
542 if (query.
size() > 0)
546 query.
prepare(
"SELECT FOUND_ROWS()");
570 QString sql =
"SELECT SQL_CALC_FOUND_ROWS "
571 "t.artist_id, t.artist_name, CONCAT_WS(',', g.genre), "
572 "COUNT(DISTINCT a.album_id) "
573 "FROM music_artists t "
574 "LEFT JOIN music_albums a ON a.artist_id = t.artist_id "
575 "JOIN music_songs s ON t.artist_id = s.artist_id "
576 "LEFT JOIN music_genres g ON s.genre_id = g.genre_id "
578 "GROUP BY t.artist_id "
579 "ORDER BY t.artist_name "
580 "LIMIT :OFFSET,:COUNT";
586 query.
prepare(sql.arg(whereString));
598 int nArtistId = query.
value(0).toInt();
599 QString sArtistName = query.
value(1).toString();
601 int nAlbumCount = query.
value(3).toInt();
619 pResults->
Add(pContainer);
625 if (query.
size() > 0)
629 query.
prepare(
"SELECT FOUND_ROWS()");
653 QString sql =
"SELECT SQL_CALC_FOUND_ROWS g.genre_id, g.genre, "
654 "COUNT( DISTINCT t.artist_id ) "
655 "FROM music_genres g "
656 "LEFT JOIN music_songs s ON g.genre_id = s.genre_id "
657 "LEFT JOIN music_artists t ON t.artist_id = s.artist_id "
659 "GROUP BY g.genre_id "
661 "LIMIT :OFFSET,:COUNT";
666 query.
prepare(sql.arg(whereString));
678 int nGenreId = query.
value(0).toInt();
679 QString sGenreName = query.
value(1).toString();
680 int nArtistCount = query.
value(2).toInt();
691 pResults->
Add(pContainer);
697 if (query.
size() > 0)
701 query.
prepare(
"SELECT FOUND_ROWS()");
725 QString sql =
"SELECT SQL_CALC_FOUND_ROWS s.song_id, t.artist_name, "
726 "a.album_name, s.name, "
727 "g.genre, s.year, s.track, "
728 "s.description, s.filename, s.length, s.size, "
729 "s.numplays, s.lastplay, w.albumart_id "
730 "FROM music_songs s "
731 "LEFT JOIN music_artists t ON t.artist_id = s.artist_id "
732 "LEFT JOIN music_albums a ON a.album_id = s.album_id "
733 "LEFT JOIN music_genres g ON g.genre_id = s.genre_id "
734 "LEFT JOIN music_albumart w ON s.song_id = w.song_id "
736 "GROUP BY s.song_id "
737 "ORDER BY t.artist_name, a.album_name, s.track "
738 "LIMIT :OFFSET,:COUNT";
743 query.
prepare(sql.arg(whereString));
755 int nId = query.
value( 0).toInt();
756 QString sArtist = query.
value( 1).toString();
757 QString sAlbum = query.
value( 2).toString();
758 QString sTitle = query.
value( 3).toString();
759 QString sGenre = query.
value( 4).toString();
760 int nYear = query.
value( 5).toInt();
761 int nTrackNum = query.
value( 6).toInt();
762 QString sDescription = query.
value( 7).toString();
763 QString sFileName = query.
value( 8).toString();
764 auto nLengthMS = std::chrono::milliseconds(query.
value( 9).toUInt());
765 uint64_t nFileSize = query.
value(10).toULongLong();
767 int nPlaybackCount = query.
value(11).toInt();
768 QDateTime lastPlayedTime = query.
value(12).toDateTime();
769 int nAlbumArtID = query.
value(13).toInt();
781 QString sRefId = QString(
"%1=%2")
795 pItem->
SetPropValue(
"originalTrackNumber" , QString::number(nTrackNum));
796 if (nYear > 0 && nYear < 9999)
799 pItem->
SetPropValue(
"playbackCount" , QString::number(nPlaybackCount));
810 QFileInfo fInfo( sFileName );
814 resURI.setPath(
"/Content/GetMusic");
815 resQuery.addQueryItem(
"Id", QString::number(nId));
816 resURI.setQuery(resQuery);
827 pResource->
AddAttribute(
"size" , QString::number( nFileSize) );
829 pResults->
Add(pItem);
835 if (query.
size() > 0)
839 query.
prepare(
"SELECT FOUND_ROWS()");
853 if (tokens[
"track"].toInt() > 0)
854 clauses.append(
"s.song_id=:TRACK_ID");
855 if (tokens[
"album"].toInt() > 0)
856 clauses.append(
"s.album_id=:ALBUM_ID");
857 if (tokens[
"artist"].toInt() > 0)
858 clauses.append(
"s.artist_id=:ARTIST_ID");
859 if (tokens[
"genre"].toInt() > 0)
860 clauses.append(
"s.genre_id=:GENRE_ID");
861 if (tokens[
"year"].toInt() > 0)
862 clauses.append(
"s.year=:YEAR");
863 if (tokens[
"directory"].toInt() > 0)
864 clauses.append(
"s.directory_id=:DIRECTORY_ID");
872 if (!clauses.isEmpty())
874 whereString =
" WHERE ";
875 whereString.append(clauses.join(
" AND "));
888 if (tokens[
"track"].toInt() > 0)
889 query.
bindValue(
":TRACK_ID", tokens[
"track"]);
890 if (tokens[
"album"].toInt() > 0)
891 query.
bindValue(
":ALBUM_ID", tokens[
"album"]);
892 if (tokens[
"artist"].toInt() > 0)
893 query.
bindValue(
":ARTIST_ID", tokens[
"artist"]);
894 if (tokens[
"genre"].toInt() > 0)
895 query.
bindValue(
":GENRE_ID", tokens[
"genre"]);
896 if (tokens[
"year"].toInt() > 0)
897 query.
bindValue(
":YEAR", tokens[
"year"]);
898 if (tokens[
"directory"].toInt() > 0)
899 query.
bindValue(
":DIRECTORY_ID", tokens[
"directory"]);
static CDSObject * CreateMusicGenre(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
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.
void CreateRoot() override
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
UPnpCDSMusic()
Music Extension for UPnP ContentDirectory Service.
bool IsSearchRequestForUs(UPnpCDSRequest *pRequest) override
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.
QString DateTimeFormat(const QDateTime &dateTime)
Date-Time Format.
int GetBackendStatusPort(void)
Returns the locally defined backend status port.
void PopulateArtworkURIS(CDSObject *pItem, int songID)
static QString CreateIDString(const QString &RequestId, const QString &Name, int Value)
QVariant value(int i) const
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_)
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.
void SetChildContainerCount(uint32_t nCount)
Allows the caller to set childContainerCount without having to load children.
bool LoadChildren(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens, const QString ¤tToken) override
Fetch the children of the container identified in the request.
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.
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 LoadTracks(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
bool IsBrowseRequestForUs(UPnpCDSRequest *pRequest) override
static bool LoadArtists(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
void SetChildCount(uint32_t nCount)
Allows the caller to set childCount without having to load children.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
CDSObject * AddChild(CDSObject *pChild)
static void BindValues(MSqlQuery &query, IDTokenMap tokens)
static CDSObject * CreateMusicTrack(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
static bool LoadGenres(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
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 LoadAlbums(const UPnpCDSRequest *pRequest, UPnpCDSExtensionResults *pResults, const IDTokenMap &tokens)
static CDSObject * CreateContainer(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
static CDSObject * CreateMusicAlbum(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
static QString BuildWhereClause(QStringList clauses, IDTokenMap tokens)
uint32_t GetChildCount(void) const
Return the number of children in this container.
CDSObject * GetChild(const QString &sID)
QString resDurationFormat(std::chrono::milliseconds msec)
res@duration Format B.2.1.4 res@duration - UPnP ContentDirectory Service 2008, 2013
static CDSObject * CreateMusicArtist(const QString &sId, const QString &sTitle, const QString &sParentId, CDSObject *pObject=nullptr)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
virtual bool IsBrowseRequestForUs(UPnpCDSRequest *pRequest)