Go to the documentation of this file.
65 "object.item.audioItem.musicTrack" )
100 pRequest->m_nRequestedCount = 0;
109 QObject::tr(
"All Tracks"),
123 QObject::tr(
"Artist"),
137 QObject::tr(
"Album"),
151 QObject::tr(
"Genre"),
210 LOG(VB_UPNP, LOG_INFO,
211 "UPnpCDSMusic::IsBrowseRequestForUs - Not sure... Calling base class.");
255 LOG(VB_UPNP, LOG_INFO,
256 "UPnpCDSMusic::IsSearchRequestForUs.. Don't know, calling base class.");
267 const IDTokenMap& tokens,
const QString& currentToken)
269 if (currentToken.isEmpty())
271 LOG(VB_GENERAL, LOG_ERR, QString(
"UPnpCDSMusic::LoadMetadata: Final "
272 "token missing from id: %1")
278 if (tokens[currentToken].isEmpty())
289 pResults->
Add(container);
294 LOG(VB_GENERAL, LOG_ERR, QString(
"UPnpCDSMusic::LoadMetadata: Requested "
295 "object cannot be found: %1")
299 if (currentToken ==
"genre")
304 return LoadGenres(pRequest, pResults, tokens);
306 if (currentToken ==
"artist")
310 if (currentToken ==
"album")
312 return LoadAlbums(pRequest, pResults, tokens);
314 if (currentToken ==
"track")
316 return LoadTracks(pRequest, pResults, tokens);
319 LOG(VB_GENERAL, LOG_ERR,
320 QString(
"UPnpCDSMusic::LoadMetadata(): "
321 "Unhandled metadata request for '%1'.").arg(currentToken));
331 const IDTokenMap& tokens,
const QString& currentToken)
333 if (currentToken.isEmpty() || currentToken ==
m_sExtensionId.toLower())
340 if (currentToken ==
"track")
342 return LoadTracks(pRequest, pResults, tokens);
344 if (currentToken ==
"genre")
346 if (tokens[
"genre"].toInt() > 0)
348 return LoadGenres(pRequest, pResults, tokens);
350 if (currentToken ==
"artist")
352 if (tokens[
"artist"].toInt() > 0)
353 return LoadAlbums(pRequest, pResults, tokens);
356 if (currentToken ==
"album")
358 if (tokens[
"album"].toInt() > 0)
359 return LoadTracks(pRequest, pResults, tokens);
360 return LoadAlbums(pRequest, pResults, tokens);
362 LOG(VB_GENERAL, LOG_ERR,
363 QString(
"UPnpCDSMusic::LoadChildren(): "
364 "Unhandled metadata request for '%1'.").arg(currentToken));
376 artURI.setPath(
"/Content/GetAlbumArt");
378 artQuery.addQueryItem(
"Id", QString::number(nSongID));
379 artURI.setQuery(artQuery);
381 QList<Property*> propList = pItem->
GetProperties(
"albumArtURI");
382 if (propList.size() >= 4)
398 QUrl mediumURI = artURI;
399 QUrlQuery mediumQuery(mediumURI.query());
400 mediumQuery.addQueryItem(
"Width",
"1024");
401 mediumQuery.addQueryItem(
"Height",
"768");
402 mediumURI.setQuery(mediumQuery);
403 pProp->
SetValue(mediumURI.toEncoded());
405 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
409 pProp = propList.at(1);
414 QUrl thumbURI = artURI;
415 QUrlQuery thumbQuery(thumbURI.query());
416 thumbQuery.addQueryItem(
"Width",
"160");
417 thumbQuery.addQueryItem(
"Height",
"160");
418 thumbURI.setQuery(thumbQuery);
419 pProp->
SetValue(thumbURI.toEncoded());
421 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
425 pProp = propList.at(2);
429 QUrl smallURI = artURI;
430 QUrlQuery smallQuery(smallURI.query());
431 smallQuery.addQueryItem(
"Width",
"640");
432 smallQuery.addQueryItem(
"Height",
"480");
433 smallURI.setQuery(smallQuery);
434 pProp->
SetValue(smallURI.toEncoded());
436 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
440 pProp = propList.at(3);
445 pProp->
SetValue(artURI.toEncoded());
447 pProp->
AddAttribute(
"xmlns:dlna",
"urn:schemas-dlna-org:metadata-1-0");
452 LOG(VB_GENERAL, LOG_ERR, QString(
"Unable to designate album artwork "
453 "for '%1' with class '%2' and id '%3'")
456 QString::number(nSongID)));
477 QString sql =
"SELECT SQL_CALC_FOUND_ROWS "
478 "a.album_id, a.album_name, t.artist_name, a.year, "
479 "a.compilation, s.song_id, g.genre, "
480 "COUNT(a.album_id), w.albumart_id "
481 "FROM music_albums a "
482 "LEFT JOIN music_artists t ON a.artist_id=t.artist_id "
483 "LEFT JOIN music_songs s ON a.album_id=s.album_id "
484 "LEFT JOIN music_genres g ON s.genre_id=g.genre_id "
485 "LEFT JOIN music_albumart w ON s.song_id=w.song_id "
487 "GROUP BY a.album_id "
488 "ORDER BY a.album_name "
489 "LIMIT :OFFSET,:COUNT";
494 query.
prepare(sql.arg(whereString));
506 int nAlbumID = query.
value(0).toInt();
507 QString sAlbumName = query.
value(1).toString();
508 QString sArtist = query.
value(2).toString();
509 QString sYear = query.
value(3).toString();
510 bool bCompilation = query.
value(4).toBool();
511 int nSongId = query.
value(5).toInt();
512 QString sGenre = query.
value(6).toString();
513 int nTrackCount = query.
value(7).toInt();
514 int nAlbumArtID = query.
value(8).toInt();
535 pResults->
Add(pContainer);
541 if (query.
size() > 0)
545 query.
prepare(
"SELECT FOUND_ROWS()");
569 QString sql =
"SELECT SQL_CALC_FOUND_ROWS "
570 "t.artist_id, t.artist_name, CONCAT_WS(',', g.genre), "
571 "COUNT(DISTINCT a.album_id) "
572 "FROM music_artists t "
573 "LEFT JOIN music_albums a ON a.artist_id = t.artist_id "
574 "JOIN music_songs s ON t.artist_id = s.artist_id "
575 "LEFT JOIN music_genres g ON s.genre_id = g.genre_id "
577 "GROUP BY t.artist_id "
578 "ORDER BY t.artist_name "
579 "LIMIT :OFFSET,:COUNT";
585 query.
prepare(sql.arg(whereString));
597 int nArtistId = query.
value(0).toInt();
598 QString sArtistName = query.
value(1).toString();
600 int nAlbumCount = query.
value(3).toInt();
618 pResults->
Add(pContainer);
624 if (query.
size() > 0)
628 query.
prepare(
"SELECT FOUND_ROWS()");
652 QString sql =
"SELECT SQL_CALC_FOUND_ROWS g.genre_id, g.genre, "
653 "COUNT( DISTINCT t.artist_id ) "
654 "FROM music_genres g "
655 "LEFT JOIN music_songs s ON g.genre_id = s.genre_id "
656 "LEFT JOIN music_artists t ON t.artist_id = s.artist_id "
658 "GROUP BY g.genre_id "
660 "LIMIT :OFFSET,:COUNT";
665 query.
prepare(sql.arg(whereString));
677 int nGenreId = query.
value(0).toInt();
678 QString sGenreName = query.
value(1).toString();
679 int nArtistCount = query.
value(2).toInt();
690 pResults->
Add(pContainer);
696 if (query.
size() > 0)
700 query.
prepare(
"SELECT FOUND_ROWS()");
724 QString sql =
"SELECT SQL_CALC_FOUND_ROWS s.song_id, t.artist_name, "
725 "a.album_name, s.name, "
726 "g.genre, s.year, s.track, "
727 "s.description, s.filename, s.length, s.size, "
728 "s.numplays, s.lastplay, w.albumart_id "
729 "FROM music_songs s "
730 "LEFT JOIN music_artists t ON t.artist_id = s.artist_id "
731 "LEFT JOIN music_albums a ON a.album_id = s.album_id "
732 "LEFT JOIN music_genres g ON g.genre_id = s.genre_id "
733 "LEFT JOIN music_albumart w ON s.song_id = w.song_id "
735 "GROUP BY s.song_id "
736 "ORDER BY t.artist_name, a.album_name, s.track "
737 "LIMIT :OFFSET,:COUNT";
742 query.
prepare(sql.arg(whereString));
754 int nId = query.
value( 0).toInt();
755 QString sArtist = query.
value( 1).toString();
756 QString sAlbum = query.
value( 2).toString();
757 QString sTitle = query.
value( 3).toString();
758 QString sGenre = query.
value( 4).toString();
759 int nYear = query.
value( 5).toInt();
760 int nTrackNum = query.
value( 6).toInt();
761 QString sDescription = query.
value( 7).toString();
762 QString sFileName = query.
value( 8).toString();
763 auto nLengthMS = std::chrono::milliseconds(query.
value( 9).toUInt());
764 uint64_t nFileSize = query.
value(10).toULongLong();
766 int nPlaybackCount = query.
value(11).toInt();
767 QDateTime lastPlayedTime = query.
value(12).toDateTime();
768 int nAlbumArtID = query.
value(13).toInt();
780 QString sRefId = QString(
"%1=%2")
794 pItem->
SetPropValue(
"originalTrackNumber" , QString::number(nTrackNum));
795 if (nYear > 0 && nYear < 9999)
798 pItem->
SetPropValue(
"playbackCount" , QString::number(nPlaybackCount));
809 QFileInfo fInfo( sFileName );
813 resURI.setPath(
"/Content/GetMusic");
814 resQuery.addQueryItem(
"Id", QString::number(nId));
815 resURI.setQuery(resQuery);
826 pResource->
AddAttribute(
"size" , QString::number( nFileSize) );
828 pResults->
Add(pItem);
834 if (query.
size() > 0)
838 query.
prepare(
"SELECT FOUND_ROWS()");
852 if (tokens[
"track"].toInt() > 0)
853 clauses.append(
"s.song_id=:TRACK_ID");
854 if (tokens[
"album"].toInt() > 0)
855 clauses.append(
"s.album_id=:ALBUM_ID");
856 if (tokens[
"artist"].toInt() > 0)
857 clauses.append(
"s.artist_id=:ARTIST_ID");
858 if (tokens[
"genre"].toInt() > 0)
859 clauses.append(
"s.genre_id=:GENRE_ID");
860 if (tokens[
"year"].toInt() > 0)
861 clauses.append(
"s.year=:YEAR");
862 if (tokens[
"directory"].toInt() > 0)
863 clauses.append(
"s.directory_id=:DIRECTORY_ID");
871 if (!clauses.isEmpty())
873 whereString =
" WHERE ";
874 whereString.append(clauses.join(
" AND "));
887 if (tokens[
"track"].toInt() > 0)
888 query.
bindValue(
":TRACK_ID", tokens[
"track"]);
889 if (tokens[
"album"].toInt() > 0)
890 query.
bindValue(
":ALBUM_ID", tokens[
"album"]);
891 if (tokens[
"artist"].toInt() > 0)
892 query.
bindValue(
":ARTIST_ID", tokens[
"artist"]);
893 if (tokens[
"genre"].toInt() > 0)
894 query.
bindValue(
":GENRE_ID", tokens[
"genre"]);
895 if (tokens[
"year"].toInt() > 0)
896 query.
bindValue(
":YEAR", tokens[
"year"]);
897 if (tokens[
"directory"].toInt() > 0)
898 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)