diff options
author | the-black-eagle <g.moore@gmx.co.uk> | 2023-09-14 19:32:19 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-09-14 19:32:19 +0100 |
commit | 55ef84ad51d59c7cc70f82ca5cdc87c499917edd (patch) | |
tree | 5f49e7de72af0c7b4089f257c20869cafde75d04 | |
parent | fb67e7c258180cd045dbae6ea614ea43beada187 (diff) | |
parent | 5dc4cccc20348f4fd993a303416feeddcc154f3a (diff) |
Merge pull request #22654 from the-black-eagle/music_video_links
[MUSIC] Scrape, store and process yt links from TADB for songs in a users local library
-rw-r--r-- | xbmc/GUIInfoManager.cpp | 10 | ||||
-rw-r--r-- | xbmc/addons/Scraper.cpp | 17 | ||||
-rw-r--r-- | xbmc/guilib/guiinfo/GUIInfoLabels.h | 1 | ||||
-rw-r--r-- | xbmc/guilib/guiinfo/MusicGUIInfo.cpp | 3 | ||||
-rw-r--r-- | xbmc/interfaces/json-rpc/AudioLibrary.cpp | 2 | ||||
-rw-r--r-- | xbmc/interfaces/json-rpc/schema/methods.json | 3 | ||||
-rw-r--r-- | xbmc/interfaces/json-rpc/schema/types.json | 13 | ||||
-rw-r--r-- | xbmc/interfaces/json-rpc/schema/version.txt | 2 | ||||
-rw-r--r-- | xbmc/music/Artist.cpp | 29 | ||||
-rw-r--r-- | xbmc/music/Artist.h | 10 | ||||
-rw-r--r-- | xbmc/music/MusicDatabase.cpp | 150 | ||||
-rw-r--r-- | xbmc/music/MusicDatabase.h | 9 | ||||
-rw-r--r-- | xbmc/music/Song.cpp | 3 | ||||
-rw-r--r-- | xbmc/music/Song.h | 1 | ||||
-rw-r--r-- | xbmc/music/tags/MusicInfoTag.cpp | 15 | ||||
-rw-r--r-- | xbmc/music/tags/MusicInfoTag.h | 3 |
16 files changed, 253 insertions, 18 deletions
diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index ac4e0f7d41..32666e4ebc 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -6857,9 +6857,17 @@ const infomap container_str[] = {{ "property", CONTAINER_PROPERTY }, /// <p><hr> /// @skinning_v20 **[New Infolabel]** \link ListItem_HdrType `ListItem.HdrType`\endlink /// } +/// \table_row3{ <b>`ListItem.SongVideoURL`</b>, +/// \anchor ListItem_SongVideoURL +/// _string_, +/// @return Link to a video of a song +/// <p><hr> +/// @skinning_v21 **[New Infolabel]** \link ListItem_SongVideoURL `ListItem.SongVideoURL`\endlink +/// } /// \table_end /// /// ----------------------------------------------------------------------------- +// clang-format off const infomap listitem_labels[]= {{ "thumb", LISTITEM_THUMB }, { "icon", LISTITEM_ICON }, { "actualicon", LISTITEM_ACTUAL_ICON }, @@ -7069,7 +7077,9 @@ const infomap listitem_labels[]= {{ "thumb", LISTITEM_THUMB }, { "albumstatus", LISTITEM_ALBUMSTATUS }, { "isautoupdateable", LISTITEM_ISAUTOUPDATEABLE }, { "hdrtype", LISTITEM_VIDEO_HDR_TYPE }, + { "songvideourl", LISTITEM_SONG_VIDEO_URL }, }; +// clang-format on /// \page modules__infolabels_boolean_conditions /// \subsection modules__infolabels_boolean_conditions_Visualisation Visualisation diff --git a/xbmc/addons/Scraper.cpp b/xbmc/addons/Scraper.cpp index 30c22473af..ad7b251052 100644 --- a/xbmc/addons/Scraper.cpp +++ b/xbmc/addons/Scraper.cpp @@ -816,6 +816,23 @@ void DetailsFromFileItem<CArtist>(const CFileItem &item, CArtist &artist) artist.discography.emplace_back(discoAlbum); } + const int numvideolinks = item.GetProperty("artist.videolinks").asInteger32(); + if (numvideolinks > 0) + { + artist.videolinks.reserve(numvideolinks); + for (int i = 1; i <= numvideolinks; ++i) + { + std::stringstream prefix; + prefix << "artist.videolink" << i; + ArtistVideoLinks videoLink; + videoLink.title = FromString(item, prefix.str() + ".title"); + videoLink.mbTrackID = FromString(item, prefix.str() + ".mbtrackid"); + videoLink.videoURL = FromString(item, prefix.str() + ".url"); + videoLink.thumbURL = FromString(item, prefix.str() + ".thumb"); + artist.videolinks.emplace_back(std::move(videoLink)); + } + } + int nThumbs = item.GetProperty("artist.thumbs").asInteger32(); ParseThumbs(artist.thumbURL, item, nThumbs, "artist.thumb"); diff --git a/xbmc/guilib/guiinfo/GUIInfoLabels.h b/xbmc/guilib/guiinfo/GUIInfoLabels.h index 5d186bc2ef..dfb5b7f422 100644 --- a/xbmc/guilib/guiinfo/GUIInfoLabels.h +++ b/xbmc/guilib/guiinfo/GUIInfoLabels.h @@ -962,6 +962,7 @@ #define LISTITEM_ALBUMSTATUS (LISTITEM_START + 206) #define LISTITEM_ISAUTOUPDATEABLE (LISTITEM_START + 207) #define LISTITEM_VIDEO_HDR_TYPE (LISTITEM_START + 208) +#define LISTITEM_SONG_VIDEO_URL (LISTITEM_START + 209) #define LISTITEM_END (LISTITEM_START + 2500) diff --git a/xbmc/guilib/guiinfo/MusicGUIInfo.cpp b/xbmc/guilib/guiinfo/MusicGUIInfo.cpp index f58a9ae3c4..ca79c2c8de 100644 --- a/xbmc/guilib/guiinfo/MusicGUIInfo.cpp +++ b/xbmc/guilib/guiinfo/MusicGUIInfo.cpp @@ -403,6 +403,9 @@ bool CMusicGUIInfo::GetLabel(std::string& value, const CFileItem *item, int cont return true; } break; + case LISTITEM_SONG_VIDEO_URL: + value = tag->GetSongVideoURL(); + return true; } } diff --git a/xbmc/interfaces/json-rpc/AudioLibrary.cpp b/xbmc/interfaces/json-rpc/AudioLibrary.cpp index 3521a3a8ae..813c4c85c4 100644 --- a/xbmc/interfaces/json-rpc/AudioLibrary.cpp +++ b/xbmc/interfaces/json-rpc/AudioLibrary.cpp @@ -991,6 +991,8 @@ JSONRPC_STATUS CAudioLibrary::SetSongDetails(const std::string &method, ITranspo song.strOrigReleaseDate = parameterObject["originaldate"].asString(); if (ParameterNotNull(parameterObject, "albumreleasedate")) song.strReleaseDate = parameterObject["albumreleasedate"].asString(); + if (ParameterNotNull(parameterObject, "songvideourl")) + song.songVideoURL = parameterObject["songvideourl"].asString(); // Update existing art. Any existing artwork that isn't specified in this request stays as is. // If the value is null then the existing art with that type is removed. diff --git a/xbmc/interfaces/json-rpc/schema/methods.json b/xbmc/interfaces/json-rpc/schema/methods.json index 4d396cf6ba..c33e23ea31 100644 --- a/xbmc/interfaces/json-rpc/schema/methods.json +++ b/xbmc/interfaces/json-rpc/schema/methods.json @@ -1220,7 +1220,8 @@ { "name": "disctitle", "$ref": "Optional.String" }, { "name": "releasedate", "$ref": "Optional.String" }, { "name": "originaldate", "$ref": "Optional.String" }, - { "name": "bpm", "$ref": "Optional.Integer" } + { "name": "bpm", "$ref": "Optional.Integer" }, + { "name": "songvideourl", "$ref": "Optional.String" } ], "returns": "string" }, diff --git a/xbmc/interfaces/json-rpc/schema/types.json b/xbmc/interfaces/json-rpc/schema/types.json index 86e8b22961..7310ddf55b 100644 --- a/xbmc/interfaces/json-rpc/schema/types.json +++ b/xbmc/interfaces/json-rpc/schema/types.json @@ -545,7 +545,8 @@ "votes", "userrating", "mood", "contributors", "displaycomposer", "displayconductor", "displayorchestra", "displaylyricist", "sortartist", "art", "sourceid", "disctitle", "releasedate", "originaldate", - "bpm", "samplerate", "bitrate", "channels", "datemodified", "datenew" ] + "bpm", "samplerate", "bitrate", "channels", "datemodified", "datenew", + "songvideourl" ] } }, "Audio.Album.ReleaseType": { @@ -692,7 +693,8 @@ "bpm": { "type": "Integer" }, "samplerate": { "type": "Integer" }, "bitrate": { "type": "Integer"}, - "channels": { "type": "Integer"} + "channels": { "type": "Integer"}, + "songvideourl": { "type": "string" } } }, "Audio.Property.Name": { @@ -1572,7 +1574,8 @@ "samplerate": { "type": "integer" }, "channels": { "type": "integer"}, "albumstatus": { "type": "string" }, - "customproperties": { "$ref": "Item.CustomProperties" } + "customproperties": { "$ref": "Item.CustomProperties" }, + "songvideourl": { "type": "string" } } }, "List.Fields.All": { @@ -1597,7 +1600,7 @@ "musicbrainzreleasegroupid", "mediapath", "dynpath", "isboxset", "totaldiscs", "disctitle", "releasedate", "originaldate", "bpm", "bitrate", "samplerate", "channels", "albumstatus", "datemodified", "datenew", "customproperties", - "albumduration"] + "albumduration", "songvideourl"] } }, "List.Item.All": { @@ -1631,7 +1634,7 @@ "specialsortseason", "specialsortepisode", "sortartist", "musicbrainzreleasegroupid", "isboxset", "totaldiscs", "disctitle", "releasedate", "originaldate", "bpm", "bitrate", "samplerate", "channels", "datemodified", "datenew", "customproperties", - "albumduration", "userrating"] + "albumduration", "userrating", "songvideourl" ] } }, "List.Item.File": { diff --git a/xbmc/interfaces/json-rpc/schema/version.txt b/xbmc/interfaces/json-rpc/schema/version.txt index 3f74ab3e4c..998f113d61 100644 --- a/xbmc/interfaces/json-rpc/schema/version.txt +++ b/xbmc/interfaces/json-rpc/schema/version.txt @@ -1 +1 @@ -JSONRPC_VERSION 13.2.1 +JSONRPC_VERSION 13.3.0 diff --git a/xbmc/music/Artist.cpp b/xbmc/music/Artist.cpp index 786f7a737b..c5626c58eb 100644 --- a/xbmc/music/Artist.cpp +++ b/xbmc/music/Artist.cpp @@ -62,6 +62,7 @@ void CArtist::MergeScrapedArtist(const CArtist& source, bool override /* = true art = source.art; discography = source.discography; + videolinks = source.videolinks; } @@ -133,6 +134,24 @@ bool CArtist::Load(const TiXmlElement *artist, bool append, bool prioritise) node = node->NextSiblingElement("album"); } + //song video links + const TiXmlElement* songurls = artist->FirstChildElement("videourl"); + if (songurls) + videolinks.clear(); + while (songurls) + { + if (songurls->FirstChild()) + { + ArtistVideoLinks videoLink; + XMLUtils::GetString(songurls, "title", videoLink.title); + XMLUtils::GetString(songurls, "musicbrainztrackid", videoLink.mbTrackID); + XMLUtils::GetString(songurls, "url", videoLink.videoURL); + XMLUtils::GetString(songurls, "thumburl", videoLink.thumbURL); + videolinks.emplace_back(std::move(videoLink)); + } + songurls = songurls->NextSiblingElement("videourl"); + } + // Support old style <fanart></fanart> for backwards compatibility of old nfo files and scrapers const TiXmlElement *fanart2 = artist->FirstChildElement("fanart"); if (fanart2) @@ -218,6 +237,16 @@ bool CArtist::Save(TiXmlNode *node, const std::string &tag, const std::string& s XMLUtils::SetString(node, "year", it.strYear); XMLUtils::SetString(node, "musicbrainzreleasegroupid", it.strReleaseGroupMBID); } + // song video links + for (const auto& it : videolinks) + { + TiXmlElement videolinkElement("videourl"); + TiXmlNode* node = artist->InsertEndChild(videolinkElement); + XMLUtils::SetString(node, "title", it.title); + XMLUtils::SetString(node, "musicbrainztrackid", it.mbTrackID); + XMLUtils::SetString(node, "url", it.videoURL); + XMLUtils::SetString(node, "thumburl", it.thumbURL); + } return true; } diff --git a/xbmc/music/Artist.h b/xbmc/music/Artist.h index 2ec77a8254..8791288f26 100644 --- a/xbmc/music/Artist.h +++ b/xbmc/music/Artist.h @@ -29,6 +29,14 @@ public: std::string strReleaseGroupMBID; }; +struct ArtistVideoLinks +{ + std::string title; + std::string mbTrackID; + std::string videoURL; + std::string thumbURL; +}; + class CArtist { public: @@ -76,6 +84,7 @@ public: dateNew.Reset(); bScrapedMBID = false; strLastScraped.clear(); + videolinks.clear(); } /*! \brief Load artist information from an XML file. @@ -117,6 +126,7 @@ public: CDateTime dateNew; // Time db record created bool bScrapedMBID = false; std::string strLastScraped; + std::vector<ArtistVideoLinks> videolinks; }; class CArtistCredit diff --git a/xbmc/music/MusicDatabase.cpp b/xbmc/music/MusicDatabase.cpp index 69819a98e4..dd54a6379a 100644 --- a/xbmc/music/MusicDatabase.cpp +++ b/xbmc/music/MusicDatabase.cpp @@ -203,6 +203,7 @@ void CMusicDatabase::CreateTables() " comment text, mood text, iBPM INTEGER NOT NULL DEFAULT 0, " " iBitRate INTEGER NOT NULL DEFAULT 0, " " iSampleRate INTEGER NOT NULL DEFAULT 0, iChannels INTEGER NOT NULL DEFAULT 0, " + " strVideoURL TEXT, " " strReplayGain text, " " dateAdded TEXT, dateNew TEXT, dateModified TEXT)"); CLog::Log(LOGINFO, "create song_artist table"); @@ -452,6 +453,7 @@ void CMusicDatabase::CreateViews() " iBitRate, " " iSampleRate, " " iChannels, " + " song.strVideoURL as strVideoURL, " " album.iAlbumDuration AS iAlbumDuration, " " album.iDiscTotal as iDiscTotal, " " song.dateAdded as dateAdded, " @@ -769,6 +771,7 @@ bool CMusicDatabase::AddAlbum(CAlbum& album, int idSource) song->userrating, // song->votes, // song->iBPM, song->iBitRate, song->iSampleRate, song->iChannels, // + song->songVideoURL, // song->replayGain); // Song must have at least one artist so set artist to [Missing] @@ -1019,6 +1022,7 @@ int CMusicDatabase::AddSong(const int idSong, int iBitRate, int iSampleRate, int iChannels, + const std::string& songVideoURL, const ReplayGain& replayGain) { int idNew = -1; @@ -1146,7 +1150,7 @@ int CMusicDatabase::AddSong(const int idSong, dtLastPlayed, // rating, userrating, votes, // replayGain, // - iBPM, iBitRate, iSampleRate, iChannels); + iBPM, iBitRate, iSampleRate, iChannels, songVideoURL); } if (!strThumb.empty()) SetArtForItem(idNew, MediaTypeSong, "thumb", strThumb); @@ -1238,7 +1242,8 @@ bool CMusicDatabase::UpdateSong(CSong& song, bool bArtists /*= true*/, bool bArt song.lastPlayed, // song.rating, song.userrating, song.votes, // song.replayGain, // - song.iBPM, song.iBitRate, song.iSampleRate, song.iChannels); + song.iBPM, song.iBitRate, song.iSampleRate, song.iChannels, // + song.songVideoURL); if (result < 0) return false; @@ -1297,7 +1302,8 @@ int CMusicDatabase::UpdateSong(int idSong, int iBPM, int iBitRate, int iSampleRate, - int iChannels) + int iChannels, + const std::string& songVideoURL) { if (idSong < 0) return -1; @@ -1319,7 +1325,7 @@ int CMusicDatabase::UpdateSong(int idSong, " strTitle = '%s', iTrack = %i, iDuration = %i, " "strReleaseDate = '%s', strOrigReleaseDate = '%s', strDiscSubtitle = '%s', " "strFileName = '%s', iBPM = %i, iBitrate = %i, iSampleRate = %i, iChannels = %i, " - "dateAdded = '%s'", + "dateAdded = '%s', strVideoURL = '%s'", idPath, artistDisp.c_str(), StringUtils::Join( genres, @@ -1327,7 +1333,7 @@ int CMusicDatabase::UpdateSong(int idSong, .c_str(), strTitle.c_str(), iTrack, iDuration, strRelease.c_str(), strOriginal.c_str(), strDiscSubtitle.c_str(), strFileName.c_str(), iBPM, iBitRate, iSampleRate, iChannels, - strDateMedia.c_str()); + strDateMedia.c_str(), songVideoURL.c_str()); if (strMusicBrainzTrackID.empty()) strSQL += PrepareSQL(", strMusicBrainzTrackID = NULL"); else @@ -1756,6 +1762,11 @@ bool CMusicDatabase::UpdateArtist(const CArtist& artist) AddArtistDiscography(artist.idArtist, disc); } + if (!DeleteArtistVideoLinks(artist.idArtist)) + CLog::Log(LOGERROR, "MusicDatabase: Error deleting ArtistVideoLinks"); + + AddArtistVideoLinks(artist); + // Set current artwork (held in art table) if (!artist.art.empty()) SetArtForItem(artist.idArtist, MediaTypeArtist, artist.art); @@ -2037,6 +2048,8 @@ bool CMusicDatabase::GetArtist(int idArtist, CArtist& artist, bool fetchAll /* = return false; if (nullptr == m_pDS) return false; + if (nullptr == m_pDS2) + return false; if (idArtist == -1) return false; // not in the database @@ -2057,7 +2070,7 @@ bool CMusicDatabase::GetArtist(int idArtist, CArtist& artist, bool fetchAll /* = m_pDS->close(); return false; } - + std::string debugSQL = strSQL + " - "; int discographyOffset = artist_enumCount; artist.discography.clear(); @@ -2077,12 +2090,36 @@ bool CMusicDatabase::GetArtist(int idArtist, CArtist& artist, bool fetchAll /* = } m_pDS->close(); // cleanup recordset data + artist.videolinks.clear(); + if (fetchAll) + { + strSQL = PrepareSQL("SELECT idSong, strTitle, strMusicBrainzTrackID, strVideoURL, url " + "FROM song JOIN album_artist ON song.idAlbum = album_artist.idAlbum " + "LEFT JOIN art ON art.media_id = song.idSong AND art.type = 'videothumb' " + "WHERE album_artist.idArtist = %i AND " + "song.strVideoURL is not NULL GROUP by song.strVideoURL ORDER BY idSong", + idArtist); + debugSQL += strSQL; + m_pDS->query(strSQL); + while (!m_pDS->eof()) + { + const dbiplus::sql_record* const record = m_pDS->get_sql_record(); + ArtistVideoLinks videoLink; + videoLink.title = record->at(1).get_asString(); + videoLink.mbTrackID = record->at(2).get_asString(); + videoLink.videoURL = record->at(3).get_asString(); + videoLink.thumbURL = record->at(4).get_asString(); + + artist.videolinks.emplace_back(std::move(videoLink)); + m_pDS->next(); + } + m_pDS->close(); + } + auto end = std::chrono::steady_clock::now(); auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); - CLog::Log(LOGDEBUG, LOGDATABASE, "{0}({1}) - took {2} ms", __FUNCTION__, strSQL, - duration.count()); - + CLog::LogF(LOGDEBUG, "{} - took {} ms", debugSQL, duration.count()); return true; } catch (...) @@ -2180,6 +2217,92 @@ bool CMusicDatabase::ClearArtistLastScrapedTime(int idArtist) return ExecuteQuery(strSQL); } +bool CMusicDatabase::AddArtistVideoLinks(const CArtist& artist) +{ + auto start = std::chrono::steady_clock::now(); + std::string dbSong; + + try + { + if (nullptr == m_pDB || nullptr == m_pDS || nullptr == m_pDS2) + return false; + + for (const auto& videoURL : artist.videolinks) + { + dbSong = videoURL.title; + std::string strSQL = PrepareSQL( + "SELECT idSong, strTitle FROM song WHERE strMusicBrainzTrackID = '%s' OR (EXISTS " + "(SELECT 1 FROM album_artist WHERE album_artist.idAlbum = song.idAlbum AND " + "album_artist.idArtist = '%i' AND song.strTitle LIKE '%%%s%%'))", + videoURL.mbTrackID.c_str(), artist.idArtist, videoURL.title.c_str()); + + if (!m_pDS->query(strSQL)) + return false; + if (m_pDS->num_rows() == 0) + continue; + + while (!m_pDS->eof()) + { + const int songId = m_pDS->fv(0).get_asInt(); + std::string strSQL2 = PrepareSQL("UPDATE song SET strVideoURL='%s' WHERE idSong = %i", + videoURL.videoURL.c_str(), songId); + CLog::Log(LOGDEBUG, "Adding videolink for song {} with id {}", dbSong.c_str(), songId); + m_pDS2->exec(strSQL2); + + if (!videoURL.thumbURL.empty()) + { // already have a videothumb for this song ? + strSQL2 = PrepareSQL("SELECT art_id FROM art " + "WHERE media_id=%i AND media_type='%s' AND type='videothumb'", + songId, MediaTypeSong); + m_pDS2->query(strSQL2); + if (!m_pDS2->eof()) + { // update existing thumb + const int artId = m_pDS2->fv(0).get_asInt(); + m_pDS2->close(); + strSQL2 = PrepareSQL("UPDATE art SET url='%s' where art_id=%d", + videoURL.thumbURL.c_str(), artId); + m_pDS2->exec(strSQL2); + } + else + { // insert new thumb + m_pDS2->close(); + strSQL2 = PrepareSQL("INSERT INTO art(media_id, media_type, type, url) " + "VALUES (%d, '%s', '%s', '%s')", + songId, MediaTypeSong, "videothumb", videoURL.thumbURL.c_str()); + m_pDS2->exec(strSQL2); + } + m_pDS2->close(); + } + m_pDS->next(); + } + m_pDS->close(); + } + auto end = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); + CLog::LogF(LOGDEBUG, "Time to store videolinks {}ms ", duration.count()); + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "MusicDatabase: Unable to add videolink for song ({})", dbSong.c_str()); + return false; + } +} + +bool CMusicDatabase::DeleteArtistVideoLinks(const int idArtist) +{ + std::string strSQL = PrepareSQL("UPDATE song SET strVideoURL = NULL WHERE idAlbum IN " + "(SELECT idAlbum FROM album_artist WHERE idArtist = %i)", + idArtist); + if (!ExecuteQuery(strSQL)) + return false; + strSQL = PrepareSQL( + "DELETE FROM art WHERE art.type = 'videothumb' AND art.media_id IN (SELECT idSong FROM song " + "JOIN album_artist ON song.idAlbum = album_artist.idAlbum WHERE album_artist.idArtist = %i)", + idArtist); + return ExecuteQuery(strSQL); +} + int CMusicDatabase::AddArtistDiscography(int idArtist, const CDiscoAlbum& discoAlbum) { std::string strSQL = PrepareSQL("INSERT INTO discography " @@ -2994,6 +3117,7 @@ CSong CMusicDatabase::GetSongFromDataset(const dbiplus::sql_record* const record song.iBitRate = record->at(offset + song_iBitRate).get_asInt(); song.iSampleRate = record->at(offset + song_iSampleRate).get_asInt(); song.iChannels = record->at(offset + song_iChannels).get_asInt(); + song.songVideoURL = record->at(offset + song_songVideoURL).get_asString(); return song; } @@ -3056,6 +3180,7 @@ void CMusicDatabase::GetFileItemFromDataset(const dbiplus::sql_record* const rec replaygain.Set(record->at(song_strReplayGain).get_asString()); item->GetMusicInfoTag()->SetReplayGain(replaygain); item->GetMusicInfoTag()->SetTotalDiscs(record->at(song_iDiscTotal).get_asInt()); + item->GetMusicInfoTag()->SetSongVideoURL(record->at(song_songVideoURL).get_asString()); item->GetMusicInfoTag()->SetLoaded(true); // Get filename with full path @@ -7578,6 +7703,7 @@ static const translateJSONField JSONtoDBSong[] = { { "bitrate", "integer", true, "iBitRate", "" }, { "samplerate", "integer", true, "iSampleRate", "" }, { "channels", "integer", true, "iChannels", "" }, + { "songvideourl", "string", true, "strVideoURL", "" }, // JOIN fields (multivalue), same order as _JoinToSongFields { "albumartistid", "array", false, "idAlbumArtist", "album_artist.idArtist AS idAlbumArtist" }, @@ -9288,6 +9414,10 @@ void CMusicDatabase::UpdateTables(int version) m_pDS->exec("DROP TABLE artist"); m_pDS->exec("ALTER TABLE artist_new RENAME TO artist"); } + + if (version < 83) + m_pDS->exec("ALTER TABLE song ADD strVideoURL TEXT"); + // Set the version of tag scanning required. // Not every schema change requires the tags to be rescanned, set to the highest schema version // that needs this. Forced rescanning (of music files that have not changed since they were @@ -9308,7 +9438,7 @@ void CMusicDatabase::UpdateTables(int version) int CMusicDatabase::GetSchemaVersion() const { - return 82; + return 83; } int CMusicDatabase::GetMusicNeedsTagScan() diff --git a/xbmc/music/MusicDatabase.h b/xbmc/music/MusicDatabase.h index 8544eb297d..791ff3d9cd 100644 --- a/xbmc/music/MusicDatabase.h +++ b/xbmc/music/MusicDatabase.h @@ -140,6 +140,7 @@ public: \param rating [in] a rating for the song \param userrating [in] a userrating (my rating) for the song \param votes [in] a vote counter for the song rating + \param songVideoURL [in] url to video of the song \param replayGain [in] album and track replaygain and peak values \return the id of the song */ @@ -171,6 +172,7 @@ public: int iBitRate, int iSampleRate, int iChannels, + const std::string& songVideoURL, const ReplayGain& replayGain); bool GetSong(int idSong, CSong& song); @@ -210,6 +212,7 @@ public: \param iBitRate [in] the bitrate of the song file \param iSampleRate [in] the sample rate of the song file \param iChannels [in] the number of audio channels in the song file + \param songVideoURL [in] url link to a video of the song \return the id of the song */ int UpdateSong(int idSong, @@ -238,7 +241,8 @@ public: int iBPM, int iBitRate, int iSampleRate, - int iChannels); + int iChannels, + const std::string& songVideoURL); //// Misc Song bool GetSongByFileName(const std::string& strFileName, CSong& song, int64_t startOffset = 0); @@ -405,6 +409,8 @@ public: int AddArtistDiscography(int idArtist, const CDiscoAlbum& discoAlbum); bool DeleteArtistDiscography(int idArtist); bool GetArtistDiscography(int idArtist, CFileItemList& items); + bool AddArtistVideoLinks(const CArtist& artist); + bool DeleteArtistVideoLinks(const int idArtist); std::string GetArtistById(int id); int GetArtistByName(const std::string& strArtist); @@ -1017,6 +1023,7 @@ private: song_iBitRate, song_iSampleRate, song_iChannels, + song_songVideoURL, song_iAlbumDuration, song_iDiscTotal, song_dateAdded, diff --git a/xbmc/music/Song.cpp b/xbmc/music/Song.cpp index 165889ca92..93b2156d7b 100644 --- a/xbmc/music/Song.cpp +++ b/xbmc/music/Song.cpp @@ -75,6 +75,7 @@ CSong::CSong(CFileItem& item) iSampleRate = tag.GetSampleRate(); iBitRate = tag.GetBitRate(); iChannels = tag.GetNoOfChannels(); + songVideoURL = tag.GetSongVideoURL(); } CSong::CSong() @@ -239,6 +240,7 @@ void CSong::Serialize(CVariant& value) const value["bitrate"] = iBitRate; value["samplerate"] = iSampleRate; value["channels"] = iChannels; + value["songvideourl"] = songVideoURL; } void CSong::Clear() @@ -279,6 +281,7 @@ void CSong::Clear() iBitRate = 0; iSampleRate = 0; iChannels = 0; + songVideoURL.clear(); replayGain = ReplayGain(); } diff --git a/xbmc/music/Song.h b/xbmc/music/Song.h index 3fc127a60f..737ba46eed 100644 --- a/xbmc/music/Song.h +++ b/xbmc/music/Song.h @@ -194,6 +194,7 @@ public: int iChannels; std::string strRecordLabel; // Record label from tag for album processing by CMusicInfoScanner::FileItemsToAlbums std::string strAlbumType; // (Musicbrainz release type) album type from tag for album processing by CMusicInfoScanner::FileItemsToAlbums + std::string songVideoURL; // url to song video ReplayGain replayGain; private: diff --git a/xbmc/music/tags/MusicInfoTag.cpp b/xbmc/music/tags/MusicInfoTag.cpp index 62548bf5d5..50c7ae773b 100644 --- a/xbmc/music/tags/MusicInfoTag.cpp +++ b/xbmc/music/tags/MusicInfoTag.cpp @@ -323,6 +323,11 @@ const std::string& CMusicInfoTag::GetStationArt() const return m_stationArt; } +const std::string& CMusicInfoTag::GetSongVideoURL() const +{ + return m_songVideoURL; +} + void CMusicInfoTag::SetURL(const std::string& strURL) { m_strURL = strURL; @@ -774,6 +779,11 @@ void CMusicInfoTag::SetStationArt(const std::string& strStationArt) m_stationArt = strStationArt; } +void CMusicInfoTag::SetSongVideoURL(const std::string& songVideoURL) +{ + m_songVideoURL = songVideoURL; +} + void CMusicInfoTag::SetArtist(const CArtist& artist) { SetArtist(artist.strArtist); @@ -882,6 +892,7 @@ void CMusicInfoTag::SetSong(const CSong& song) SetBitRate(song.iBitRate); SetSampleRate(song.iSampleRate); SetNoOfChannels(song.iChannels); + SetSongVideoURL(song.songVideoURL); if (song.replayGain.Get(ReplayGain::TRACK).Valid()) m_replayGain.Set(ReplayGain::TRACK, song.replayGain.Get(ReplayGain::TRACK)); @@ -970,6 +981,7 @@ void CMusicInfoTag::Serialize(CVariant& value) const value["bitrate"] = m_bitrate; value["samplerate"] = m_samplerate; value["channels"] = m_channels; + value["songvideourl"] = m_songVideoURL; } void CMusicInfoTag::ToSortable(SortItem& sortable, Field field) const @@ -1071,6 +1083,7 @@ void CMusicInfoTag::Archive(CArchive& ar) ar << m_samplerate; ar << m_bitrate; ar << m_channels; + ar << m_songVideoURL; } else { @@ -1138,6 +1151,7 @@ void CMusicInfoTag::Archive(CArchive& ar) ar >> m_samplerate; ar >> m_bitrate; ar >> m_channels; + ar >> m_songVideoURL; } } @@ -1193,6 +1207,7 @@ void CMusicInfoTag::Clear() m_channels = 0; m_stationName.clear(); m_stationArt.clear(); + m_songVideoURL.clear(); } void CMusicInfoTag::AppendArtist(const std::string &artist) diff --git a/xbmc/music/tags/MusicInfoTag.h b/xbmc/music/tags/MusicInfoTag.h index 3c1b994425..0b264a9301 100644 --- a/xbmc/music/tags/MusicInfoTag.h +++ b/xbmc/music/tags/MusicInfoTag.h @@ -85,6 +85,7 @@ public: const std::string& GetAlbumReleaseStatus() const; const std::string& GetStationName() const; const std::string& GetStationArt() const; + const std::string& GetSongVideoURL() const; const EmbeddedArtInfo &GetCoverArtInfo() const; const ReplayGain& GetReplayGain() const; CAlbum::ReleaseType GetAlbumReleaseType() const; @@ -157,6 +158,7 @@ public: void SetAlbumReleaseStatus(const std::string& strReleaseStatus); void SetStationName(const std::string& strStationName); // name of online radio station void SetStationArt(const std::string& strStationArt); + void SetSongVideoURL(const std::string& songVideoURL); // link to video of song /*! \brief Append a unique artist to the artist list Checks if we have this artist already added, and if not adds it to the songs artist list. @@ -255,6 +257,7 @@ protected: int m_bitrate; std::string m_stationName; std::string m_stationArt; // Used to fetch thumb URL for Shoutcasts + std::string m_songVideoURL; // link to a video for a song EmbeddedArtInfo m_coverArt; ///< art information |