Handle the original date of the songs
This commit is contained in:
@@ -43,11 +43,9 @@
|
||||
- Search patterns in the genre list
|
||||
- ReleaseView/ArtistView/GenreView: <All> -> Track count for this special category. Easier: add a special entry '<All>' that contains everything?
|
||||
- MediaPlayer: move slider using js (http://redmine.webtoolkit.eu/boards/2/topics/7924?r=8478)
|
||||
- TrackView : handle original release date
|
||||
- TrackView : handle duration > 1 hour
|
||||
- TrackView : select only relevant columns to speed up queries (do not fetch eveything)
|
||||
- TrackView : Reselect the current selected item when displaying the updated search results
|
||||
- ReleaseView: display the original release's publication year
|
||||
- OGG metadata -> properly handle metadata nested in the audio stream
|
||||
- Filters: use directly the id as constraint, instead of the name?
|
||||
- Implement a playlist:
|
||||
|
||||
@@ -283,8 +283,8 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
// We estimate this is a audio file if:
|
||||
// - we found a least one audio stream
|
||||
// - the duration is not null
|
||||
if (items.find(MetaData::AudioStreams) == items.end()
|
||||
|| boost::any_cast<std::vector<MetaData::AudioStream> >(items[MetaData::AudioStreams]).empty())
|
||||
if (items.find(MetaData::Type::AudioStreams) == items.end()
|
||||
|| boost::any_cast<std::vector<MetaData::AudioStream> >(items[MetaData::Type::AudioStreams]).empty())
|
||||
{
|
||||
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Skipped '" << file << "' (no audio stream found)";
|
||||
|
||||
@@ -295,8 +295,8 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (items.find(MetaData::Duration) == items.end()
|
||||
|| boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Duration]).total_seconds() == 0)
|
||||
if (items.find(MetaData::Type::Duration) == items.end()
|
||||
|| boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Type::Duration]).total_seconds() == 0)
|
||||
{
|
||||
LMS_LOG(MOD_DBUPDATER, SEV_DEBUG) << "Skipped '" << file << "' (no duration or duration 0)";
|
||||
|
||||
@@ -310,8 +310,8 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
|
||||
// ***** Title
|
||||
std::string title;
|
||||
if (items.find(MetaData::Title) != items.end()) {
|
||||
title = boost::any_cast<std::string>(items[MetaData::Title]);
|
||||
if (items.find(MetaData::Type::Title) != items.end()) {
|
||||
title = boost::any_cast<std::string>(items[MetaData::Type::Title]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -322,9 +322,9 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
|
||||
// ***** Artist
|
||||
Wt::Dbo::ptr<Artist> artist;
|
||||
if (items.find(MetaData::Artist) != items.end())
|
||||
if (items.find(MetaData::Type::Artist) != items.end())
|
||||
{
|
||||
const std::string artistName (boost::any_cast<std::string>(items[MetaData::Artist]));
|
||||
const std::string artistName (boost::any_cast<std::string>(items[MetaData::Type::Artist]));
|
||||
artist = Artist::getByName(_db.getSession(), artistName );
|
||||
if (!artist)
|
||||
artist = Artist::create( _db.getSession(), artistName );
|
||||
@@ -337,9 +337,9 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
|
||||
// ***** Release
|
||||
Wt::Dbo::ptr<Release> release;
|
||||
if (items.find(MetaData::Album) != items.end())
|
||||
if (items.find(MetaData::Type::Album) != items.end())
|
||||
{
|
||||
const std::string albumName (boost::any_cast<std::string>(items[MetaData::Album]));
|
||||
const std::string albumName (boost::any_cast<std::string>(items[MetaData::Type::Album]));
|
||||
release = Release::getByName(_db.getSession(), albumName);
|
||||
if (!release)
|
||||
release = Release::create( _db.getSession(), albumName );
|
||||
@@ -354,9 +354,9 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
typedef std::list<std::string> GenreList;
|
||||
GenreList genreList;
|
||||
std::vector< Genre::pointer > genres;
|
||||
if (items.find(MetaData::Genres) != items.end())
|
||||
if (items.find(MetaData::Type::Genres) != items.end())
|
||||
{
|
||||
genreList = (boost::any_cast<GenreList>(items[MetaData::Genres]));
|
||||
genreList = (boost::any_cast<GenreList>(items[MetaData::Type::Genres]));
|
||||
|
||||
BOOST_FOREACH(const std::string& genre, genreList) {
|
||||
Genre::pointer dbGenre ( Genre::getByName(_db.getSession(), genre) );
|
||||
@@ -406,17 +406,26 @@ Updater::processAudioFile( const boost::filesystem::path& file, Stats& stats)
|
||||
track.modify()->setArtist( artist );
|
||||
track.modify()->setRelease( release );
|
||||
|
||||
if (items.find(MetaData::TrackNumber) != items.end())
|
||||
track.modify()->setTrackNumber( boost::any_cast<std::size_t>(items[MetaData::TrackNumber]) );
|
||||
if (items.find(MetaData::Type::TrackNumber) != items.end())
|
||||
track.modify()->setTrackNumber( boost::any_cast<std::size_t>(items[MetaData::Type::TrackNumber]) );
|
||||
|
||||
if (items.find(MetaData::DiscNumber) != items.end())
|
||||
track.modify()->setDiscNumber( boost::any_cast<std::size_t>(items[MetaData::DiscNumber]) );
|
||||
if (items.find(MetaData::Type::DiscNumber) != items.end())
|
||||
track.modify()->setDiscNumber( boost::any_cast<std::size_t>(items[MetaData::Type::DiscNumber]) );
|
||||
|
||||
if (items.find(MetaData::Duration) != items.end())
|
||||
track.modify()->setDuration( boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Duration]) );
|
||||
if (items.find(MetaData::Type::Duration) != items.end())
|
||||
track.modify()->setDuration( boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Type::Duration]) );
|
||||
|
||||
if (items.find(MetaData::CreationTime) != items.end())
|
||||
track.modify()->setCreationTime( boost::any_cast<boost::posix_time::ptime>(items[MetaData::CreationTime]) );
|
||||
if (items.find(MetaData::Type::Date) != items.end())
|
||||
track.modify()->setDate( boost::any_cast<boost::posix_time::ptime>(items[MetaData::Type::Date]) );
|
||||
|
||||
if (items.find(MetaData::Type::OriginalDate) != items.end())
|
||||
{
|
||||
track.modify()->setOriginalDate( boost::any_cast<boost::posix_time::ptime>(items[MetaData::Type::OriginalDate]) );
|
||||
|
||||
// If a file has an OriginalDate but no date, set the date to ease filtering
|
||||
if (items.find(MetaData::Type::Date) == items.end())
|
||||
track.modify()->setDate( boost::any_cast<boost::posix_time::ptime>(items[MetaData::Type::OriginalDate]) );
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
|
||||
@@ -616,8 +625,8 @@ Updater::processVideoFile( const boost::filesystem::path& file, Stats& stats)
|
||||
// We estimate this is a video if:
|
||||
// - we found a least one video stream
|
||||
// - the duration is not null
|
||||
if (items.find(MetaData::VideoStreams) == items.end()
|
||||
|| boost::any_cast<std::vector<MetaData::VideoStream> >(items[MetaData::VideoStreams]).empty())
|
||||
if (items.find(MetaData::Type::VideoStreams) == items.end()
|
||||
|| boost::any_cast<std::vector<MetaData::VideoStream> >(items[MetaData::Type::VideoStreams]).empty())
|
||||
{
|
||||
LMS_LOG(MOD_DBUPDATER, SEV_ERROR) << "Skipped '" << file << "' (no video stream found)";
|
||||
|
||||
@@ -628,8 +637,8 @@ Updater::processVideoFile( const boost::filesystem::path& file, Stats& stats)
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (items.find(MetaData::Duration) == items.end()
|
||||
|| boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Duration]).total_seconds() == 0)
|
||||
if (items.find(MetaData::Type::Duration) == items.end()
|
||||
|| boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Type::Duration]).total_seconds() == 0)
|
||||
{
|
||||
LMS_LOG(MOD_DBUPDATER, SEV_ERROR) << "Skipped '" << file << "' (no duration or duration 0)";
|
||||
|
||||
@@ -659,7 +668,7 @@ Updater::processVideoFile( const boost::filesystem::path& file, Stats& stats)
|
||||
assert(video);
|
||||
|
||||
video.modify()->setName( file.filename().string() );
|
||||
video.modify()->setDuration( boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Duration]) );
|
||||
video.modify()->setDuration( boost::any_cast<boost::posix_time::time_duration>(items[MetaData::Type::Duration]) );
|
||||
video.modify()->setLastWriteTime(lastWriteTime);
|
||||
|
||||
transaction.commit();
|
||||
|
||||
@@ -196,7 +196,8 @@ class Track
|
||||
void setDuration(boost::posix_time::time_duration duration) { _duration = duration; }
|
||||
void setLastWriteTime(boost::posix_time::ptime time) { _fileLastWrite = time; }
|
||||
void setChecksum(const std::vector<unsigned char>& checksum) { _fileChecksum = checksum; }
|
||||
void setCreationTime(const boost::posix_time::ptime& time) { _creationTime = time; }
|
||||
void setDate(const boost::posix_time::ptime& date) { _date = date; }
|
||||
void setOriginalDate(const boost::posix_time::ptime& date) { _originalDate = date; }
|
||||
void setGenres(const std::string& genreList) { _genreList = genreList; }
|
||||
void setGenres(std::vector<Genre::pointer> genres);
|
||||
void setArtist(Artist::pointer artist) { _artist = artist; }
|
||||
@@ -207,7 +208,8 @@ class Track
|
||||
std::string getName(void) const { return _name; }
|
||||
const std::string& getPath(void) const { return _filePath; }
|
||||
boost::posix_time::time_duration getDuration(void) const { return _duration; }
|
||||
boost::posix_time::ptime getCreationTime(void) const { return _creationTime; }
|
||||
boost::posix_time::ptime getDate(void) const { return _date; }
|
||||
boost::posix_time::ptime getOriginalDate(void) const { return _originalDate; }
|
||||
Artist::pointer getArtist(void) const { return _artist; }
|
||||
Release::pointer getRelease(void) const { return _release; }
|
||||
bool hasGenre(Genre::pointer genre) const { return _genres.count(genre); }
|
||||
@@ -223,7 +225,8 @@ class Track
|
||||
Wt::Dbo::field(a, _discNumber, "disc_number");
|
||||
Wt::Dbo::field(a, _name, "name");
|
||||
Wt::Dbo::field(a, _duration, "duration");
|
||||
Wt::Dbo::field(a, _creationTime, "creation_time");
|
||||
Wt::Dbo::field(a, _date, "date");
|
||||
Wt::Dbo::field(a, _originalDate, "original_date");
|
||||
Wt::Dbo::field(a, _genreList, "genre_list");
|
||||
Wt::Dbo::field(a, _filePath, "path");
|
||||
Wt::Dbo::field(a, _fileLastWrite, "last_write");
|
||||
@@ -241,7 +244,8 @@ class Track
|
||||
int _discNumber;
|
||||
std::string _name;
|
||||
boost::posix_time::time_duration _duration;
|
||||
boost::posix_time::ptime _creationTime;
|
||||
boost::posix_time::ptime _date;
|
||||
boost::posix_time::ptime _originalDate;
|
||||
std::string _genreList;
|
||||
std::string _filePath;
|
||||
std::vector<unsigned char> _fileChecksum;
|
||||
|
||||
+19
-15
@@ -103,16 +103,16 @@ AvFormat::parse(const boost::filesystem::path& p, Items& items)
|
||||
}
|
||||
|
||||
if (!videoStreams.empty())
|
||||
items.insert( std::make_pair(MetaData::VideoStreams, videoStreams));
|
||||
items.insert( std::make_pair(MetaData::Type::VideoStreams, videoStreams));
|
||||
if (!audioStreams.empty())
|
||||
items.insert( std::make_pair(MetaData::AudioStreams, audioStreams));
|
||||
items.insert( std::make_pair(MetaData::Type::AudioStreams, audioStreams));
|
||||
if (!subtitleStreams.empty())
|
||||
items.insert( std::make_pair(MetaData::SubtitleStreams, SubtitleStreams));
|
||||
items.insert( std::make_pair(MetaData::Type::SubtitleStreams, subtitleStreams));
|
||||
|
||||
}
|
||||
|
||||
// Duration
|
||||
items.insert( std::make_pair(MetaData::Duration, boost::posix_time::time_duration( boost::posix_time::seconds( input.getDurationSecs() )) ));
|
||||
items.insert( std::make_pair(MetaData::Type::Duration, boost::posix_time::time_duration( boost::posix_time::seconds( input.getDurationSecs() )) ));
|
||||
|
||||
// Embedded MetaData
|
||||
// Make sure to convert strings into UTF-8
|
||||
@@ -120,38 +120,42 @@ AvFormat::parse(const boost::filesystem::path& p, Items& items)
|
||||
for (it = metadata.begin(); it != metadata.end(); ++it)
|
||||
{
|
||||
if (boost::iequals(it->first, "artist"))
|
||||
items.insert( std::make_pair(MetaData::Artist, string_trim( string_to_utf8(it->second)) ));
|
||||
items.insert( std::make_pair(MetaData::Type::Artist, string_trim( string_to_utf8(it->second)) ));
|
||||
else if (boost::iequals(it->first, "album"))
|
||||
items.insert( std::make_pair(MetaData::Album, string_trim( string_to_utf8(it->second)) ));
|
||||
items.insert( std::make_pair(MetaData::Type::Album, string_trim( string_to_utf8(it->second)) ));
|
||||
else if (boost::iequals(it->first, "title"))
|
||||
items.insert( std::make_pair(MetaData::Title, string_trim( string_to_utf8(it->second)) ));
|
||||
items.insert( std::make_pair(MetaData::Type::Title, string_trim( string_to_utf8(it->second)) ));
|
||||
else if (boost::iequals(it->first, "track")) {
|
||||
std::size_t number;
|
||||
if (readAs<std::size_t>(it->second, number))
|
||||
items.insert( std::make_pair(MetaData::TrackNumber, number ));
|
||||
items.insert( std::make_pair(MetaData::Type::TrackNumber, number ));
|
||||
}
|
||||
else if (boost::iequals(it->first, "disc"))
|
||||
{
|
||||
std::size_t number;
|
||||
if (readAs<std::size_t>(it->second, number))
|
||||
items.insert( std::make_pair(MetaData::DiscNumber, number ));
|
||||
items.insert( std::make_pair(MetaData::Type::DiscNumber, number ));
|
||||
}
|
||||
else if (boost::iequals(it->first, "date")
|
||||
|| boost::iequals(it->first, "year")
|
||||
|| boost::iequals(it->first, "WM/Year")
|
||||
|| boost::iequals(it->first, "TDOR") // Original date fallback
|
||||
|| boost::iequals(it->first, "TORY") // Original date fallback
|
||||
)
|
||||
|| boost::iequals(it->first, "WM/Year"))
|
||||
{
|
||||
boost::posix_time::ptime p;
|
||||
if (readAsPosixTime(it->second, p))
|
||||
items.insert( std::make_pair(MetaData::CreationTime, p));
|
||||
items.insert( std::make_pair(MetaData::Type::Date, p));
|
||||
}
|
||||
else if (boost::iequals(it->first, "TDOR") // Original release time (ID3v2 2.4)
|
||||
|| boost::iequals(it->first, "TORY")) // Original release year
|
||||
{
|
||||
boost::posix_time::ptime p;
|
||||
if (readAsPosixTime(it->second, p))
|
||||
items.insert( std::make_pair(MetaData::Type::OriginalDate, p));
|
||||
}
|
||||
else if (boost::iequals(it->first, "genre"))
|
||||
{
|
||||
std::list<std::string> genres;
|
||||
if (readList(it->second, ";,", genres))
|
||||
items.insert( std::make_pair(MetaData::Genres, genres));
|
||||
items.insert( std::make_pair(MetaData::Type::Genres, genres));
|
||||
|
||||
}
|
||||
/* else
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace MetaData
|
||||
{
|
||||
|
||||
enum Type
|
||||
enum class Type
|
||||
{
|
||||
Artist, // string
|
||||
Title, // string
|
||||
@@ -37,7 +37,8 @@ namespace MetaData
|
||||
Duration, // boost::posix_time::time_duration
|
||||
TrackNumber, // size_t
|
||||
DiscNumber, // size_t
|
||||
CreationTime, // boost::posix_time::ptime
|
||||
Date, // boost::posix_time::ptime
|
||||
OriginalDate, // boost::posix_time::ptime
|
||||
Cover, // GenericData
|
||||
AudioStreams, // vector<AudioStream>
|
||||
VideoStreams, // vector<VideoStream>
|
||||
|
||||
@@ -284,8 +284,12 @@ AudioCollectionRequestHandler::processGetTracks(const AudioCollectionRequest::Ge
|
||||
|
||||
track->set_name( std::string( boost::locale::conv::to_utf<char>((*it)->getName(), "UTF-8") ) );
|
||||
track->set_duration_secs( (*it)->getDuration().total_seconds() );
|
||||
// if (!(*it)->getCreationTime().is_special())
|
||||
// track->set_release_date( boost::posix_time::to_simple_string((*it)->getCreationTime()) );
|
||||
|
||||
// Only send the year part of the release times
|
||||
if (!(*it)->getDate().is_special())
|
||||
track->set_release_date( std::to_string((*it)->getDate().date().year()) );
|
||||
if (!(*it)->getOriginalDate().is_special())
|
||||
track->set_original_release_date( std::to_string((*it)->getOriginalDate().date().year()) );
|
||||
|
||||
BOOST_FOREACH(Database::Genre::pointer genre, (*it)->getGenres())
|
||||
track->add_genre_id( genre.id() );
|
||||
|
||||
@@ -44,7 +44,8 @@ _trackStats(nullptr)
|
||||
_queryModel.addColumn( "track.track_number", "Track #" );
|
||||
_queryModel.addColumn( "track.name", "Track" );
|
||||
_queryModel.addColumn( "track.duration", "Duration" );
|
||||
_queryModel.addColumn( "track.creation_time", "Date" );
|
||||
_queryModel.addColumn( "track.date", "Date" );
|
||||
_queryModel.addColumn( "track.original_date", "Original Date" );
|
||||
_queryModel.addColumn( "track.genre_list", "Genres" );
|
||||
|
||||
_queryModel.setBatchSize(1000);
|
||||
@@ -57,16 +58,24 @@ _trackStats(nullptr)
|
||||
_tableView->setAlternatingRowColors(true);
|
||||
_tableView->setModel(&_queryModel);
|
||||
|
||||
// Duration display
|
||||
{
|
||||
// TODO better handle 1 hour+ files!
|
||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||
delegate->setTextFormat("mm:ss");
|
||||
_tableView->setItemDelegateForColumn(5, delegate);
|
||||
}
|
||||
|
||||
// Date display, just the year
|
||||
{
|
||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||
delegate->setTextFormat("yyyy");
|
||||
_tableView->setItemDelegateForColumn(6, delegate);
|
||||
}
|
||||
{
|
||||
// TODO better handle 1 hour+ files!
|
||||
Wt::WItemDelegate *delegate = new Wt::WItemDelegate(this);
|
||||
delegate->setTextFormat("mm:ss");
|
||||
_tableView->setItemDelegateForColumn(5, delegate);
|
||||
delegate->setTextFormat("yyyy");
|
||||
_tableView->setItemDelegateForColumn(7, delegate);
|
||||
}
|
||||
|
||||
// TODO other event!
|
||||
@@ -101,7 +110,7 @@ TrackWidget::refresh(const Constraint& constraint)
|
||||
|
||||
Wt::Dbo::Query<ResultType> query = _db.getSession().query<ResultType>( sqlQuery.get() );
|
||||
|
||||
query.groupBy("track").orderBy("artist.name,track.creation_time,release.name,track.disc_number,track.track_number");
|
||||
query.groupBy("track").orderBy("artist.name,track.date,release.name,track.disc_number,track.track_number");
|
||||
|
||||
BOOST_FOREACH(const std::string& bindArg, sqlQuery.where().getBindArgs()) {
|
||||
LMS_LOG(MOD_UI, SEV_DEBUG) << "Binding value '" << bindArg << "'";
|
||||
|
||||
+15
-1
@@ -83,12 +83,20 @@ struct TrackInfo
|
||||
|
||||
boost::posix_time::time_duration duration;
|
||||
|
||||
std::string date;
|
||||
std::string original_date;
|
||||
|
||||
TrackInfo(): id(0), release_id(0), artist_id(0), disc_number(0), track_number(0) {}
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const TrackInfo& info)
|
||||
{
|
||||
os << "id = " << info.id << ", name = '" << info.name << "', track_number = " << info.track_number << ", duration = " << info.duration;
|
||||
if (!info.date.empty())
|
||||
os << ", date = " << info.date;
|
||||
if (!info.original_date.empty())
|
||||
os << ", original date = " << info.original_date;
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -352,6 +360,12 @@ class TestClient
|
||||
if (response.audio_collection_response().track_list().tracks(i).has_disc_number())
|
||||
track.disc_number = response.audio_collection_response().track_list().tracks(i).disc_number();
|
||||
|
||||
if (response.audio_collection_response().track_list().tracks(i).has_release_date())
|
||||
track.date = response.audio_collection_response().track_list().tracks(i).release_date();
|
||||
|
||||
if (response.audio_collection_response().track_list().tracks(i).has_original_release_date())
|
||||
track.original_date = response.audio_collection_response().track_list().tracks(i).original_release_date();
|
||||
|
||||
|
||||
tracks.push_back( track );
|
||||
nbAdded++;
|
||||
@@ -756,7 +770,7 @@ int main()
|
||||
TestClient client( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4::loopback(), 5080));
|
||||
|
||||
// Use a dumb account in order to login TODO parametrize
|
||||
if (!client.login("admin", "admin"))
|
||||
if (!client.login("admin", "toto"))
|
||||
throw std::runtime_error("login failed!");
|
||||
|
||||
// **** REVISION ***
|
||||
|
||||
Reference in New Issue
Block a user