Permalink
Browse files

Added dateAdded to song table for musiclibrary and ask the user to re…

…fresh musiclibrary

Added the "date added" sorting to some musiclibrary views
Show the date that we sort by in these views
AdvancedSettings for musiclibrary can now handle dateadded like videolibrary already does
Handle the extraction of the file added date in one place (for music database and video database)
  • Loading branch information...
Razzeee committed Jun 23, 2015
1 parent 092818a commit 5b054c2d09014d8e416777d354978cf4302149ce
@@ -251,6 +251,7 @@ CGUIViewStateMusicDatabase::CGUIViewStateMusicDatabase(const CFileItemList& item
AddSortMethod(SortByLabel, sortAttribute, 551, LABEL_MASKS(strTrackLeft, strTrackRight));
AddSortMethod(SortByTime, 180, LABEL_MASKS("%T - %A", "%D")); // Titel, Artist, Duration| empty, empty
AddSortMethod(SortByRating, 563, LABEL_MASKS("%T - %A", "%R")); // Title - Artist, Rating
AddSortMethod(SortByDateAdded, 570, LABEL_MASKS("%A - %T", "%a")); // Title - Artist, DateAdded | empty, empty

const CViewState *viewState = CViewStateSettings::Get().Get("musicnavsongs");
SetSortMethod(viewState->m_sortDescription);
@@ -271,6 +272,7 @@ CGUIViewStateMusicDatabase::CGUIViewStateMusicDatabase(const CFileItemList& item
AddSortMethod(SortByTime, 180, LABEL_MASKS("%T - %A", "%D")); // Titel, Artist, Duration| empty, empty
AddSortMethod(SortByRating, 563, LABEL_MASKS("%T - %A", "%R")); // Title - Artist, Rating
AddSortMethod(SortByYear, 562, LABEL_MASKS("%T - %A", "%Y")); // Title, Artist, Year
AddSortMethod(SortByDateAdded, 570, LABEL_MASKS("%A - %T", "%a")); // Title - Artist, DateAdded | empty, empty

const CViewState *viewState = CViewStateSettings::Get().Get("musicnavsongs");
// the "All Albums" entries always default to SortByAlbum as this is most logical - user can always
@@ -54,6 +54,7 @@
#include "settings/Settings.h"
#include "utils/StringUtils.h"
#include "guilib/LocalizeStrings.h"
#include "utils/FileUtils.h"
#include "utils/LegacyPathTranslation.h"
#include "utils/log.h"
#include "TextureCache.h"
@@ -157,7 +158,7 @@ void CMusicDatabase::CreateTables()
" iTimesPlayed integer, iStartOffset integer, iEndOffset integer, "
" idThumb integer, "
" lastplayed varchar(20) default NULL, "
" rating char default '0', comment text, mood text)");
" rating char default '0', comment text, mood text, dateAdded text)");
CLog::Log(LOGINFO, "create song_artist table");
m_pDS->exec("CREATE TABLE song_artist (idArtist integer, idSong integer, strJoinPhrase text, boolFeatured integer, iOrder integer, strArtist text)");
CLog::Log(LOGINFO, "create song_genre table");
@@ -277,7 +278,8 @@ void CMusicDatabase::CreateViews()
" album.bCompilation AS bCompilation,"
" album.strArtists AS strAlbumArtists,"
" album.strReleaseType AS strAlbumReleaseType,"
" song.mood as mood "
" song.mood as mood,"
" song.dateAdded as dateAdded "
"FROM song"
" JOIN album ON"
" song.idAlbum=album.idAlbum"
@@ -709,6 +711,8 @@ int CMusicDatabase::AddSong(const int idAlbum,
if (bHasKaraoke)
AddKaraokeData(idSong, iKaraokeNumber);

UpdateFileDateAdded(idSong, strPathAndFileName);

AnnounceUpdate(MediaTypeSong, idSong);
}
catch (...)
@@ -827,6 +831,9 @@ int CMusicDatabase::UpdateSong(int idSong,
strSQL += PrepareSQL(" WHERE idSong = %i", idSong);

bool status = ExecuteQuery(strSQL);

UpdateFileDateAdded(idSong, strPathAndFileName);

if (status)
AnnounceUpdate(MediaTypeSong, idSong);
return idSong;
@@ -1668,6 +1675,7 @@ CSong CMusicDatabase::GetSongFromDataset(const dbiplus::sql_record* const record
song.strTitle = record->at(offset + song_strTitle).get_asString();
song.iTimesPlayed = record->at(offset + song_iTimesPlayed).get_asInt();
song.lastPlayed.SetFromDBDateTime(record->at(offset + song_lastplayed).get_asString());
song.dateAdded.SetFromDBDateTime(record->at(offset + song_dateAdded).get_asString());
song.iStartOffset = record->at(offset + song_iStartOffset).get_asInt();
song.iEndOffset = record->at(offset + song_iEndOffset).get_asInt();
song.strMusicBrainzTrackID = record->at(offset + song_strMusicBrainzTrackID).get_asString();
@@ -1716,6 +1724,7 @@ void CMusicDatabase::GetFileItemFromDataset(const dbiplus::sql_record* const rec
item->GetMusicInfoTag()->SetMood(record->at(song_mood).get_asString());
item->GetMusicInfoTag()->SetPlayCount(record->at(song_iTimesPlayed).get_asInt());
item->GetMusicInfoTag()->SetLastPlayed(record->at(song_lastplayed).get_asString());
item->GetMusicInfoTag()->SetDateAdded(record->at(song_dateAdded).get_asString());
std::string strRealPath = URIUtils::AddFileToFolder(record->at(song_strPath).get_asString(), record->at(song_strFileName).get_asString());
item->GetMusicInfoTag()->SetURL(strRealPath);
item->GetMusicInfoTag()->SetCompilation(record->at(song_bCompilation).get_asInt() == 1);
@@ -3690,6 +3699,7 @@ bool CMusicDatabase::GetSongsNav(const std::string& strBaseDir, CFileItemList& i

void CMusicDatabase::UpdateTables(int version)
{
CLog::Log(LOGINFO, "%s - updating tables", __FUNCTION__);
if (version < 34)
{
m_pDS->exec("ALTER TABLE artist ADD strMusicBrainzArtistID text\n");
@@ -3843,11 +3853,17 @@ void CMusicDatabase::UpdateTables(int version)
{
m_pDS->exec("ALTER TABLE song ADD mood text\n");
}
if (version < 53)
{
m_pDS->exec("ALTER TABLE song ADD dateAdded text");
CMediaSettings::Get().SetMusicNeedsUpdate(53);
CSettings::Get().Save();
}
}

int CMusicDatabase::GetSchemaVersion() const
{
return 52;
return 53;
}

unsigned int CMusicDatabase::GetSongIDs(const Filter &filter, vector<pair<int,int> > &songIDs)
@@ -5651,3 +5667,32 @@ bool CMusicDatabase::GetFilter(CDbUrl &musicUrl, Filter &filter, SortDescription

return true;
}

void CMusicDatabase::UpdateFileDateAdded(int songId, const std::string& strFileNameAndPath)
{
if (songId < 0 || strFileNameAndPath.empty())
return;

CDateTime dateAdded;
try
{
if (NULL == m_pDB.get()) return;
if (NULL == m_pDS.get()) return;

// 1 prefering to use the files mtime(if it's valid) and only using the file's ctime if the mtime isn't valid
if (g_advancedSettings.m_iMusicLibraryDateAdded == 1)
dateAdded = CFileUtils::GetModificationDate(strFileNameAndPath, false);
//2 using the newer datetime of the file's mtime and ctime
else if (g_advancedSettings.m_iMusicLibraryDateAdded == 2)
dateAdded = CFileUtils::GetModificationDate(strFileNameAndPath, true);
//0 using the current datetime if non of the above matches or one returns an invalid datetime
if (!dateAdded.IsValid())
dateAdded = CDateTime::GetCurrentDateTime();

m_pDS->exec(PrepareSQL("UPDATE song SET dateAdded='%s' WHERE idSong=%d", dateAdded.GetAsDBDateTime().c_str(), songId));
}
catch (...)
{
CLog::Log(LOGERROR, "%s unable to update dateadded for song (%s) with date (%s)", __FUNCTION__, songId, dateAdded.GetAsDBDateTime().c_str());

This comment has been minimized.

@xhaggi

xhaggi Jul 14, 2015

Member
format specifies type 'char *' but the argument has type 'int'.

please change it to %i

}
}
@@ -497,6 +497,12 @@ class CMusicDatabase : public CDatabase
CAlbum GetAlbumFromDataset(dbiplus::Dataset* pDS, int offset = 0, bool imageURL = false);
CAlbum GetAlbumFromDataset(const dbiplus::sql_record* const record, int offset = 0, bool imageURL = false);
CArtistCredit GetArtistCreditFromDataset(const dbiplus::sql_record* const record, int offset = 0);
/*! \brief Updates the dateAdded field in the song table for the file
with the given songId and the given path based on the files modification date
\param songId id of the song in the song table
\param strFileNameAndPath path to the file
*/
void UpdateFileDateAdded(int songId, const std::string& strFileNameAndPath);
void GetFileItemFromDataset(CFileItem* item, const CMusicDbUrl &baseUrl);
void GetFileItemFromDataset(const dbiplus::sql_record* const record, CFileItem* item, const CMusicDbUrl &baseUrl);
CSong GetAlbumInfoSongFromDataset(const dbiplus::sql_record* const record, int offset = 0);
@@ -541,6 +547,7 @@ class CMusicDatabase : public CDatabase
song_strAlbumArtists,
song_strAlbumReleaseType,
song_mood,
song_dateAdded,
song_enumCount // end of the enum, do not add past here
} SongFields;

@@ -76,6 +76,7 @@ CSong::CSong(CFileItem& item)
bCompilation = tag.GetCompilation();
embeddedArt = tag.GetCoverArtInfo();
strFileName = tag.GetURL().empty() ? item.GetPath() : tag.GetURL();
dateAdded = tag.GetDateAdded();
strThumb = item.GetUserMusicThumb(true);
iStartOffset = item.m_lStartOffset;
iEndOffset = item.m_lEndOffset;
@@ -121,6 +122,7 @@ void CSong::Serialize(CVariant& value) const
value["rating"] = rating;
value["timesplayed"] = iTimesPlayed;
value["lastplayed"] = lastPlayed.IsValid() ? lastPlayed.GetAsDBDateTime() : "";
value["dateadded"] = dateAdded.IsValid() ? dateAdded.GetAsDBDateTime() : "";
value["karaokenumber"] = (int64_t) iKaraokeNumber;
value["albumid"] = idAlbum;
}
@@ -146,6 +148,7 @@ void CSong::Clear()
idSong = -1;
iTimesPlayed = 0;
lastPlayed.Reset();
dateAdded.Reset();
iKaraokeNumber = 0;
strKaraokeLyrEncoding.clear();
iKaraokeDelay = 0;
@@ -101,6 +101,7 @@ class CSong: public ISerializable
int iYear;
int iTimesPlayed;
CDateTime lastPlayed;
CDateTime dateAdded;
int iStartOffset;
int iEndOffset;
bool bCompilation;
@@ -115,6 +115,7 @@ const CMusicInfoTag& CMusicInfoTag::operator =(const CMusicInfoTag& tag)
m_strLyrics = tag.m_strLyrics;
m_cuesheet = tag.m_cuesheet;
m_lastPlayed = tag.m_lastPlayed;
m_dateAdded = tag.m_dateAdded;
m_bCompilation = tag.m_bCompilation;
m_iDuration = tag.m_iDuration;
m_iTrack = tag.m_iTrack;
@@ -268,6 +269,11 @@ const CDateTime &CMusicInfoTag::GetLastPlayed() const
return m_lastPlayed;
}

const CDateTime &CMusicInfoTag::GetDateAdded() const
{
return m_dateAdded;
}

bool CMusicInfoTag::GetCompilation() const
{
return m_bCompilation;
@@ -429,6 +435,16 @@ void CMusicInfoTag::SetLastPlayed(const CDateTime& lastplayed)
m_lastPlayed = lastplayed;
}

void CMusicInfoTag::SetDateAdded(const std::string& dateAdded)
{
m_dateAdded.SetFromDBDateTime(dateAdded);
}

void CMusicInfoTag::SetDateAdded(const CDateTime& dateAdded)
{
m_dateAdded = dateAdded;
}

void CMusicInfoTag::SetCompilation(bool compilation)
{
m_bCompilation = compilation;
@@ -551,6 +567,7 @@ void CMusicInfoTag::SetSong(const CSong& song)
SetCueSheet(song.strCueSheet);
SetPlayCount(song.iTimesPlayed);
SetLastPlayed(song.lastPlayed);
SetDateAdded(song.dateAdded);
SetCoverArtInfo(song.embeddedArt.size, song.embeddedArt.mime);
m_rating = song.rating;
m_strURL = song.strFileName;
@@ -598,6 +615,7 @@ void CMusicInfoTag::Serialize(CVariant& value) const
value["rating"] = (int)(m_rating - '0');
value["playcount"] = m_iTimesPlayed;
value["lastplayed"] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::Empty;
value["dateadded"] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::Empty;
value["lyrics"] = m_strLyrics;
value["albumid"] = m_iAlbumId;
value["compilationartist"] = m_bCompilation;
@@ -632,6 +650,7 @@ void CMusicInfoTag::ToSortable(SortItem& sortable, Field field) const
case FieldRating: sortable[FieldRating] = (float)(m_rating - '0'); break;
case FieldPlaycount: sortable[FieldPlaycount] = m_iTimesPlayed; break;
case FieldLastPlayed: sortable[FieldLastPlayed] = m_lastPlayed.IsValid() ? m_lastPlayed.GetAsDBDateTime() : StringUtils::Empty; break;
case FieldDateAdded: sortable[FieldDateAdded] = m_dateAdded.IsValid() ? m_dateAdded.GetAsDBDateTime() : StringUtils::Empty; break;
case FieldListeners: sortable[FieldListeners] = m_listeners; break;
case FieldId: sortable[FieldId] = (int64_t)m_iDbId; break;
default: break;
@@ -658,6 +677,7 @@ void CMusicInfoTag::Archive(CArchive& ar)
ar << m_musicBrainzAlbumArtistID;
ar << m_strMusicBrainzTRMID;
ar << m_lastPlayed;
ar << m_dateAdded;
ar << m_strComment;
ar << m_strMood;
ar << m_rating;
@@ -690,6 +710,7 @@ void CMusicInfoTag::Archive(CArchive& ar)
ar >> m_musicBrainzAlbumArtistID;
ar >> m_strMusicBrainzTRMID;
ar >> m_lastPlayed;
ar >> m_dateAdded;
ar >> m_strComment;
ar >> m_strMood;
ar >> m_rating;
@@ -726,6 +747,7 @@ void CMusicInfoTag::Clear()
m_iTrack = 0;
m_bLoaded = false;
m_lastPlayed.Reset();
m_dateAdded.Reset();
m_bCompilation = false;
m_strComment.clear();
m_strMood.clear();
@@ -73,6 +73,7 @@ class CMusicInfoTag : public IArchivable, public ISerializable, public ISortable
const std::string& GetLyrics() const;
const std::string& GetCueSheet() const;
const CDateTime& GetLastPlayed() const;
const CDateTime& GetDateAdded() const;
bool GetCompilation() const;
char GetRating() const;
int GetListeners() const;
@@ -116,6 +117,8 @@ class CMusicInfoTag : public IArchivable, public ISerializable, public ISortable
void SetPlayCount(int playcount);
void SetLastPlayed(const std::string& strLastPlayed);
void SetLastPlayed(const CDateTime& strLastPlayed);
void SetDateAdded(const std::string& strLastPlayed);
void SetDateAdded(const CDateTime& strLastPlayed);
void SetCompilation(bool compilation);
void SetCoverArtInfo(size_t size, const std::string &mimeType);
void SetReplayGain(const ReplayGain& aGain);
@@ -167,6 +170,7 @@ class CMusicInfoTag : public IArchivable, public ISerializable, public ISortable
std::string m_strLyrics;
std::string m_cuesheet;
CDateTime m_lastPlayed;
CDateTime m_dateAdded;
bool m_bCompilation;
int m_iDuration;
int m_iTrack; // consists of the disk number in the high 16 bits, the track number in the low 16bits
@@ -1180,7 +1180,7 @@ bool CGUIWindowMusicBase::CanContainFilter(const std::string &strDirectory) cons
void CGUIWindowMusicBase::OnInitWindow()
{
CGUIMediaWindow::OnInitWindow();
if (CMediaSettings::Get().GetMusicNeedsUpdate() == 35)
if (CMediaSettings::Get().GetMusicNeedsUpdate() == 53)
{
if (g_infoManager.GetLibraryBool(LIBRARY_HAS_MUSIC) && !g_application.IsMusicScanning())
{
@@ -274,6 +274,7 @@ void CAdvancedSettings::Initialize()
m_prioritiseAPEv2tags = false;
m_musicItemSeparator = " / ";
m_videoItemSeparator = " / ";
m_iMusicLibraryDateAdded = 1; // prefer mtime over ctime and current time

m_bVideoLibraryAllItemsOnBottom = false;
m_iVideoLibraryRecentlyAddedItems = 25;
@@ -736,6 +737,7 @@ void CAdvancedSettings::ParseSettingsFile(const std::string &file)
XMLUtils::GetString(pElement, "albumformat", m_strMusicLibraryAlbumFormat);
XMLUtils::GetString(pElement, "albumformatright", m_strMusicLibraryAlbumFormatRight);
XMLUtils::GetString(pElement, "itemseparator", m_musicItemSeparator);
XMLUtils::GetInt(pElement, "dateadded", m_iMusicLibraryDateAdded);
}

pElement = pRootElement->FirstChildElement("videolibrary");
@@ -265,6 +265,7 @@ class CAdvancedSettings : public ISettingCallback, public ISettingsHandler
std::string m_fanartImages;

int m_iMusicLibraryRecentlyAddedItems;
int m_iMusicLibraryDateAdded;
bool m_bMusicLibraryAllItemsOnBottom;
bool m_bMusicLibraryAlbumsSortByArtistThenYear;
bool m_bMusicLibraryCleanOnUpdate;
@@ -96,7 +96,7 @@ std::string DatabaseUtils::GetField(Field field, const MediaType &mediaType, Dat
else if (field == FieldPath) return "songview.strPath";
else if (field == FieldArtist || field == FieldAlbumArtist) return "songview.strArtists";
else if (field == FieldGenre) return "songview.strGenre";
else if (field == FieldDateAdded && queryPart == DatabaseQueryPartOrderBy) return "songview.idSong"; // only used for order clauses
else if (field == FieldDateAdded && queryPart == DatabaseQueryPartOrderBy) return "songview.dateAdded";
}
else if (mediaType == MediaTypeArtist)
{
Oops, something went wrong.

15 comments on commit 5b054c2

@Memphiz

This comment has been minimized.

Copy link
Member

Memphiz replied Jul 8, 2015

This one broke unit test:

TestDatabaseUtils.GetField_MediaTypeSong

TestDatabaseUtils.cpp:292
Value of: varstr.c_str()
Actual: "songview.dateAdded"
Expected: refstr.c_str()
Which is: "songview.idSong"

@mkortstiege fyi ping

@akva2

This comment has been minimized.

Copy link
Contributor

akva2 replied Jul 27, 2015

at least somebody noticed. the fact that you don't notice up front is extremely troubling.

@Razzeee

This comment has been minimized.

Copy link
Member

Razzeee replied Jul 27, 2015

Well using the build in visual studio test suit just returns a blank window for me.
And there is no documentation that I can find in the kodi docs about what kind of unit testing/framework is being used.

@notspiff

This comment has been minimized.

Copy link
Contributor

notspiff replied Jul 27, 2015

Wasnt really aimed at you but rather still not having tests run on jenkins after years.

It is gtest based.

@notspiff

This comment has been minimized.

Copy link
Contributor

notspiff replied Jul 27, 2015

Argh same bastard different logins;)

@notspiff

This comment has been minimized.

Copy link
Contributor

notspiff replied Jul 27, 2015

All fine and dandy if people acted on it. But this has been left for over a month - it is time to force the issue and run on the normal trigger.

@Memphiz

This comment has been minimized.

Copy link
Member

Memphiz replied Jul 27, 2015

@mkortstiege another ping - are you around atm?

@mkortstiege

This comment has been minimized.

Copy link
Member

mkortstiege replied Jul 27, 2015

@Memphiz, busy with other things right now.

@Montellese

This comment has been minimized.

Copy link
Member

Montellese replied Jul 27, 2015

@Razzeee

This comment has been minimized.

Copy link
Member

Razzeee replied Jul 27, 2015

run-tests.bat is just crashing on me do I need to run it from a specific folder?

'getbranch.bat' is not recognized as an internal or external command,
operable program or batch file.
Building Debug Testsuite
Compiling testsuite...
Microsoft (R) Build Engine version 12.0.31101.0
[Microsoft .NET Framework, version 4.0.30319.34209]
Copyright (C) Microsoft Corporation. All rights reserved.

MSBUILD : error MSB1009: Project file does not exist.
Switch: ..\VS2010Express\XBMC for Windows.sln
The system cannot find the path specified.
------------------------------------------------------------
---------------
   ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR ERROR
---------------
ERROR: "-test.EXE failed to build\Projekte\xbmc\project\..\vs2010express\XBMC\Debug Testsuite\objs\XBMC.log"
------------------------------------------------------------

Edit: Ignore that, seems like I need to be in ..\xbmc\project\Win32BuildSetup

@Montellese

This comment has been minimized.

Copy link
Member

Montellese replied Jul 27, 2015

I "always" run it from VS and put a breakpoint at the end of the test's main() function to see the output. There's some VS2013 extension for GoogleTest but I never got that working properly.

@Razzeee

This comment has been minimized.

Copy link
Member

Razzeee replied Jul 27, 2015

@Montellese
how do you run it from VS?

@Montellese

This comment has been minimized.

Copy link
Member

Montellese replied Jul 27, 2015

I build and run the Debug Testsuite configuration. But the cmd window disappears immediately after the last test has finished so I add a breakpoint to the end of main() in xbmc/test/xbmc-test.cpp.

@Razzeee

This comment has been minimized.

Copy link
Member

Razzeee replied Jul 28, 2015

@Montellese
Thanks, thats working. We should document that in the wiki.

Please sign in to comment.