diff --git a/Dockerfile-build-arch b/Dockerfile-build-arch
index eaecaaef..1d8ecd92 100644
--- a/Dockerfile-build-arch
+++ b/Dockerfile-build-arch
@@ -13,7 +13,7 @@ ARG BUILD_PACKAGES="\
taglib \
wt"
-RUN pacman -Syy
+RUN pacman -Syu --noconfirm
RUN pacman -S --noconfirm ${BUILD_PACKAGES}
# LMS
diff --git a/approot/messages.xml b/approot/messages.xml
index bb07bcc6..19b1a95e 100644
--- a/approot/messages.xml
+++ b/approot/messages.xml
@@ -104,7 +104,7 @@
User already exists!
New user
New user created!
-Edit user '{1}'
+User '{1}'
User updated!
@@ -133,6 +133,7 @@
Recently modified
Recently played
Albums
+Search
Star
Starred
Playlists
@@ -187,7 +188,6 @@
Play History
-
Audio
These audio settings are local to your browser!
@@ -208,6 +208,7 @@
Internal
ListenBrainz
ListenBrainz token
+Settings
Artist list mode
All artists
Album artists
diff --git a/approot/messages_fr.xml b/approot/messages_fr.xml
index 8d6a1b38..571e40c4 100644
--- a/approot/messages_fr.xml
+++ b/approot/messages_fr.xml
@@ -133,6 +133,7 @@
Modifiés récemment
Joués récemment
Albums
+Rechercher
Ajouter aux favoris
Favoris
Playlists
@@ -207,6 +208,7 @@
Interne
ListenBrainz
Jeton ListenBrainz
+Paramètres
Mode de listage des artistes
Tous les artistes
Tous les artistes d'album
diff --git a/approot/messages_it.xml b/approot/messages_it.xml
index 4a254842..9dc877b8 100644
--- a/approot/messages_it.xml
+++ b/approot/messages_it.xml
@@ -133,9 +133,9 @@
Riprodotti di recente
Album
+Ricerca
Aggiungi ai preferiti
Preferiti
-
Tracce
Tipo
Rimuovi dai preferiti
@@ -207,6 +207,7 @@
+
Modalità di elencazione artisti
Tutti gli artisti
Artisti album
diff --git a/approot/messages_zh.xml b/approot/messages_zh.xml
index 7738c0f6..cdfb1a8d 100644
--- a/approot/messages_zh.xml
+++ b/approot/messages_zh.xml
@@ -130,6 +130,7 @@
最近更改
最近播放
专辑
+搜索
收藏
已收藏
播放列表
@@ -201,6 +202,7 @@
+
歌手列表模式
所有歌手
专辑歌手
diff --git a/conf/lms.conf b/conf/lms.conf
index ca7dae59..5729eaa9 100644
--- a/conf/lms.conf
+++ b/conf/lms.conf
@@ -80,3 +80,6 @@ cover-preferred-file-names = ("cover", "front" );
# Set to true if you want to hide duplicate tracks
scanner-skip-duplicate-recording-mbid = false;
+
+# Scanner read style for metadata, maybe be 'fast', 'average' or 'accurate'
+scanner-parser-read-style = "accurate";
diff --git a/docroot/js/mediaplayer.js b/docroot/js/mediaplayer.js
index f9e81d54..32807cc0 100644
--- a/docroot/js/mediaplayer.js
+++ b/docroot/js/mediaplayer.js
@@ -16,58 +16,112 @@ const Mode = {
Object.freeze(Mode);
LMS.mediaplayer = function () {
+ let _root = {};
+ let _elems = {};
+ let _offset = 0;
+ let _trackId = null;
+ let _duration = 0;
+ let _audioNativeSrc;
+ let _audioTranscodeSrc;
+ let _settings = {};
+ let _playedDuration = 0;
+ let _lastStartPlaying = null;
+ let _audioIsInit = false;
+ let _pendingTrackParameters = null;
+ let _gainNode = null;
+ let _audioCtx = null;
- var _root = {};
- var _elems = {};
- var _offset = 0;
- var _trackId = null;
- var _duration = 0;
- var _audioNativeSrc;
- var _audioTranscodeSrc;
- var _settings = {};
- var _audioCtx = new (window.AudioContext || window.webkitAudioContext)();
- var _gainNode = _audioCtx.createGain();
- var _playedDuration = 0;
- var _lastStartPlaying = null;
+ let _unlock = function() {
+ document.removeEventListener("touchstart", _unlock);
+ document.removeEventListener("touchend", _unlock);
+ document.removeEventListener("click", _unlock);
+ _initAudioCtx();
+ };
- var _updateControls = function() {
+ document.addEventListener("touchstart", _unlock);
+ document.addEventListener("touchend", _unlock);
+ document.addEventListener("click", _unlock);
+
+ let _initAudioCtx = function() {
+ if (_audioIsInit) {
+ _audioCtx.resume(); // not sure of this
+ return;
+ }
+
+ _audioIsInit = true;
+
+ _audioCtx = new (window.AudioContext || window.webkitAudioContext)();
+ _gainNode = _audioCtx.createGain();
+ source = _audioCtx.createMediaElementSource(_elems.audio);
+ source.connect(_gainNode);
+ _gainNode.connect(_audioCtx.destination);
+ _audioCtx.resume(); // not sure of this
+
+ if ("mediaSession" in navigator) {
+ navigator.mediaSession.setActionHandler("play", function() {
+ _playPause();
+ });
+ navigator.mediaSession.setActionHandler("pause", function() {
+ _playPause();
+ });
+ navigator.mediaSession.setActionHandler("previoustrack", function() {
+ _playPrevious();
+ });
+ navigator.mediaSession.setActionHandler("nexttrack", function() {
+ _playNext();
+ });
+ }
+
+ if (_pendingTrackParameters != null) {
+ _applyAudioTrackParameters(_pendingTrackParameters);
+ _pendingTrackParameters = null;
+ }
+ }
+
+ let _updateControls = function() {
const pauseClass = "fa-pause";
const playClass = "fa-play";
if (_elems.audio.paused) {
_elems.playpause.firstElementChild.classList.remove(pauseClass);
_elems.playpause.firstElementChild.classList.add(playClass);
+ if ("mediaSession" in navigator) {
+ navigator.mediaSession.playbackState = "paused";
+ }
}
else {
_elems.playpause.firstElementChild.classList.remove(playClass);
_elems.playpause.firstElementChild.classList.add(pauseClass);
+ if ("mediaSession" in navigator) {
+ navigator.mediaSession.playbackState = "playing";
+ }
}
}
- var _startTimer = function() {
+ let _startTimer = function() {
if (_lastStartPlaying == null)
Wt.emit(_root, "scrobbleListenNow", _trackId);
_lastStartPlaying = Date.now();
}
- var _stopTimer = function() {
+ let _stopTimer = function() {
if (_lastStartPlaying != null) {
_playedDuration += Date.now() - _lastStartPlaying;
}
}
- var _resetTimer = function() {
+ let _resetTimer = function() {
if (_lastStartPlaying != null)
Wt.emit(_root, "scrobbleListenFinished", _trackId, _playedDuration);
_playedDuration = 0;
_lastStartPlaying = null;
}
- var _durationToString = function (duration) {
- var minutes = parseInt(duration / 60, 10);
- var seconds = parseInt(duration, 10) % 60;
+ let _durationToString = function (duration) {
+ let minutes = parseInt(duration / 60, 10);
+ let seconds = parseInt(duration, 10) % 60;
- var res = "";
+ let res = "";
res += minutes + ":";
res += (seconds < 10 ? "0" + seconds : seconds);
@@ -75,21 +129,15 @@ LMS.mediaplayer = function () {
return res;
}
- var _playTrack = function() {
- var playPromise = _elems.audio.play();
-
- if (playPromise !== undefined) {
- playPromise.then(_ => {
- // Automatic playback started
- })
- .catch(error => {
- // Auto-play was prevented
- });
- }
+ let _playTrack = function() {
+ _elems.audio.play()
+ .then(_ => {})
+ .catch(error => { console.log("Cannot play audio: " + error); });
}
- var _playPause = function() {
- _audioCtx.resume();
+ let _playPause = function() {
+ _initAudioCtx();
+
if (_elems.audio.paused && _elems.audio.children.length > 0) {
_playTrack();
}
@@ -97,25 +145,17 @@ LMS.mediaplayer = function () {
_elems.audio.pause();
}
- var _playPrevious = function() {
- _audioCtx.resume();
- _requestPreviousTrack();
- }
-
- var _playNext = function() {
- _audioCtx.resume();
- _requestNextTrack();
- }
-
- var _requestPreviousTrack = function() {
+ let _playPrevious = function() {
+ _initAudioCtx();
Wt.emit(_root, "playPrevious");
}
- var _requestNextTrack = function() {
+ let _playNext = function() {
+ _initAudioCtx();
Wt.emit(_root, "playNext");
}
- var _initVolume = function() {
+ let _initVolume = function() {
if (typeof(Storage) !== "undefined" && localStorage.volume) {
_elems.volumeslider.value = Number(localStorage.volume);
}
@@ -123,7 +163,7 @@ LMS.mediaplayer = function () {
_setVolume(_elems.volumeslider.value);
}
- var _initDefaultSettings = function(defaultSettings) {
+ let _initDefaultSettings = function(defaultSettings) {
if (typeof(Storage) !== "undefined" && localStorage.settings) {
_settings = Object.assign(defaultSettings, JSON.parse(localStorage.settings));
}
@@ -134,7 +174,7 @@ LMS.mediaplayer = function () {
Wt.emit(_root, "settingsLoaded", JSON.stringify(_settings));
}
- var _setVolume = function(volume) {
+ let _setVolume = function(volume) {
_elems.lastvolume = _elems.audio.volume;
_elems.audio.volume = volume;
@@ -162,11 +202,11 @@ LMS.mediaplayer = function () {
}
}
- var _setReplayGain = function (replayGain) {
+ let _setReplayGain = function (replayGain) {
_gainNode.gain.value = Math.pow(10, (_settings.replayGain.preAmpGain + replayGain) / 20);
}
- var init = function(root, defaultSettings) {
+ let init = function(root, defaultSettings) {
_root = root;
_elems.audio = document.getElementById("lms-mp-audio");
@@ -181,10 +221,6 @@ LMS.mediaplayer = function () {
_elems.volumeslider = document.getElementById("lms-mp-volume-slider");
_elems.transcodingActive = document.getElementById("lms-transcoding-active");
- var source = _audioCtx.createMediaElementSource(_elems.audio);
- source.connect(_gainNode);
- _gainNode.connect(_audioCtx.destination);
-
_elems.playpause.addEventListener("click", function() {
_playPause();
});
@@ -196,7 +232,7 @@ LMS.mediaplayer = function () {
_playNext();
});
_elems.seek.addEventListener("change", function() {
- _audioCtx.resume();
+ _initAudioCtx();
let mode = _getAudioMode();
if (!mode)
return;
@@ -285,30 +321,21 @@ LMS.mediaplayer = function () {
event.preventDefault();
});
- if ('mediaSession' in navigator) {
- navigator.mediaSession.setActionHandler("previoustrack", function() {
- _requestPreviousTrack();
- });
- navigator.mediaSession.setActionHandler("nexttrack", function() {
- _requestNextTrack();
- });
- }
-
}
- var _removeAudioSources = function() {
+ let _removeAudioSources = function() {
while ( _elems.audio.lastElementChild) {
_elems.audio.removeChild( _elems.audio.lastElementChild);
}
}
- var _addAudioSource = function(audioSrc) {
+ let _addAudioSource = function(audioSrc) {
let source = document.createElement('source');
source.src = audioSrc;
_elems.audio.appendChild(source);
}
- var _getAudioMode = function() {
+ let _getAudioMode = function() {
if (_elems.audio.currentSrc) {
if (_elems.audio.currentSrc.includes("format"))
return Mode.Transcode;
@@ -319,7 +346,7 @@ LMS.mediaplayer = function () {
return undefined;
}
- var loadTrack = function(params, autoplay) {
+ let loadTrack = function(params, autoplay) {
_stopTimer();
_resetTimer();
@@ -343,15 +370,24 @@ LMS.mediaplayer = function () {
}
_elems.audio.load();
- _setReplayGain(params.replayGain);
-
_elems.curtime.innerHTML = _durationToString(_offset);
_elems.duration.innerHTML = _durationToString(_duration);
+ if (!_audioIsInit) {
+ _pendingTrackParameters = params;
+ return;
+ }
+
+ _applyAudioTrackParameters(params);
+
if (autoplay && _audioCtx.state == "running")
_playTrack();
+ }
- if ('mediaSession' in navigator) {
+ let _applyAudioTrackParameters = function(params)
+ {
+ _setReplayGain(params.replayGain);
+ if ("mediaSession" in navigator) {
navigator.mediaSession.metadata = new MediaMetadata({
title: params.title,
artist: params.artist,
@@ -361,11 +397,11 @@ LMS.mediaplayer = function () {
}
}
- var stop = function() {
+ let stop = function() {
_elems.audio.pause();
}
- var setSettings = function(settings) {
+ let setSettings = function(settings) {
_settings = settings;
if (typeof(Storage) !== "undefined") {
diff --git a/src/libs/av/impl/AudioFile.cpp b/src/libs/av/impl/AudioFile.cpp
index 7aead667..a90636ef 100644
--- a/src/libs/av/impl/AudioFile.cpp
+++ b/src/libs/av/impl/AudioFile.cpp
@@ -178,7 +178,7 @@ AudioFile::getBestStream() const
}
bool
-AudioFile::hasAttachedPictures(void) const
+AudioFile::hasAttachedPictures() const
{
for (std::size_t i = 0; i < _context->nb_streams; ++i)
{
diff --git a/src/libs/av/impl/TranscodeResourceHandler.cpp b/src/libs/av/impl/TranscodeResourceHandler.cpp
index 6554999c..0f2687d3 100644
--- a/src/libs/av/impl/TranscodeResourceHandler.cpp
+++ b/src/libs/av/impl/TranscodeResourceHandler.cpp
@@ -18,32 +18,48 @@
*/
#include "TranscodeResourceHandler.hpp"
+#include "utils/Logger.hpp"
namespace Av
{
+ namespace
+ {
+ std::size_t
+ doEstimateContentLength(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters)
+ {
+ const std::size_t estimatedContentLength {transcodeParameters.bitrate / 8 * static_cast(std::chrono::duration_cast(inputFileParameters.duration).count()) / 1000};
+ return estimatedContentLength;
+ }
+ }
std::unique_ptr
- createTranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters)
+ createTranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters, bool estimateContentLength)
{
- return std::make_unique(trackPath, parameters);
+ return std::make_unique(inputFileParameters, transcodeParameters, estimateContentLength);
}
// TODO set some nice HTTP return code
- TranscodeResourceHandler::TranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters)
- : _transcoder {trackPath, parameters}
+ TranscodeResourceHandler::TranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters, bool estimateContentLength)
+ : _estimatedContentLength {estimateContentLength ? std::make_optional(doEstimateContentLength(inputFileParameters, transcodeParameters)) : std::nullopt}
+ , _transcoder {inputFileParameters, transcodeParameters}
{
+ if (_estimatedContentLength)
+ LMS_LOG(TRANSCODE, DEBUG) << "Estimated content length = " << *_estimatedContentLength;
}
Wt::Http::ResponseContinuation*
TranscodeResourceHandler::processRequest(const Wt::Http::Request& /*request*/, Wt::Http::Response& response)
{
+ if (_estimatedContentLength)
+ response.setContentLength(*_estimatedContentLength);
response.setMimeType(_transcoder.getOutputMimeType());
- if (_nbBytesReady > 0)
+ if (_bytesReadyCount > 0)
{
- response.out().write(reinterpret_cast(&_buffer[0]), _nbBytesReady);
- _nbBytesReady = 0;
+ response.out().write(reinterpret_cast(&_buffer[0]), _bytesReadyCount);
+ _bytesReadyCount = 0;
+ _totalServedByteCount += _bytesReadyCount;
}
if (!_transcoder.finished())
@@ -52,13 +68,30 @@ namespace Av
continuation->waitForMoreData();
_transcoder.asyncRead(_buffer.data(), _buffer.size(), [=](std::size_t nbBytesRead)
{
- assert(_nbBytesReady == 0);
- _nbBytesReady = nbBytesRead;
+ assert(_bytesReadyCount == 0);
+ _bytesReadyCount = nbBytesRead;
continuation->haveMoreData();
});
return continuation;
}
+ else
+ {
+ // pad with 0 if necessary as duration may not be accurate
+ if (_estimatedContentLength && *_estimatedContentLength > _totalServedByteCount)
+ {
+ const std::size_t padSize {*_estimatedContentLength - _totalServedByteCount};
+
+ LMS_LOG(TRANSCODE, DEBUG) << "Adding " << padSize << " padding bytes";
+
+ for (std::size_t i {}; i < padSize; ++i)
+ response.out().put(0);
+
+ _totalServedByteCount += padSize;
+ }
+
+ LMS_LOG(TRANSCODE, DEBUG) << "Transcoding finished. Total served byte count = " << _totalServedByteCount;
+ }
return {};
}
diff --git a/src/libs/av/impl/TranscodeResourceHandler.hpp b/src/libs/av/impl/TranscodeResourceHandler.hpp
index 2395a6cc..47d76f36 100644
--- a/src/libs/av/impl/TranscodeResourceHandler.hpp
+++ b/src/libs/av/impl/TranscodeResourceHandler.hpp
@@ -21,6 +21,7 @@
#include
#include
+#include
#include "av/TranscodeParameters.hpp"
#include "utils/IResourceHandler.hpp"
@@ -28,19 +29,19 @@
namespace Av
{
-
class TranscodeResourceHandler final : public IResourceHandler
{
public:
- TranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters);
+ TranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& parameters, bool estimateContentLength);
private:
Wt::Http::ResponseContinuation* processRequest(const Wt::Http::Request& request, Wt::Http::Response& reponse) override;
static constexpr std::size_t _chunkSize {32768};
+ std::optional _estimatedContentLength;
std::array _buffer;
- std::size_t _nbBytesReady {};
- const std::filesystem::path _trackPath;
+ std::size_t _bytesReadyCount {};
+ std::size_t _totalServedByteCount {};
Transcoder _transcoder;
};
}
diff --git a/src/libs/av/impl/Transcoder.cpp b/src/libs/av/impl/Transcoder.cpp
index 55b00d1b..4b828d5a 100644
--- a/src/libs/av/impl/Transcoder.cpp
+++ b/src/libs/av/impl/Transcoder.cpp
@@ -30,7 +30,7 @@
namespace Av {
-#define LOG(sev) LMS_LOG(TRANSCODE, sev) << "[" << _id << "] - "
+#define LOG(sev) LMS_LOG(TRANSCODE, sev) << "[" << _debugId << "] - "
static std::atomic globalId {};
static std::filesystem::path ffmpegPath;
@@ -43,10 +43,10 @@ Transcoder::init()
throw Exception {"File '" + ffmpegPath.string() + "' does not exist!"};
}
-Transcoder::Transcoder(const std::filesystem::path& filePath, const TranscodeParameters& parameters)
-: _id {globalId++}
-, _filePath {filePath}
-, _parameters {parameters}
+Transcoder::Transcoder(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters)
+: _debugId {globalId++}
+, _inputFileParameters {inputFileParameters}
+, _transcodeParameters {transcodeParameters}
{
start();
}
@@ -61,17 +61,17 @@ Transcoder::start()
try
{
- if (!std::filesystem::exists(_filePath))
- throw Exception {"File '" + _filePath.string() + "' does not exist!"};
- else if (!std::filesystem::is_regular_file( _filePath) )
- throw Exception {"File '" + _filePath.string() + "' is not regular!"};
+ if (!std::filesystem::exists(_inputFileParameters.trackPath))
+ throw Exception {"File '" + _inputFileParameters.trackPath.string() + "' does not exist!"};
+ else if (!std::filesystem::is_regular_file( _inputFileParameters.trackPath) )
+ throw Exception {"File '" + _inputFileParameters.trackPath.string() + "' is not regular!"};
}
catch (const std::filesystem::filesystem_error& e)
{
- throw Exception {"File error '" + _filePath.string() + "': " + e.what()};
+ throw Exception {"File error '" + _inputFileParameters.trackPath.string() + "': " + e.what()};
}
- LOG(INFO) << "Transcoding file '" << _filePath.string() << "'";
+ LOG(INFO) << "Transcoding file '" << _inputFileParameters.trackPath.string() << "'";
std::vector args;
@@ -90,22 +90,22 @@ Transcoder::start()
args.emplace_back("-ss");
std::ostringstream oss;
- oss << std::fixed << std::showpoint << std::setprecision(3) << (_parameters.offset.count() / float {1000});
+ oss << std::fixed << std::showpoint << std::setprecision(3) << (_transcodeParameters.offset.count() / float {1000});
args.emplace_back(oss.str());
}
// Input file
args.emplace_back("-i");
- args.emplace_back(_filePath.string());
+ args.emplace_back(_inputFileParameters.trackPath.string());
// Stream mapping, if set
- if (_parameters.stream)
+ if (_transcodeParameters.stream)
{
args.emplace_back("-map");
- args.emplace_back("0:" + std::to_string(*_parameters.stream));
+ args.emplace_back("0:" + std::to_string(*_transcodeParameters.stream));
}
- if (_parameters.stripMetadata)
+ if (_transcodeParameters.stripMetadata)
{
// Strip metadata
args.emplace_back("-map_metadata");
@@ -117,10 +117,10 @@ Transcoder::start()
// Output bitrates
args.emplace_back("-b:a");
- args.emplace_back(std::to_string(_parameters.bitrate));
+ args.emplace_back(std::to_string(_transcodeParameters.bitrate));
// Codecs and formats
- switch (_parameters.format)
+ switch (_transcodeParameters.format)
{
case Format::MP3:
args.emplace_back("-f");
@@ -156,10 +156,10 @@ Transcoder::start()
break;
default:
- throw Exception {"Unhandled format (" + std::to_string(static_cast(_parameters.format)) + ")"};
+ throw Exception {"Unhandled format (" + std::to_string(static_cast(_transcodeParameters.format)) + ")"};
}
- _outputMimeType = formatToMimetype(_parameters.format);
+ _outputMimeType = formatToMimetype(_transcodeParameters.format);
args.emplace_back("pipe:1");
diff --git a/src/libs/av/impl/Transcoder.hpp b/src/libs/av/impl/Transcoder.hpp
index 54ebe5e5..a4de2976 100644
--- a/src/libs/av/impl/Transcoder.hpp
+++ b/src/libs/av/impl/Transcoder.hpp
@@ -32,7 +32,7 @@ namespace Av
class Transcoder
{
public:
- Transcoder(const std::filesystem::path& file, const TranscodeParameters& parameters);
+ Transcoder(const InputFileParameters& inputFileParameters, const TranscodeParameters& transcodeParameters);
~Transcoder();
Transcoder(const Transcoder&) = delete;
@@ -49,7 +49,7 @@ namespace Av
std::size_t readSome(std::byte* buffer, std::size_t bufferSize);
const std::string& getOutputMimeType() const { return _outputMimeType; }
- const TranscodeParameters& getParameters() const { return _parameters; }
+ const TranscodeParameters& getParameters() const { return _transcodeParameters; }
bool finished() const;
@@ -58,9 +58,9 @@ namespace Av
void start();
- const std::size_t _id {};
- const std::filesystem::path _filePath;
- const TranscodeParameters _parameters;
+ const std::size_t _debugId {};
+ const InputFileParameters _inputFileParameters;
+ const TranscodeParameters _transcodeParameters;
std::unique_ptr _childProcess;
diff --git a/src/libs/av/include/av/TranscodeParameters.hpp b/src/libs/av/include/av/TranscodeParameters.hpp
index c155f6e7..f709fef8 100644
--- a/src/libs/av/include/av/TranscodeParameters.hpp
+++ b/src/libs/av/include/av/TranscodeParameters.hpp
@@ -20,12 +20,19 @@
#pragma once
#include
+#include
#include
#include "Types.hpp"
namespace Av
{
+ struct InputFileParameters
+ {
+ std::filesystem::path trackPath;
+ std::chrono::milliseconds duration;
+ };
+
struct TranscodeParameters
{
Format format;
diff --git a/src/libs/av/include/av/TranscodeResourceHandlerCreator.hpp b/src/libs/av/include/av/TranscodeResourceHandlerCreator.hpp
index 0255cfc4..8b232d8a 100644
--- a/src/libs/av/include/av/TranscodeResourceHandlerCreator.hpp
+++ b/src/libs/av/include/av/TranscodeResourceHandlerCreator.hpp
@@ -19,15 +19,15 @@
#pragma once
-#include
#include
#include "utils/IResourceHandler.hpp"
namespace Av
{
+ struct InputFileParameters;
struct TranscodeParameters;
- std::unique_ptr createTranscodeResourceHandler(const std::filesystem::path& trackPath, const TranscodeParameters& parameters);
+ std::unique_ptr createTranscodeResourceHandler(const InputFileParameters& inputFileParameters, const TranscodeParameters& parameters, bool estimateContentLength);
}
diff --git a/src/libs/metadata/CMakeLists.txt b/src/libs/metadata/CMakeLists.txt
index 90b6fd92..b2502aa3 100644
--- a/src/libs/metadata/CMakeLists.txt
+++ b/src/libs/metadata/CMakeLists.txt
@@ -5,6 +5,7 @@ endif()
add_library(lmsmetadata SHARED
impl/AvFormatParser.cpp
+ impl/Factory.cpp
impl/TagLibParser.cpp
impl/Utils.cpp
)
diff --git a/src/libs/metadata/impl/AvFormatParser.cpp b/src/libs/metadata/impl/AvFormatParser.cpp
index e5a472dd..1284ffb7 100644
--- a/src/libs/metadata/impl/AvFormatParser.cpp
+++ b/src/libs/metadata/impl/AvFormatParser.cpp
@@ -17,7 +17,7 @@
* along with LMS. If not, see .
*/
-#include "metadata/AvFormatParser.hpp"
+#include "AvFormatParser.hpp"
#include
#include
diff --git a/src/libs/metadata/include/metadata/AvFormatParser.hpp b/src/libs/metadata/impl/AvFormatParser.hpp
similarity index 100%
rename from src/libs/metadata/include/metadata/AvFormatParser.hpp
rename to src/libs/metadata/impl/AvFormatParser.hpp
diff --git a/src/libs/metadata/impl/Factory.cpp b/src/libs/metadata/impl/Factory.cpp
new file mode 100644
index 00000000..4341024e
--- /dev/null
+++ b/src/libs/metadata/impl/Factory.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 Emeric Poupon
+ *
+ * This file is part of LMS.
+ *
+ * LMS is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LMS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LMS. If not, see .
+ */
+
+#include "metadata/IParser.hpp"
+
+#include "utils/Exception.hpp"
+#include "utils/Logger.hpp"
+
+#include "AvFormatParser.hpp"
+#include "TagLibParser.hpp"
+#include "Utils.hpp"
+
+namespace MetaData
+{
+ std::unique_ptr
+ createParser(ParserType parserType, ParserReadStyle parserReadStyle)
+ {
+ switch (parserType)
+ {
+ case ParserType::TagLib:
+ LMS_LOG(METADATA, INFO) << "Creating TagLib parser with read style = " << Utils::readStyleToString(parserReadStyle);
+ return std::make_unique(parserReadStyle);
+ case ParserType::AvFormat:
+ LMS_LOG(METADATA, INFO) << "Creating AvFormat parser";
+ return std::make_unique();
+ }
+
+ throw LmsException {"Unhandled parser type"};
+ }
+}
+
diff --git a/src/libs/metadata/impl/TagLibParser.cpp b/src/libs/metadata/impl/TagLibParser.cpp
index 0e8d827c..d5c9ba6c 100644
--- a/src/libs/metadata/impl/TagLibParser.cpp
+++ b/src/libs/metadata/impl/TagLibParser.cpp
@@ -17,7 +17,7 @@
* along with LMS. If not, see .
*/
-#include "metadata/TagLibParser.hpp"
+#include "TagLibParser.hpp"
#include
#include
@@ -33,7 +33,10 @@
#include
#include
+#include "utils/IConfig.hpp"
+#include "utils/Exception.hpp"
#include "utils/Logger.hpp"
+#include "utils/Service.hpp"
#include "utils/String.hpp"
#include "Utils.hpp"
@@ -145,6 +148,26 @@ getAlbum(const TagLib::PropertyMap& properties)
return Album {std::move(albumName.front()), albumMBID.front()};
}
+static
+TagLib::AudioProperties::ReadStyle
+readStyleToTagLibReadStyle(ParserReadStyle readStyle)
+{
+ switch (readStyle)
+ {
+ case ParserReadStyle::Fast: return TagLib::AudioProperties::ReadStyle::Fast;
+ case ParserReadStyle::Average: return TagLib::AudioProperties::ReadStyle::Average;
+ case ParserReadStyle::Accurate: return TagLib::AudioProperties::ReadStyle::Accurate;
+ }
+
+ throw LmsException {"Cannot convert read style"};
+}
+
+
+TagLibParser::TagLibParser(ParserReadStyle readStyle)
+ : _readStyle {readStyleToTagLibReadStyle(readStyle)}
+{
+}
+
void
TagLibParser::processTag(Track& track, const std::string& tag, const TagLib::StringList& values, bool debug)
{
@@ -267,7 +290,7 @@ TagLibParser::parse(const std::filesystem::path& p, bool debug)
{
TagLib::FileRef f {p.string().c_str(),
true, // read audio properties
- TagLib::AudioProperties::Fast}; // TODO parametrize this
+ _readStyle};
if (f.isNull())
{
@@ -286,7 +309,7 @@ TagLibParser::parse(const std::filesystem::path& p, bool debug)
{
const TagLib::AudioProperties *properties {f.audioProperties() };
- track.duration = std::chrono::milliseconds {properties->length() * 1000};
+ track.duration = std::chrono::milliseconds {properties->lengthInMilliseconds()};
MetaData::AudioStream audioStream {static_cast(properties->bitrate() * 1000)};
track.audioStreams = {std::move(audioStream)};
diff --git a/src/libs/metadata/include/metadata/TagLibParser.hpp b/src/libs/metadata/impl/TagLibParser.hpp
similarity index 88%
rename from src/libs/metadata/include/metadata/TagLibParser.hpp
rename to src/libs/metadata/impl/TagLibParser.hpp
index ee298f8d..8990fd1c 100644
--- a/src/libs/metadata/include/metadata/TagLibParser.hpp
+++ b/src/libs/metadata/impl/TagLibParser.hpp
@@ -19,6 +19,7 @@
#pragma once
+#include
#include "metadata/IParser.hpp"
namespace TagLib
@@ -32,10 +33,14 @@ namespace MetaData
// Parse that makes use of AvFormat
class TagLibParser : public IParser
{
+ public:
+ TagLibParser(ParserReadStyle readStyle);
+
private:
std::optional