Merge branch 'develop'

This commit is contained in:
emeric
2018-11-17 16:01:30 +01:00
18 changed files with 238 additions and 37 deletions
+1
View File
@@ -34,6 +34,7 @@ lms_approot_DATA = \
approot/login.xml \
approot/mediaplayer.xml \
approot/messages.xml \
approot/messages_fr.xml \
approot/playhistory.xml \
approot/playqueue.xml \
approot/release.xml \
+10
View File
@@ -6,6 +6,16 @@
<h2>${title}</h2>
</div>
<div class="form-horizontal">
${<if-has-last-login-attempt>}
<div class="form-group">
<label class="control-label col-sm-2" for="{id:last-login-attempt}">
${tr:Lms.Admin.User.last-login-attempt}
</label>
<div class="col-sm-5">
${last-login-attempt}
</div>
</div>
${</if-has-last-login-attempt>}
${<if-has-login>}
<div class="form-group">
<label class="control-label col-sm-2" for="${id:login}">
-3
View File
@@ -39,9 +39,6 @@
<div class="col-sm-5">
${remember-me}
</div>
<div class="help-block col-sm-5">
${remember-me-info}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
+7 -1
View File
@@ -49,6 +49,7 @@
<message id="Lms.Admin.User.demo-account">Demo account</message>
<message id="Lms.Admin.User.demo-account-already-exists">Demo account already exists!</message>
<message id="Lms.Admin.User.demo-password-invalid">Demo password must be the login name!</message>
<message id="Lms.Admin.User.last-login-attempt">Last login attempt</message>
<message id="Lms.Admin.User.user-already-exists">User already exists!</message>
<message id="Lms.Admin.User.user-create">New user</message>
<message id="Lms.Admin.User.user-created">New user created!</message>
@@ -84,7 +85,7 @@
<!--Explore:Releases-->
<message id="Lms.Explore.ReleasesInfo.recently-added">Recently added</message>
<message id="Lms.Explore.ReleasesInfo.most-played">Top releases</message>
<message id="Lms.Explore.ReleasesInfo.most-played">Top albums</message>
<!--Explore:Tracks-->
<message id="Lms.Explore.TracksInfo.recently-added">Recently added</message>
@@ -118,4 +119,9 @@
<message id="Lms.Settings.webma">WEBMA</message>
<message id="Lms.Settings.settings-saved">New settings saved!</message>
<!--Wt-->
<message id="Wt.Auth.valid"></message>
<message id="Wt.Auth.user-name-info"></message>
<message id="Wt.Auth.password-info"></message>
</messages>
+132
View File
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8" ?>
<messages xmlns:if="Wt.WTemplate.conditions">
<!--Common-->
<message id="Lms.add">Ajouter</message>
<message id="Lms.apply">Appliquer</message>
<message id="Lms.discard">Annuler</message>
<message id="Lms.administration">Administration</message>
<message id="Lms.cancel">Annuler</message>
<message id="Lms.create">Créer</message>
<message id="Lms.login">Login</message>
<message id="Lms.logout">Logout</message>
<message id="Lms.not-a-directory">N'est pas un répertoire</message>
<message id="Lms.password">Mot de passe</message>
<message id="Lms.password-confirm">Confirmation du mot de passe</message>
<message id="Lms.passwords-dont-match">Les mots de passe ne correspondent pas</message>
<message id="Lms.quit-confirm">Êtes-vous sûr ?</message>
<message id="Lms.quit-other-session">Une autre session a été ouverte. Reouvrir celle-ci ?</message>
<message id="Lms.save">Sauvegarder</message>
<!--Administration-->
<message id="Lms.Admin.Database.daily">Tous les jours</message>
<message id="Lms.Admin.Database.database">Base de données</message>
<message id="Lms.Admin.Database.immediate-scan">Scanner maintenant !</message>
<message id="Lms.Admin.Database.monthly">Tous les mois</message>
<message id="Lms.Admin.Database.never">Jamais</message>
<message id="Lms.Admin.Database.path">Dossier racine des fichiers de musique</message>
<message id="Lms.Admin.Database.scan-complete">Scan terminé : {1} fichiers, {2} ajouts, {3} mises à jour, {4} suppressions, {5} duplicatas, {6} erreurs</message>
<message id="Lms.Admin.Database.scan-launched">Scan lancé !</message>
<message id="Lms.Admin.Database.scan-options">Options </message>
<message id="Lms.Admin.Database.settings-saved">Nouveaux paramètres sauvegardés !</message>
<message id="Lms.Admin.Database.tags">Tags</message>
<message id="Lms.Admin.Database.update-period">Périodicité des mises à jour</message>
<message id="Lms.Admin.Database.update-start-time">Heure de départ de la mise à jour</message>
<message id="Lms.Admin.Database.weekly">Toutes les semaines</message>
<!--Users-->
<message id="Lms.Admin.Users.add">Ajouter</message>
<message id="Lms.Admin.Users.admin">Admin</message>
<message id="Lms.Admin.Users.del">Supprimer</message>
<message id="Lms.Admin.Users.del-user">Supprimer l'utilisateur</message>
<message id="Lms.Admin.Users.del-user-name">Supprimer l'utilisateur ?</message>
<message id="Lms.Admin.Users.demo">Demo</message>
<message id="Lms.Admin.Users.edit">Editer</message>
<message id="Lms.Admin.Users.users">Utilisateurs</message>
<!--User-->
<message id="Lms.Admin.User.audio-bitrate-limit">Limite du bitrate</message>
<message id="Lms.Admin.User.demo-account">Compte de démonstration</message>
<message id="Lms.Admin.User.demo-account-already-exists">Le compte de démonstration existe déjà !</message>
<message id="Lms.Admin.User.demo-password-invalid">Le password doit être égal au login !</message>
<message id="Lms.Admin.User.last-login-attempt">Dernière tentative de login</message>
<message id="Lms.Admin.User.user-already-exists">L'utilisateur existe déjà !</message>
<message id="Lms.Admin.User.user-create">Nouvel utilisateur</message>
<message id="Lms.Admin.User.user-created">Nouvel utilisateur créé !</message>
<message id="Lms.Admin.User.user-edit">Utilisateur {1}</message>
<message id="Lms.Admin.User.user-updated">L'utilisateur a été mis à jour !</message>
<!--Wizard-->
<message id="Lms.Admin.InitWizard.done">Le compte d'administration a été créé. Rafraichissez la page pour continuer !</message>
<message id="Lms.Admin.InitWizard.header">Creation du compte administrateur</message>
<!--Auth-->
<message id="Lms.Auth.remember-me">Se souvenir de moi</message>
<message id="Lms.Auth.welcome">Bienvenue !</message>
<!--Explore-->
<message id="Lms.Explore.add-filter">Ajouter filtre</message>
<message id="Lms.Explore.artists">Artistes</message>
<message id="Lms.Explore.filters">Filtres</message>
<message id="Lms.Explore.releases">Albums</message>
<message id="Lms.Explore.search-placeholder">Rechercher...</message>
<message id="Lms.Explore.show-more">Montrer plus</message>
<message id="Lms.Explore.tracks">Pistes</message>
<message id="Lms.Explore.type">Type</message>
<message id="Lms.Explore.value">Valeur</message>
<message id="Lms.Explore.various-artists">Artistes divers</message>
<!--Explore:Artists-->
<message id="Lms.Explore.ArtistsInfo.recently-added">Ajouts récents</message>
<message id="Lms.Explore.ArtistsInfo.most-played">Artistes populaires</message>
<!--Explore:Release-->
<message id="Lms.Explore.Release.copyright">Copyright</message>
<!--Explore:Releases-->
<message id="Lms.Explore.ReleasesInfo.recently-added">Ajouts récents</message>
<message id="Lms.Explore.ReleasesInfo.most-played">Albums populaires</message>
<!--Explore:Tracks-->
<message id="Lms.Explore.TracksInfo.recently-added">Ajouts récents</message>
<message id="Lms.Explore.TracksInfo.most-played">Pistes populaires</message>
<!--Playqueue-->
<message id="Lms.PlayQueue.clear">Effacer</message>
<message id="Lms.PlayQueue.nb-tracks">{1} pistes</message>
<message id="Lms.PlayQueue.nb-tracks-added">{1} pistes ajoutées</message>
<message id="Lms.PlayQueue.nb-tracks-playing">{1} pistes ajouées</message>
<message id="Lms.PlayQueue.playqueue">Liste de lecture</message>
<message id="Lms.PlayQueue.radio-mode">Mode radio</message>
<message id="Lms.PlayQueue.repeat">Repéter</message>
<message id="Lms.PlayQueue.shuffle">Mélanger</message>
<!--PlayHistory-->
<message id="Lms.PlayHistory.playhistory">Historique de lecture</message>
<!--Settings-->
<message id="Lms.Settings.account">Compte</message>
<message id="Lms.Settings.audio">Audio</message>
<message id="Lms.Settings.auto">Auto</message>
<message id="Lms.Settings.bitrate">Bitrate</message>
<message id="Lms.Settings.demo-cannot-save">Impossible de sauvegarder en utilisant un compte de démo !</message>
<message id="Lms.Settings.encoding">Encodage</message>
<message id="Lms.Settings.mp3">MP3</message>
<message id="Lms.Settings.oga">OGA</message>
<message id="Lms.Settings.settings">Paramètres</message>
<message id="Lms.Settings.webma">WEBMA</message>
<message id="Lms.Settings.settings-saved">Paramètres sauvegardés !</message>
<!--Wt-->
<message id="Wt.WMessageBox.Yes">Oui</message>
<message id="Wt.WMessageBox.No">Non</message>
<message id="Wt.Auth.valid"></message>
<message id="Wt.Auth.user-name-info"></message>
<message id="Wt.Auth.user-name-invalid">Invalide</message>
<message id="Wt.Auth.password-info"></message>
<message id="Wt.Auth.password-invalid">Mot de passe invalide</message>
</messages>
+4
View File
@@ -2,6 +2,10 @@
<messages xmlns:if="Wt.WTemplate.conditions">
<!--FORMS message blocks-->
<message id="Lms.Explore.Release.template.link-btn">
<i class="fa fa-external-link"></i>
</message>
<message id="Lms.Explore.Release.template">
<div class="row">
<div class="col-md-12">
+3 -3
View File
@@ -158,7 +158,7 @@ a:hover {
}
.Lms-explore-release-entry-controls {
text-align: center;
text-align: right;
}
.Lms-explore-release-entry-playing {
@@ -202,7 +202,7 @@ a:hover {
}
.Lms-explore-tracks-entry-controls {
text-align: center;
text-align: right;
}
.Lms-explore-tracksinfo-entry {
@@ -235,7 +235,7 @@ a:hover {
}
.Lms-playqueue-entry-controls {
text-align: center;
text-align: right;
}
.Lms-playqueue-btn {
+1
View File
@@ -54,6 +54,7 @@ Handler::configureAuth(void)
{
authService.setEmailVerificationEnabled(false);
authService.setAuthTokensEnabled(true, "lmsauth");
authService.setAuthTokenValidity(24 * 60 * 365); // A year
authService.setIdentityPolicy(Wt::Auth::IdentityPolicy::LoginName);
authService.setRandomTokenLength(32);
+1
View File
@@ -95,6 +95,7 @@ class Track : public Wt::Dbo::Dbo<Track>
void setCopyrightURL(const std::string& copyrightURL) { _copyrightURL = std::string(copyrightURL, 0, _maxCopyrightURLLength); }
void setArtist(Wt::Dbo::ptr<Artist> artist) { _artist = artist; }
void setRelease(Wt::Dbo::ptr<Release> release) { _release = release; }
void eraseClusters() { _clusters.clear(); }
std::size_t getScanVersion() const { return _scanVersion; }
boost::optional<std::size_t> getTrackNumber() const;
+1 -5
View File
@@ -35,11 +35,7 @@ User::audioBitrates =
};
User::User()
: _maxAudioBitrate(audioBitrates.back()),
_type(Type::REGULAR),
_audioBitrate(defaultAudioBitrate),
_audioEncoding(AudioEncoding::AUTO),
_curPlayingTrackPos(0)
: _maxAudioBitrate(audioBitrates.back())
{
}
+12 -4
View File
@@ -76,6 +76,8 @@ class User : public Wt::Dbo::Dbo<User>
void setAudioEncoding(AudioEncoding encoding) { _audioEncoding = encoding; }
void setMaxAudioBitrate(std::size_t bitrate);
void setCurPlayingTrackPos(std::size_t pos) { _curPlayingTrackPos = pos; }
void setRadio(bool val) { _radio = val; }
void setRepeatAll(bool val) { _repeatAll = val; }
// read
bool isAdmin() const { return _type == Type::ADMIN; }
@@ -84,6 +86,8 @@ class User : public Wt::Dbo::Dbo<User>
AudioEncoding getAudioEncoding() const { return _audioEncoding; }
std::size_t getMaxAudioBitrate() const;
std::size_t getCurPlayingTrackPos() const { return _curPlayingTrackPos; }
bool isRepeatAllSet() const { return _repeatAll; }
bool isRadioSet() const { return _radio; }
Wt::Dbo::ptr<TrackList> getQueuedTrackList() const;
Wt::Dbo::ptr<TrackList> getPlayedTrackList() const;
@@ -97,6 +101,8 @@ class User : public Wt::Dbo::Dbo<User>
Wt::Dbo::field(a, _audioEncoding, "audio_encoding");
// User's dynamic data
Wt::Dbo::field(a, _curPlayingTrackPos, "cur_playing_track_pos");
Wt::Dbo::field(a, _repeatAll, "repeat_all");
Wt::Dbo::field(a, _radio, "radio");
Wt::Dbo::hasMany(a, _tracklists, Wt::Dbo::ManyToOne, "user");
}
@@ -106,14 +112,16 @@ class User : public Wt::Dbo::Dbo<User>
// Admin defined settings
int _maxAudioBitrate;
Type _type;
Type _type = Type::REGULAR;
// User defined settings
int _audioBitrate;
AudioEncoding _audioEncoding;
int _audioBitrate = defaultAudioBitrate;
AudioEncoding _audioEncoding = AudioEncoding::AUTO;
// User's dynamic data
int _curPlayingTrackPos; // Current track position in queue
int _curPlayingTrackPos = 0; // Current track position in queue
bool _repeatAll = false;
bool _radio = false;
Wt::Dbo::collection< Wt::Dbo::ptr<TrackList> > _tracklists;
+1 -2
View File
@@ -504,8 +504,7 @@ MediaScanner::scanAudioFile(const boost::filesystem::path& file, bool forceScan,
LMS_LOG(DBUPDATER, INFO) << "Updating '" << file.string() << "'";
// Remove the songs from its clusters
for (auto cluster : track->getClusters())
cluster.remove();
track.modify()->eraseClusters();
stats.updates++;
}
+2
View File
@@ -85,6 +85,8 @@ LmsApplication::LmsApplication(const Wt::WEnvironment& env, Wt::Dbo::SqlConnecti
bootstrapTheme->setResponsive(true);
setTheme(std::move(bootstrapTheme));
addMetaHeader(Wt::MetaHeaderType::Meta, "viewport", "width=device-width, user-scalable=no");
useStyleSheet("css/lms.css");
useStyleSheet("resources/font-awesome/css/font-awesome.min.css");
+47 -14
View File
@@ -30,7 +30,6 @@
#include "LmsApplication.hpp"
namespace UserInterface {
PlayQueue::PlayQueue()
@@ -38,6 +37,12 @@ PlayQueue::PlayQueue()
{
addFunction("tr", &Wt::WTemplate::Functions::tr);
{
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
_repeatAll = LmsApp->getUser()->isRepeatAllSet();
_radioMode = LmsApp->getUser()->isRadioSet();
}
Wt::WText* clearBtn = bindNew<Wt::WText>("clear-btn", Wt::WString::tr("Lms.PlayQueue.template.clear-btn"), Wt::TextFormat::XHTML);
clearBtn->setToolTip(Wt::WString::tr("Lms.PlayQueue.clear"));
clearBtn->clicked().connect([=]
@@ -68,21 +73,33 @@ PlayQueue::PlayQueue()
addSome();
});
Wt::WText* repeatBtn = bindNew<Wt::WText>("repeat-btn", Wt::WString::tr("Lms.PlayQueue.template.repeat-btn"), Wt::TextFormat::XHTML);
repeatBtn->setToolTip(Wt::WString::tr("Lms.PlayQueue.repeat"));
repeatBtn->clicked().connect([=]
_repeatBtn = bindNew<Wt::WText>("repeat-btn", Wt::WString::tr("Lms.PlayQueue.template.repeat-btn"), Wt::TextFormat::XHTML);
_repeatBtn->setToolTip(Wt::WString::tr("Lms.PlayQueue.repeat"));
_repeatBtn->clicked().connect([=]
{
_repeatAll = !_repeatAll;
repeatBtn->toggleStyleClass("Lms-playqueue-btn-selected", _repeatAll);
});
updateRepeatBtn();
Wt::WText* radioBtn = bindNew<Wt::WText>("radio-btn", Wt::WString::tr("Lms.PlayQueue.template.radio-btn"));
radioBtn->setToolTip(Wt::WString::tr("Lms.PlayQueue.radio-mode"));
radioBtn->clicked().connect([=]
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
if (!LmsApp->getUser()->isDemo())
LmsApp->getUser().modify()->setRepeatAll(_repeatAll);
});
updateRepeatBtn();
_radioBtn = bindNew<Wt::WText>("radio-btn", Wt::WString::tr("Lms.PlayQueue.template.radio-btn"));
_radioBtn->setToolTip(Wt::WString::tr("Lms.PlayQueue.radio-mode"));
_radioBtn->clicked().connect([=]
{
_radioMode = !_radioMode;
radioBtn->toggleStyleClass("Lms-playqueue-btn-selected", _radioMode);
updateRadioBtn();
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
if (!LmsApp->getUser()->isDemo())
LmsApp->getUser().modify()->setRadio(_radioMode);
});
updateRadioBtn();
_nbTracks = bindNew<Wt::WText>("nb-tracks");
@@ -103,15 +120,31 @@ PlayQueue::PlayQueue()
updateInfo();
addSome();
if (!LmsApp->getUser()->isDemo())
{
LmsApp->post([=]
Wt::Dbo::Transaction transaction(LmsApp->getDboSession());
if (!LmsApp->getUser()->isDemo())
{
load(LmsApp->getUser()->getCurPlayingTrackPos(), false);
});
LmsApp->post([=]
{
load(LmsApp->getUser()->getCurPlayingTrackPos(), false);
});
}
}
}
void
PlayQueue::updateRepeatBtn()
{
_repeatBtn->toggleStyleClass("Lms-playqueue-btn-selected", _repeatAll);
}
void
PlayQueue::updateRadioBtn()
{
_radioBtn->toggleStyleClass("Lms-playqueue-btn-selected",_radioMode);
}
Database::TrackList::pointer
PlayQueue::getTrackList()
{
+7 -3
View File
@@ -63,6 +63,8 @@ class PlayQueue : public Wt::WTemplate
void addRadioTrack();
void updateInfo();
void updateCurrentTrack(bool selected);
void updateRepeatBtn();
void updateRadioBtn();
void load(std::size_t pos, bool play);
void stop();
@@ -70,9 +72,11 @@ class PlayQueue : public Wt::WTemplate
bool _repeatAll = false;
bool _radioMode = false;
boost::optional<Database::IdType> _tracklistId;
Wt::WContainerWidget* _entriesContainer;
Wt::WPushButton* _showMore;
Wt::WText* _nbTracks;
Wt::WContainerWidget* _entriesContainer = nullptr;
Wt::WPushButton* _showMore = nullptr;
Wt::WText* _nbTracks = nullptr;
Wt::WText* _repeatBtn = nullptr;
Wt::WText* _radioBtn = nullptr;
boost::optional<std::size_t> _trackPos; // current track position, if set
};
+5
View File
@@ -274,6 +274,11 @@ UserView::refreshView()
auto authUser = LmsApp->getDb().getUserDatabase().findWithId( std::to_string(*userId) );
auto name = authUser.identity(Wt::Auth::Identity::LoginName);
t->bindString("title", Wt::WString::tr("Lms.Admin.User.user-edit").arg(name), Wt::TextFormat::Plain);
t->setCondition("if-has-last-login-attempt", true);
Wt::WLineEdit *lastLoginAttempt = t->bindNew<Wt::WLineEdit>("last-login-attempt");
lastLoginAttempt->setText(authUser.lastLoginAttempt().toString());
lastLoginAttempt->setEnabled(false);
}
else
{
+2 -2
View File
@@ -106,8 +106,8 @@ Release::refresh()
Wt::WLink link(*copyrightURL);
link.setTarget(Wt::LinkTarget::NewWindow);
Wt::WAnchor* anchor = t->bindNew<Wt::WAnchor>("copyright-url", link);
anchor->setTextFormat(Wt::TextFormat::Plain);
anchor->setText(Wt::WString::fromUTF8(*copyrightURL));
anchor->setTextFormat(Wt::TextFormat::XHTML);
anchor->setText(Wt::WString::tr("Lms.Explore.Release.template.link-btn"));
}
if (copyright)
+2
View File
@@ -20,6 +20,8 @@ int main(int argc, char *argv[])
{
MetaData::TagLibParser parser;
parser.setClusterTypeNames( {"MOOD", "GENRE"} );
boost::optional<MetaData::Items> items = parser.parse(argv[1], true);
if (!items)
{