Added a way to save the current playqueue in a playlist
This commit is contained in:
+3
-3
@@ -32,9 +32,9 @@
|
|||||||
${value class="form-control"}
|
${value class="form-control"}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<div class="modal-footer">
|
</div>
|
||||||
${add-btn class="btn btn-primary me-1"}${cancel-btn class="btn btn-secondary"}
|
<div class="modal-footer">
|
||||||
</div>
|
${add-btn class="btn btn-primary me-1"}${cancel-btn class="btn btn-secondary"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,6 +26,10 @@
|
|||||||
<message id="Lms.passwords-dont-match">Passwords don't match</message>
|
<message id="Lms.passwords-dont-match">Passwords don't match</message>
|
||||||
<message id="Lms.quit-other-session">Another session has been open. Reopen this one?</message>
|
<message id="Lms.quit-other-session">Another session has been open. Reopen this one?</message>
|
||||||
<message id="Lms.save">Save</message>
|
<message id="Lms.save">Save</message>
|
||||||
|
<message id="Lms.track-count">
|
||||||
|
<plural case="0">{1} track</plural>
|
||||||
|
<plural case="1">{1} tracks</plural>
|
||||||
|
</message>
|
||||||
<message id="Lms.user">User</message>
|
<message id="Lms.user">User</message>
|
||||||
|
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
@@ -157,12 +161,6 @@
|
|||||||
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
||||||
<message id="Lms.Explore.Release.disc">Disc {1}</message>
|
<message id="Lms.Explore.Release.disc">Disc {1}</message>
|
||||||
|
|
||||||
<!--Explore:TrackList-->
|
|
||||||
<message id="Lms.Explore.TrackList.track-count">
|
|
||||||
<plural case="0">{1} track</plural>
|
|
||||||
<plural case="1">{1} tracks</plural>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<!--Explore:TrackLists-->
|
<!--Explore:TrackLists-->
|
||||||
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Delete playlist?</message>
|
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Delete playlist?</message>
|
||||||
|
|
||||||
@@ -174,7 +172,7 @@
|
|||||||
|
|
||||||
<!--Playqueue-->
|
<!--Playqueue-->
|
||||||
<message id="Lms.PlayQueue.clear">Clear</message>
|
<message id="Lms.PlayQueue.clear">Clear</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks">{1} tracks</message>
|
<message id="Lms.PlayQueue.create-tracklist">Create new playlist</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks-added">
|
<message id="Lms.PlayQueue.nb-tracks-added">
|
||||||
<plural case="0">Added {1} track</plural>
|
<plural case="0">Added {1} track</plural>
|
||||||
<plural case="1">Added {1} tracks</plural>
|
<plural case="1">Added {1} tracks</plural>
|
||||||
@@ -183,6 +181,7 @@
|
|||||||
<message id="Lms.PlayQueue.playqueue-full">Play Queue full!</message>
|
<message id="Lms.PlayQueue.playqueue-full">Play Queue full!</message>
|
||||||
<message id="Lms.PlayQueue.radio-mode">Radio mode</message>
|
<message id="Lms.PlayQueue.radio-mode">Radio mode</message>
|
||||||
<message id="Lms.PlayQueue.repeat">Repeat</message>
|
<message id="Lms.PlayQueue.repeat">Repeat</message>
|
||||||
|
<message id="Lms.PlayQueue.replace-tracklist">Replace existing playlist</message>
|
||||||
<message id="Lms.PlayQueue.shuffle">Shuffle</message>
|
<message id="Lms.PlayQueue.shuffle">Shuffle</message>
|
||||||
|
|
||||||
<!--PlayHistory-->
|
<!--PlayHistory-->
|
||||||
|
|||||||
@@ -26,6 +26,10 @@
|
|||||||
<message id="Lms.passwords-dont-match">Les mots de passe ne correspondent pas</message>
|
<message id="Lms.passwords-dont-match">Les mots de passe ne correspondent pas</message>
|
||||||
<message id="Lms.quit-other-session">Une autre session a été ouverte. Reouvrir celle-ci ?</message>
|
<message id="Lms.quit-other-session">Une autre session a été ouverte. Reouvrir celle-ci ?</message>
|
||||||
<message id="Lms.save">Sauvegarder</message>
|
<message id="Lms.save">Sauvegarder</message>
|
||||||
|
<message id="Lms.track-count">
|
||||||
|
<plural case="0">{1} piste</plural>
|
||||||
|
<plural case="1">{1} pistes</plural>
|
||||||
|
</message>
|
||||||
<message id="Lms.user">Utilisateur</message>
|
<message id="Lms.user">Utilisateur</message>
|
||||||
|
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
@@ -157,12 +161,6 @@
|
|||||||
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
||||||
<message id="Lms.Explore.Release.disc">Disque {1}</message>
|
<message id="Lms.Explore.Release.disc">Disque {1}</message>
|
||||||
|
|
||||||
<!--Explore:TrackList-->
|
|
||||||
<message id="Lms.Explore.TrackList.track-count">
|
|
||||||
<plural case="0">{1} piste</plural>
|
|
||||||
<plural case="1">{1} pistes</plural>
|
|
||||||
</message>
|
|
||||||
|
|
||||||
<!--Explore:TrackLists-->
|
<!--Explore:TrackLists-->
|
||||||
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Supprimer la liste de lecture ?</message>
|
<message id="Lms.Explore.TrackLists.del-tracklist-confirm">Supprimer la liste de lecture ?</message>
|
||||||
|
|
||||||
@@ -174,7 +172,7 @@
|
|||||||
|
|
||||||
<!--Playqueue-->
|
<!--Playqueue-->
|
||||||
<message id="Lms.PlayQueue.clear">Effacer</message>
|
<message id="Lms.PlayQueue.clear">Effacer</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks">{1} pistes</message>
|
<message id="Lms.PlayQueue.create-tracklist">Créer une nouvelle liste de lecture</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks-added">
|
<message id="Lms.PlayQueue.nb-tracks-added">
|
||||||
<plural case="0">{1} piste ajoutée</plural>
|
<plural case="0">{1} piste ajoutée</plural>
|
||||||
<plural case="1">{1} pistes ajoutées</plural>
|
<plural case="1">{1} pistes ajoutées</plural>
|
||||||
@@ -183,12 +181,12 @@
|
|||||||
<message id="Lms.PlayQueue.playqueue-full">Liste de lecture pleine!</message>
|
<message id="Lms.PlayQueue.playqueue-full">Liste de lecture pleine!</message>
|
||||||
<message id="Lms.PlayQueue.radio-mode">Mode radio</message>
|
<message id="Lms.PlayQueue.radio-mode">Mode radio</message>
|
||||||
<message id="Lms.PlayQueue.repeat">Repéter</message>
|
<message id="Lms.PlayQueue.repeat">Repéter</message>
|
||||||
|
<message id="Lms.PlayQueue.replace-tracklist">Remplacer une liste de lecture existante</message>
|
||||||
<message id="Lms.PlayQueue.shuffle">Mélanger</message>
|
<message id="Lms.PlayQueue.shuffle">Mélanger</message>
|
||||||
|
|
||||||
<!--PlayHistory-->
|
<!--PlayHistory-->
|
||||||
<message id="Lms.PlayHistory.playhistory">Historique de lecture</message>
|
<message id="Lms.PlayHistory.playhistory">Historique de lecture</message>
|
||||||
|
|
||||||
|
|
||||||
<!--Settings-->
|
<!--Settings-->
|
||||||
<message id="Lms.Settings.audio">Audio</message>
|
<message id="Lms.Settings.audio">Audio</message>
|
||||||
<message id="Lms.Settings.audio-settings-are-local">Ces paramètres audio sont locaux à votre navigateur !</message>
|
<message id="Lms.Settings.audio-settings-are-local">Ces paramètres audio sont locaux à votre navigateur !</message>
|
||||||
|
|||||||
+12
-8
@@ -26,6 +26,10 @@
|
|||||||
<message id="Lms.passwords-dont-match">Le password non corrispondono</message>
|
<message id="Lms.passwords-dont-match">Le password non corrispondono</message>
|
||||||
<message id="Lms.quit-other-session">Un'altra sessione è stata aperta. Vuoi riaprire questa?</message>
|
<message id="Lms.quit-other-session">Un'altra sessione è stata aperta. Vuoi riaprire questa?</message>
|
||||||
<message id="Lms.save">Salva</message>
|
<message id="Lms.save">Salva</message>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<message id="Lms.user">Utente</message>
|
<message id="Lms.user">Utente</message>
|
||||||
|
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
@@ -46,6 +50,7 @@
|
|||||||
<message id="Lms.Admin.Database.menu-database"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Raccolta musicale</message>
|
<message id="Lms.Admin.Database.menu-database"><i class="fa fa-fw fa-database" aria-hidden="true"></i> Raccolta musicale</message>
|
||||||
<message id="Lms.Admin.Database.never">Mai</message>
|
<message id="Lms.Admin.Database.never">Mai</message>
|
||||||
<message id="Lms.Admin.Database.path">Cartella dei file multimediali</message>
|
<message id="Lms.Admin.Database.path">Cartella dei file multimediali</message>
|
||||||
|
|
||||||
<message id="Lms.Admin.Database.recommendation-engine-type">Modalità di raccomandazione brani consigliati</message>
|
<message id="Lms.Admin.Database.recommendation-engine-type">Modalità di raccomandazione brani consigliati</message>
|
||||||
<message id="Lms.Admin.Database.recommendation-engine-type.clusters">Basata sui tag</message>
|
<message id="Lms.Admin.Database.recommendation-engine-type.clusters">Basata sui tag</message>
|
||||||
<message id="Lms.Admin.Database.recommendation-engine-type.features">Basata sull'analisi acustica</message>
|
<message id="Lms.Admin.Database.recommendation-engine-type.features">Basata sull'analisi acustica</message>
|
||||||
@@ -156,12 +161,6 @@
|
|||||||
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
<message id="Lms.Explore.Release.copyright">Copyright</message>
|
||||||
<message id="Lms.Explore.Release.disc">Disco {1}</message>
|
<message id="Lms.Explore.Release.disc">Disco {1}</message>
|
||||||
|
|
||||||
<!--Explore:TrackList-->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--Explore:TrackLists-->
|
<!--Explore:TrackLists-->
|
||||||
|
|
||||||
|
|
||||||
@@ -173,7 +172,7 @@
|
|||||||
|
|
||||||
<!--Playqueue-->
|
<!--Playqueue-->
|
||||||
<message id="Lms.PlayQueue.clear">Cancella</message>
|
<message id="Lms.PlayQueue.clear">Cancella</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks">{1} tracce</message>
|
|
||||||
<message id="Lms.PlayQueue.nb-tracks-added">
|
<message id="Lms.PlayQueue.nb-tracks-added">
|
||||||
<plural case="0">Aggiunta {1} traccia</plural>
|
<plural case="0">Aggiunta {1} traccia</plural>
|
||||||
<plural case="1">Aggiunte {1} tracce</plural>
|
<plural case="1">Aggiunte {1} tracce</plural>
|
||||||
@@ -182,12 +181,12 @@
|
|||||||
<message id="Lms.PlayQueue.playqueue-full">Coda di riproduzione piena!</message>
|
<message id="Lms.PlayQueue.playqueue-full">Coda di riproduzione piena!</message>
|
||||||
<message id="Lms.PlayQueue.radio-mode">Modalità radio</message>
|
<message id="Lms.PlayQueue.radio-mode">Modalità radio</message>
|
||||||
<message id="Lms.PlayQueue.repeat">Ripeti</message>
|
<message id="Lms.PlayQueue.repeat">Ripeti</message>
|
||||||
|
|
||||||
<message id="Lms.PlayQueue.shuffle">Mischia</message>
|
<message id="Lms.PlayQueue.shuffle">Mischia</message>
|
||||||
|
|
||||||
<!--PlayHistory-->
|
<!--PlayHistory-->
|
||||||
<message id="Lms.PlayHistory.playhistory">Storico riproduzione</message>
|
<message id="Lms.PlayHistory.playhistory">Storico riproduzione</message>
|
||||||
|
|
||||||
|
|
||||||
<!--Settings-->
|
<!--Settings-->
|
||||||
<message id="Lms.Settings.audio">Audio</message>
|
<message id="Lms.Settings.audio">Audio</message>
|
||||||
<message id="Lms.Settings.audio-settings-are-local">Queste impostazioni sono salvate localmente su questo dispositivo!</message>
|
<message id="Lms.Settings.audio-settings-are-local">Queste impostazioni sono salvate localmente su questo dispositivo!</message>
|
||||||
@@ -203,6 +202,11 @@
|
|||||||
<message id="Lms.Settings.replaygain-mode.release">Album</message>
|
<message id="Lms.Settings.replaygain-mode.release">Album</message>
|
||||||
<message id="Lms.Settings.replaygain-preamp">Preamplificazione ReplayGain</message>
|
<message id="Lms.Settings.replaygain-preamp">Preamplificazione ReplayGain</message>
|
||||||
<message id="Lms.Settings.replaygain-preamp-no-rg-info">Preamplificazione ReplayGain (se non è disponibile nessuna informazione)</message>
|
<message id="Lms.Settings.replaygain-preamp-no-rg-info">Preamplificazione ReplayGain (se non è disponibile nessuna informazione)</message>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<message id="Lms.Settings.subsonic-artist-list-mode">Modalità di elencazione artisti</message>
|
<message id="Lms.Settings.subsonic-artist-list-mode">Modalità di elencazione artisti</message>
|
||||||
<message id="Lms.Settings.subsonic-artist-list-mode.all-artists">Tutti gli artisti</message>
|
<message id="Lms.Settings.subsonic-artist-list-mode.all-artists">Tutti gli artisti</message>
|
||||||
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Artisti album</message>
|
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">Artisti album</message>
|
||||||
|
|||||||
+11
-7
@@ -26,6 +26,10 @@
|
|||||||
<message id="Lms.passwords-dont-match">密码不匹配</message>
|
<message id="Lms.passwords-dont-match">密码不匹配</message>
|
||||||
<message id="Lms.quit-other-session">另一个会话已打开,重新打开这一个?</message>
|
<message id="Lms.quit-other-session">另一个会话已打开,重新打开这一个?</message>
|
||||||
<message id="Lms.save">保存</message>
|
<message id="Lms.save">保存</message>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<message id="Lms.user">用户</message>
|
<message id="Lms.user">用户</message>
|
||||||
|
|
||||||
<!--Errors-->
|
<!--Errors-->
|
||||||
@@ -157,12 +161,6 @@
|
|||||||
<message id="Lms.Explore.Release.copyright">版权所有</message>
|
<message id="Lms.Explore.Release.copyright">版权所有</message>
|
||||||
<message id="Lms.Explore.Release.disc">唱片 {1}</message>
|
<message id="Lms.Explore.Release.disc">唱片 {1}</message>
|
||||||
|
|
||||||
<!--Explore:TrackList-->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--Explore:TrackLists-->
|
<!--Explore:TrackLists-->
|
||||||
|
|
||||||
|
|
||||||
@@ -174,7 +172,7 @@
|
|||||||
|
|
||||||
<!--Playqueue-->
|
<!--Playqueue-->
|
||||||
<message id="Lms.PlayQueue.clear">清除</message>
|
<message id="Lms.PlayQueue.clear">清除</message>
|
||||||
<message id="Lms.PlayQueue.nb-tracks">{1} 音轨</message>
|
|
||||||
<message id="Lms.PlayQueue.nb-tracks-added">
|
<message id="Lms.PlayQueue.nb-tracks-added">
|
||||||
<plural case="0">添加 {1} 音轨</plural>
|
<plural case="0">添加 {1} 音轨</plural>
|
||||||
<plural case="1">已添加 {1} 音轨</plural>
|
<plural case="1">已添加 {1} 音轨</plural>
|
||||||
@@ -183,6 +181,7 @@
|
|||||||
<message id="Lms.PlayQueue.playqueue-full">播放队列已满!</message>
|
<message id="Lms.PlayQueue.playqueue-full">播放队列已满!</message>
|
||||||
<message id="Lms.PlayQueue.radio-mode">广播模式</message>
|
<message id="Lms.PlayQueue.radio-mode">广播模式</message>
|
||||||
<message id="Lms.PlayQueue.repeat">循环播放</message>
|
<message id="Lms.PlayQueue.repeat">循环播放</message>
|
||||||
|
|
||||||
<message id="Lms.PlayQueue.shuffle">随机播放</message>
|
<message id="Lms.PlayQueue.shuffle">随机播放</message>
|
||||||
|
|
||||||
<!--PlayHistory-->
|
<!--PlayHistory-->
|
||||||
@@ -203,6 +202,11 @@
|
|||||||
<message id="Lms.Settings.replaygain-mode.release">专辑</message>
|
<message id="Lms.Settings.replaygain-mode.release">专辑</message>
|
||||||
<message id="Lms.Settings.replaygain-preamp">ReplayGain 放大器</message>
|
<message id="Lms.Settings.replaygain-preamp">ReplayGain 放大器</message>
|
||||||
<message id="Lms.Settings.replaygain-preamp-no-rg-info">ReplayGain 放大器 (如果没有信息)</message>
|
<message id="Lms.Settings.replaygain-preamp-no-rg-info">ReplayGain 放大器 (如果没有信息)</message>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<message id="Lms.Settings.subsonic-artist-list-mode">歌手列表模式</message>
|
<message id="Lms.Settings.subsonic-artist-list-mode">歌手列表模式</message>
|
||||||
<message id="Lms.Settings.subsonic-artist-list-mode.all-artists">所有歌手</message>
|
<message id="Lms.Settings.subsonic-artist-list-mode.all-artists">所有歌手</message>
|
||||||
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">专辑歌手</message>
|
<message id="Lms.Settings.subsonic-artist-list-mode.release-artists">专辑歌手</message>
|
||||||
|
|||||||
+56
-7
@@ -6,22 +6,26 @@
|
|||||||
<message id="Lms.PlayQueue.template.shuffle-btn"><i class="fa fa-fw fa-random"></i></message>
|
<message id="Lms.PlayQueue.template.shuffle-btn"><i class="fa fa-fw fa-random"></i></message>
|
||||||
<message id="Lms.PlayQueue.template.repeat-btn"><i class="fa fa-fw fa-repeat"></i></message>
|
<message id="Lms.PlayQueue.template.repeat-btn"><i class="fa fa-fw fa-repeat"></i></message>
|
||||||
<message id="Lms.PlayQueue.template.radio-btn"><i class="fa fa-fw fa-rss"></i></message>
|
<message id="Lms.PlayQueue.template.radio-btn"><i class="fa fa-fw fa-rss"></i></message>
|
||||||
|
<message id="Lms.PlayQueue.template.save-btn"><i class="fa fa-fw fa-save"></i></message>
|
||||||
|
|
||||||
<message id="Lms.PlayQueue.template">
|
<message id="Lms.PlayQueue.template">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="d-inline-block me-2" title="${tr:Lms.PlayQueue.clear}">
|
|
||||||
${clear-btn class="btn btn-secondary"}
|
|
||||||
</div>
|
|
||||||
<div class="d-inline-block me-2" title="${tr:Lms.PlayQueue.shuffle}">
|
|
||||||
${shuffle-btn class="btn btn-secondary"}
|
|
||||||
</div>
|
|
||||||
<div class="btn-group me-2">
|
<div class="btn-group me-2">
|
||||||
${repeat-btn class="btn-check"}
|
${repeat-btn class="btn-check"}
|
||||||
<label class="btn btn-outline-info" title="${tr:Lms.PlayQueue.repeat}" for="${id:repeat-btn}">${tr:Lms.PlayQueue.template.repeat-btn}</label>
|
<label class="btn btn-outline-info" title="${tr:Lms.PlayQueue.repeat}" for="${id:repeat-btn}">${tr:Lms.PlayQueue.template.repeat-btn}</label>
|
||||||
${radio-btn class="btn-check"}
|
${radio-btn class="btn-check"}
|
||||||
<label class="btn btn-outline-info" title="${tr:Lms.PlayQueue.radio-mode}" for="${id:radio-btn}">${tr:Lms.PlayQueue.template.radio-btn}</label>
|
<label class="btn btn-outline-info" title="${tr:Lms.PlayQueue.radio-mode}" for="${id:radio-btn}">${tr:Lms.PlayQueue.template.radio-btn}</label>
|
||||||
</div>
|
</div>
|
||||||
${nb-tracks}
|
<div class="d-inline-block me-2" title="${tr:Lms.save}">
|
||||||
|
${save-btn class="btn btn-secondary"}
|
||||||
|
</div>
|
||||||
|
<div class="d-inline-block me-2" title="${tr:Lms.PlayQueue.shuffle}">
|
||||||
|
${shuffle-btn class="btn btn-secondary"}
|
||||||
|
</div>
|
||||||
|
<div class="d-inline-block me-2" title="${tr:Lms.PlayQueue.clear}">
|
||||||
|
${clear-btn class="btn btn-warning"}
|
||||||
|
</div>
|
||||||
|
${track-count}
|
||||||
</div>
|
</div>
|
||||||
${entries}
|
${entries}
|
||||||
</message>
|
</message>
|
||||||
@@ -56,4 +60,49 @@
|
|||||||
${artist}
|
${artist}
|
||||||
</message>
|
</message>
|
||||||
|
|
||||||
|
<message id="Lms.PlayQueue.template.save-as-tracklist">
|
||||||
|
<div class="modal fade" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">${tr:Lms.save}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form>
|
||||||
|
<div class="mb-2">
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
${create-tracklist-btn class="form-check-input" name="action-type"}
|
||||||
|
<label class="form-check-label" for="${id:create-tracklist-btn}">${tr:Lms.PlayQueue.create-tracklist}</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
${replace-tracklist-btn class="form-check-input" name="action-type"}
|
||||||
|
<label class="form-check-label" for="${id:replace-tracklist-btn}">${tr:Lms.PlayQueue.replace-tracklist}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
${contents}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
${save-btn class="btn btn-primary me-1"}${cancel-btn class="btn btn-secondary"}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</message>
|
||||||
|
|
||||||
|
<message id="Lms.PlayQueue.template.save-as-tracklist.create-tracklist">
|
||||||
|
${name class="form-control"}
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
${name-info}
|
||||||
|
</div>
|
||||||
|
</message>
|
||||||
|
|
||||||
|
<message id="Lms.PlayQueue.template.save-as-tracklist.replace-tracklist">
|
||||||
|
${name class="form-control"}
|
||||||
|
<div class="invalid-feedback">
|
||||||
|
${name-info}
|
||||||
|
</div>
|
||||||
|
</message>
|
||||||
|
|
||||||
</messages>
|
</messages>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ createQuery(Session& session, const Track::FindParameters& params)
|
|||||||
{
|
{
|
||||||
session.checkSharedLocked();
|
session.checkSharedLocked();
|
||||||
|
|
||||||
auto query {session.getDboSession().query<TrackId>("SELECT DISTINCT t.id from track t")};
|
auto query {session.getDboSession().query<TrackId>(params.distinct ? "SELECT DISTINCT t.id FROM track t" : "SELECT t.id FROM track t")};
|
||||||
|
|
||||||
for (std::string_view keyword : params.keywords)
|
for (std::string_view keyword : params.keywords)
|
||||||
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
|
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ class Track : public Object<Track, TrackId>
|
|||||||
bool nonRelease {}; // only tracks that do not belong to a release
|
bool nonRelease {}; // only tracks that do not belong to a release
|
||||||
ReleaseId release; // matching this release
|
ReleaseId release; // matching this release
|
||||||
TrackListId trackList; // matching this trackList
|
TrackListId trackList; // matching this trackList
|
||||||
|
bool distinct {true};
|
||||||
|
|
||||||
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
FindParameters& setClusters(const std::vector<ClusterId>& _clusters) { clusters = _clusters; return *this; }
|
||||||
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
FindParameters& setKeywords(const std::vector<std::string_view>& _keywords) { keywords = _keywords; return *this; }
|
||||||
@@ -82,6 +83,7 @@ class Track : public Object<Track, TrackId>
|
|||||||
FindParameters& setNonRelease(bool _nonRelease) { nonRelease = _nonRelease; return *this; }
|
FindParameters& setNonRelease(bool _nonRelease) { nonRelease = _nonRelease; return *this; }
|
||||||
FindParameters& setRelease(ReleaseId _release) { release = _release; return *this; }
|
FindParameters& setRelease(ReleaseId _release) { release = _release; return *this; }
|
||||||
FindParameters& setTrackList(TrackListId _trackList) { trackList = _trackList; return *this; }
|
FindParameters& setTrackList(TrackListId _trackList) { trackList = _trackList; return *this; }
|
||||||
|
FindParameters& setDistinct(bool _distinct) { distinct = _distinct; return *this; }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PathResult
|
struct PathResult
|
||||||
|
|||||||
+209
-2
@@ -20,7 +20,13 @@
|
|||||||
#include "PlayQueue.hpp"
|
#include "PlayQueue.hpp"
|
||||||
|
|
||||||
#include <Wt/WCheckBox.h>
|
#include <Wt/WCheckBox.h>
|
||||||
|
#include <Wt/WComboBox.h>
|
||||||
|
#include <Wt/WFormModel.h>
|
||||||
|
#include <Wt/WLineEdit.h>
|
||||||
|
#include <Wt/WRadioButton.h>
|
||||||
#include <Wt/WPushButton.h>
|
#include <Wt/WPushButton.h>
|
||||||
|
#include <Wt/WStackedWidget.h>
|
||||||
|
#include <Wt/WTemplateFormView.h>
|
||||||
|
|
||||||
#include "services/database/Cluster.hpp"
|
#include "services/database/Cluster.hpp"
|
||||||
#include "services/database/Release.hpp"
|
#include "services/database/Release.hpp"
|
||||||
@@ -36,13 +42,79 @@
|
|||||||
#include "utils/String.hpp"
|
#include "utils/String.hpp"
|
||||||
|
|
||||||
#include "common/InfiniteScrollingContainer.hpp"
|
#include "common/InfiniteScrollingContainer.hpp"
|
||||||
|
#include "common/MandatoryValidator.hpp"
|
||||||
|
#include "common/ValueStringModel.hpp"
|
||||||
#include "resource/DownloadResource.hpp"
|
#include "resource/DownloadResource.hpp"
|
||||||
#include "LmsApplication.hpp"
|
#include "LmsApplication.hpp"
|
||||||
#include "MediaPlayer.hpp"
|
#include "MediaPlayer.hpp"
|
||||||
|
#include "ModalManager.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
namespace UserInterface {
|
namespace UserInterface {
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class CreateTrackListModel : public Wt::WFormModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static inline const Field NameField {"name"};
|
||||||
|
|
||||||
|
CreateTrackListModel()
|
||||||
|
{
|
||||||
|
addField(NameField);
|
||||||
|
setValidator(NameField, createMandatoryValidator());
|
||||||
|
}
|
||||||
|
|
||||||
|
Wt::WString getName() const { return valueText(NameField); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReplaceTrackListModel : public Wt::WFormModel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static inline const Field NameField {"name"};
|
||||||
|
using TrackListModel = ValueStringModel<Database::TrackListId>;
|
||||||
|
|
||||||
|
ReplaceTrackListModel()
|
||||||
|
{
|
||||||
|
addField(NameField);
|
||||||
|
setValidator(NameField, createMandatoryValidator());
|
||||||
|
}
|
||||||
|
|
||||||
|
Database::TrackListId getTrackListId() const
|
||||||
|
{
|
||||||
|
auto row {trackListModel->getRowFromString(valueText(NameField))};
|
||||||
|
return trackListModel->getValue(*row);
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
std::shared_ptr<TrackListModel>
|
||||||
|
createTrackListModel()
|
||||||
|
{
|
||||||
|
using namespace Database;
|
||||||
|
|
||||||
|
auto model {std::make_shared<TrackListModel>()};
|
||||||
|
|
||||||
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
|
TrackList::FindParameters params;
|
||||||
|
params.setType(TrackListType::Playlist);
|
||||||
|
params.setUser(LmsApp->getUserId());
|
||||||
|
params.setSortMethod(TrackListSortMethod::Name);
|
||||||
|
|
||||||
|
auto tracklists {TrackList::find(LmsApp->getDbSession(), params)};
|
||||||
|
for (const TrackListId trackListId : tracklists.results)
|
||||||
|
{
|
||||||
|
const TrackList::pointer trackList {TrackList::find(LmsApp->getDbSession(), trackListId)};
|
||||||
|
model->add(Wt::WString::fromUTF8(std::string {trackList->getName()}), trackListId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<TrackListModel> trackListModel {createTrackListModel()};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
PlayQueue::PlayQueue()
|
PlayQueue::PlayQueue()
|
||||||
: Template {Wt::WString::tr("Lms.PlayQueue.template")}
|
: Template {Wt::WString::tr("Lms.PlayQueue.template")}
|
||||||
{
|
{
|
||||||
@@ -55,6 +127,12 @@ PlayQueue::PlayQueue()
|
|||||||
clearTracks();
|
clearTracks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Wt::WPushButton* saveBtn {bindNew<Wt::WPushButton>("save-btn", Wt::WString::tr("Lms.PlayQueue.template.save-btn"), Wt::TextFormat::XHTML)};
|
||||||
|
saveBtn->clicked().connect([=]
|
||||||
|
{
|
||||||
|
saveAsTrackList();
|
||||||
|
});
|
||||||
|
|
||||||
_entriesContainer = bindNew<InfiniteScrollingContainer>("entries");
|
_entriesContainer = bindNew<InfiniteScrollingContainer>("entries");
|
||||||
_entriesContainer->onRequestElements.connect([this]
|
_entriesContainer->onRequestElements.connect([this]
|
||||||
{
|
{
|
||||||
@@ -108,7 +186,7 @@ PlayQueue::PlayQueue()
|
|||||||
_radioBtn->setCheckState(Wt::CheckState::Checked);
|
_radioBtn->setCheckState(Wt::CheckState::Checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
_nbTracks = bindNew<Wt::WText>("nb-tracks");
|
_nbTracks = bindNew<Wt::WText>("track-count");
|
||||||
|
|
||||||
LmsApp->preQuit().connect([=]
|
LmsApp->preQuit().connect([=]
|
||||||
{
|
{
|
||||||
@@ -287,7 +365,8 @@ PlayQueue::updateInfo()
|
|||||||
{
|
{
|
||||||
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
auto transaction {LmsApp->getDbSession().createSharedTransaction()};
|
||||||
|
|
||||||
_nbTracks->setText(Wt::WString::tr("Lms.PlayQueue.nb-tracks").arg(static_cast<unsigned>(getTrackList()->getCount())));
|
const auto trackCount {getTrackList()->getCount()};
|
||||||
|
_nbTracks->setText(Wt::WString::trn("Lms.track-count", trackCount).arg(trackCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -541,5 +620,133 @@ PlayQueue::getReplayGain(std::size_t pos, const Database::Track::pointer& track)
|
|||||||
return settings->replayGain.preAmpGainIfNoInfo;
|
return settings->replayGain.preAmpGainIfNoInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PlayQueue::saveAsTrackList()
|
||||||
|
{
|
||||||
|
auto modal {std::make_unique<Template>(Wt::WString::tr("Lms.PlayQueue.template.save-as-tracklist"))};
|
||||||
|
modal->addFunction("id", &Wt::WTemplate::Functions::id);
|
||||||
|
modal->addFunction("tr", &Wt::WTemplate::Functions::tr);
|
||||||
|
Wt::WWidget* modalPtr {modal.get()};
|
||||||
|
|
||||||
|
auto* cancelBtn {modal->bindNew<Wt::WPushButton>("cancel-btn", Wt::WString::tr("Lms.cancel"))};
|
||||||
|
cancelBtn->clicked().connect([=]
|
||||||
|
{
|
||||||
|
LmsApp->getModalManager().dispose(modalPtr);
|
||||||
|
});
|
||||||
|
|
||||||
|
Wt::WStackedWidget* contentStack {modal->bindNew<Wt::WStackedWidget>("contents")};
|
||||||
|
|
||||||
|
// Create/Replace selector
|
||||||
|
enum Index : int
|
||||||
|
{
|
||||||
|
CreateTrackList = 0,
|
||||||
|
ReplaceTrackList = 1,
|
||||||
|
};
|
||||||
|
{
|
||||||
|
modal->bindNew<Wt::WRadioButton>("replace-tracklist-btn")
|
||||||
|
->clicked().connect([=]
|
||||||
|
{
|
||||||
|
contentStack->setCurrentIndex(Index::ReplaceTrackList);
|
||||||
|
});
|
||||||
|
|
||||||
|
Wt::WRadioButton* createTrackListBtn {modal->bindNew<Wt::WRadioButton>("create-tracklist-btn")};
|
||||||
|
createTrackListBtn->setChecked();
|
||||||
|
createTrackListBtn->clicked().connect([=]
|
||||||
|
{
|
||||||
|
contentStack->setCurrentIndex(Index::CreateTrackList);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Create TrackList
|
||||||
|
Wt::WTemplateFormView* createTrackList {contentStack->addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.PlayQueue.template.save-as-tracklist.create-tracklist"))};
|
||||||
|
auto createTrackListModel {std::make_shared<CreateTrackListModel>()};
|
||||||
|
createTrackList->setFormWidget(CreateTrackListModel::NameField, std::make_unique<Wt::WLineEdit>());
|
||||||
|
createTrackList->updateView(createTrackListModel.get());
|
||||||
|
|
||||||
|
// Replace TrackList
|
||||||
|
Wt::WTemplateFormView* replaceTrackList {contentStack->addNew<Wt::WTemplateFormView>(Wt::WString::tr("Lms.PlayQueue.template.save-as-tracklist.replace-tracklist"))};
|
||||||
|
auto replaceTrackListModel {std::make_shared<ReplaceTrackListModel>()};
|
||||||
|
{
|
||||||
|
auto name {std::make_unique<Wt::WComboBox>()};
|
||||||
|
name->setModel(replaceTrackListModel->trackListModel);
|
||||||
|
replaceTrackList->setFormWidget(ReplaceTrackListModel::NameField, std::move(name));
|
||||||
|
}
|
||||||
|
replaceTrackList->updateView(replaceTrackListModel.get());
|
||||||
|
|
||||||
|
auto* saveBtn {modal->bindNew<Wt::WPushButton>("save-btn", Wt::WString::tr("Lms.save"))};
|
||||||
|
saveBtn->clicked().connect([=]
|
||||||
|
{
|
||||||
|
bool success {};
|
||||||
|
switch (contentStack->currentIndex())
|
||||||
|
{
|
||||||
|
case Index::CreateTrackList:
|
||||||
|
createTrackList->updateModel(createTrackListModel.get());
|
||||||
|
if (createTrackListModel->validate())
|
||||||
|
{
|
||||||
|
exportToNewTrackList(createTrackListModel->getName());
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
createTrackList->updateView(createTrackListModel.get());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Index::ReplaceTrackList:
|
||||||
|
replaceTrackList->updateModel(replaceTrackListModel.get());
|
||||||
|
if (replaceTrackListModel->validate())
|
||||||
|
{
|
||||||
|
exportToTrackList(replaceTrackListModel->getTrackListId());
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
replaceTrackList->updateView(replaceTrackListModel.get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
LmsApp->getModalManager().dispose(modalPtr);
|
||||||
|
});
|
||||||
|
|
||||||
|
LmsApp->getModalManager().show(std::move(modal));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PlayQueue::exportToNewTrackList(const Wt::WString& name)
|
||||||
|
{
|
||||||
|
using namespace Database;
|
||||||
|
|
||||||
|
Database::TrackListId trackListId;
|
||||||
|
|
||||||
|
{
|
||||||
|
Session& session {LmsApp->getDbSession()};
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
TrackList::pointer trackList {session.create<TrackList>(name.toUTF8(), TrackListType::Playlist, false, LmsApp->getUser())};
|
||||||
|
trackListId = trackList->getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
exportToTrackList(trackListId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PlayQueue::exportToTrackList(Database::TrackListId trackListId)
|
||||||
|
{
|
||||||
|
using namespace Database;
|
||||||
|
|
||||||
|
Session& session {LmsApp->getDbSession()};
|
||||||
|
auto transaction {session.createUniqueTransaction()};
|
||||||
|
|
||||||
|
TrackList::pointer trackList {TrackList::find(LmsApp->getDbSession(), trackListId)};
|
||||||
|
trackList.modify()->clear();
|
||||||
|
|
||||||
|
Track::FindParameters params;
|
||||||
|
params.setTrackList(_tracklistId);
|
||||||
|
params.setDistinct(false);
|
||||||
|
params.setSortMethod(TrackSortMethod::TrackList);
|
||||||
|
|
||||||
|
const auto tracks {Track::find(session, params)};
|
||||||
|
|
||||||
|
for (const TrackId trackId : tracks.results)
|
||||||
|
session.create<TrackListEntry>(Track::find(session, trackId), trackList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,10 @@ class PlayQueue : public Template
|
|||||||
void addRadioTrackFromSimilarity(std::shared_ptr<Similarity::Finder> similarityFinder);
|
void addRadioTrackFromSimilarity(std::shared_ptr<Similarity::Finder> similarityFinder);
|
||||||
void addRadioTrackFromClusters();
|
void addRadioTrackFromClusters();
|
||||||
std::optional<float> getReplayGain(std::size_t pos, const Database::ObjectPtr<Database::Track>& track) const;
|
std::optional<float> getReplayGain(std::size_t pos, const Database::ObjectPtr<Database::Track>& track) const;
|
||||||
|
void saveAsTrackList();
|
||||||
|
|
||||||
|
void exportToNewTrackList(const Wt::WString& name);
|
||||||
|
void exportToTrackList(Database::TrackListId trackList);
|
||||||
|
|
||||||
static inline constexpr std::size_t _capacity {1000};
|
static inline constexpr std::size_t _capacity {1000};
|
||||||
static inline constexpr std::size_t _batchSize {12};
|
static inline constexpr std::size_t _batchSize {12};
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ getTrackListTracks(Database::Session& session, Database::TrackListId trackListId
|
|||||||
params.setClusters(clusters);
|
params.setClusters(clusters);
|
||||||
params.setRange({0, maxTrackCount});
|
params.setRange({0, maxTrackCount});
|
||||||
params.setSortMethod(TrackSortMethod::TrackList);
|
params.setSortMethod(TrackSortMethod::TrackList);
|
||||||
|
params.setDistinct(false);
|
||||||
|
|
||||||
return Database::Track::find(session, params).results;
|
return Database::Track::find(session, params).results;
|
||||||
}
|
}
|
||||||
@@ -252,6 +253,5 @@ Explore::handleTracksAction(PlayQueueAction action, const std::vector<Database::
|
|||||||
tracksAction.emit(action, tracksId);
|
tracksAction.emit(action, tracksId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,8 @@ namespace UserInterface
|
|||||||
|
|
||||||
bindString("name", std::string {trackList->getName()}, Wt::TextFormat::Plain);
|
bindString("name", std::string {trackList->getName()}, Wt::TextFormat::Plain);
|
||||||
bindString("duration", Utils::durationToString(trackList->getDuration()));
|
bindString("duration", Utils::durationToString(trackList->getDuration()));
|
||||||
bindString("track-count", Wt::WString::trn("Lms.Explore.TrackList.track-count", trackList->getCount()).arg(trackList->getCount()));
|
const auto trackCount {trackList->getCount()};
|
||||||
|
bindString("track-count", Wt::WString::trn("Lms.track-count", trackCount).arg(trackCount));
|
||||||
|
|
||||||
Wt::WContainerWidget* clusterContainers {bindNew<Wt::WContainerWidget>("clusters")};
|
Wt::WContainerWidget* clusterContainers {bindNew<Wt::WContainerWidget>("clusters")};
|
||||||
{
|
{
|
||||||
@@ -189,6 +190,7 @@ namespace UserInterface
|
|||||||
params.setTrackList(_trackListId);
|
params.setTrackList(_trackListId);
|
||||||
params.setSortMethod(Database::TrackSortMethod::TrackList);
|
params.setSortMethod(Database::TrackSortMethod::TrackList);
|
||||||
params.setRange({static_cast<std::size_t>(_container->getCount()), _batchSize});
|
params.setRange({static_cast<std::size_t>(_container->getCount()), _batchSize});
|
||||||
|
params.setDistinct(false);
|
||||||
|
|
||||||
const auto trackIds {Database::Track::find(LmsApp->getDbSession(), params)};
|
const auto trackIds {Database::Track::find(LmsApp->getDbSession(), params)};
|
||||||
for (const TrackId trackId : trackIds.results)
|
for (const TrackId trackId : trackIds.results)
|
||||||
@@ -199,22 +201,5 @@ namespace UserInterface
|
|||||||
|
|
||||||
_container->setHasMore(trackIds.moreResults && _container->getCount() < _maxCount);
|
_container->setHasMore(trackIds.moreResults && _container->getCount() < _maxCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
void
|
|
||||||
Tracks::addSome()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Database::TrackId>
|
|
||||||
Tracks::getAllTracks()
|
|
||||||
{
|
|
||||||
RangeResults<TrackId> trackIds {_trackCollector.get(Range {})};
|
|
||||||
|
|
||||||
return std::move(trackIds.results);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
} // namespace UserInterface
|
} // namespace UserInterface
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user