Merge branch 'develop' into features-training
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# LMS - Lightweight Music Server
|
# LMS - Lightweight Music Server
|
||||||
|
|
||||||
[](https://travis-ci.org/epoupon/lms)
|
[](https://travis-ci.org/epoupon/lms) 
|
||||||
|
|
||||||
_LMS_ is a self-hosted music streaming software: access your music collection from anywhere using a web interface!
|
_LMS_ is a self-hosted music streaming software: access your music collection from anywhere using a web interface!
|
||||||
|
|
||||||
@@ -51,10 +51,10 @@ __Note__: since _LMS_ stores hashed and salted passwords, it cannot handle the _
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### From packages
|
### Docker
|
||||||
|
_Docker_ images are available, please see detailed instructions on https://hub.docker.com/r/epoupon/lms.
|
||||||
#### Debian Buster packages
|
|
||||||
|
|
||||||
|
### Debian Buster packages
|
||||||
_Buster_ packages are provided for _amd64_ and _armhf_ architectures.
|
_Buster_ packages are provided for _amd64_ and _armhf_ architectures.
|
||||||
|
|
||||||
As root, trust the following debian package provider and add it in your list of repositories:
|
As root, trust the following debian package provider and add it in your list of repositories:
|
||||||
|
|||||||
@@ -161,6 +161,7 @@
|
|||||||
<message id="Lms.Settings.transcoding-bitrate">Transcode bitrate</message>
|
<message id="Lms.Settings.transcoding-bitrate">Transcode bitrate</message>
|
||||||
<message id="Lms.Settings.transcoding-format">Transcode format</message>
|
<message id="Lms.Settings.transcoding-format">Transcode format</message>
|
||||||
<message id="Lms.Settings.transcoding-enable">Enable transcoding</message>
|
<message id="Lms.Settings.transcoding-enable">Enable transcoding</message>
|
||||||
|
<message id="Lms.Settings.transcoding.matroska_opus">Matroska/Opus</message>
|
||||||
<message id="Lms.Settings.transcoding.mp3">MP3</message>
|
<message id="Lms.Settings.transcoding.mp3">MP3</message>
|
||||||
<message id="Lms.Settings.transcoding.ogg_opus">Ogg/Opus</message>
|
<message id="Lms.Settings.transcoding.ogg_opus">Ogg/Opus</message>
|
||||||
<message id="Lms.Settings.transcoding.ogg_vorbis">Ogg/Vorbis</message>
|
<message id="Lms.Settings.transcoding.ogg_vorbis">Ogg/Vorbis</message>
|
||||||
|
|||||||
@@ -161,6 +161,7 @@
|
|||||||
<message id="Lms.Settings.transcoding-bitrate">Bitrate du transcodage</message>
|
<message id="Lms.Settings.transcoding-bitrate">Bitrate du transcodage</message>
|
||||||
<message id="Lms.Settings.transcoding-format">Format du transcodage</message>
|
<message id="Lms.Settings.transcoding-format">Format du transcodage</message>
|
||||||
<message id="Lms.Settings.transcoding-enable">Activer le transcodage</message>
|
<message id="Lms.Settings.transcoding-enable">Activer le transcodage</message>
|
||||||
|
<message id="Lms.Settings.transcoding.matroska_opus">Matroska/Opus</message>
|
||||||
<message id="Lms.Settings.transcoding.mp3">MP3</message>
|
<message id="Lms.Settings.transcoding.mp3">MP3</message>
|
||||||
<message id="Lms.Settings.transcoding.ogg_opus">Ogg/Opus</message>
|
<message id="Lms.Settings.transcoding.ogg_opus">Ogg/Opus</message>
|
||||||
<message id="Lms.Settings.transcoding.ogg_vorbis">Ogg/Vorbis</message>
|
<message id="Lms.Settings.transcoding.ogg_vorbis">Ogg/Vorbis</message>
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.59)
|
AC_PREREQ(2.59)
|
||||||
AC_INIT(lms, 3.1.0, test@test)
|
AC_INIT(lms, 3.2.0, test@test)
|
||||||
AM_INIT_AUTOMAKE
|
AM_INIT_AUTOMAKE
|
||||||
AC_CONFIG_HEADER(src/config/config.h)
|
AC_CONFIG_HEADER(src/config/config.h)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
FROM alpine:3.10 AS build
|
||||||
|
|
||||||
|
WORKDIR /tmp/workdir
|
||||||
|
|
||||||
|
ARG MAKEFLAGS="-j2"
|
||||||
|
ARG FFMPEG_VERSION=4.1.4
|
||||||
|
ARG WT_VERSION=4.1.1
|
||||||
|
ARG IMAGEMAGICK6_VERSION=6.9.10-71
|
||||||
|
ARG PSTREAMS_VERSION=1.0.1
|
||||||
|
ARG LMS_VERSION=3.2.0
|
||||||
|
|
||||||
|
ARG PREFIX="/tmp/install"
|
||||||
|
|
||||||
|
ARG BUILD_PACKAGES=" \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
bzip2 \
|
||||||
|
pkgconfig \
|
||||||
|
coreutils \
|
||||||
|
autoconf \
|
||||||
|
automake \
|
||||||
|
libtool \
|
||||||
|
g++ \
|
||||||
|
make \
|
||||||
|
libjpeg-turbo-dev \
|
||||||
|
openjpeg-dev \
|
||||||
|
libpng-dev \
|
||||||
|
tiff-dev \
|
||||||
|
nasm \
|
||||||
|
yasm \
|
||||||
|
curl \
|
||||||
|
libogg-dev \
|
||||||
|
opus-dev \
|
||||||
|
libvorbis-dev \
|
||||||
|
lame-dev \
|
||||||
|
cmake \
|
||||||
|
zlib-dev \
|
||||||
|
openssl-dev \
|
||||||
|
boost-dev \
|
||||||
|
libconfig-dev \
|
||||||
|
taglib-dev"
|
||||||
|
|
||||||
|
RUN apk add --no-cache --update ${BUILD_PACKAGES}
|
||||||
|
|
||||||
|
## ffmpeg
|
||||||
|
RUN \
|
||||||
|
DIR=/tmp/ffmpeg && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
|
curl -sLO https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2 && \
|
||||||
|
tar -jx --strip-components=1 -f ffmpeg-${FFMPEG_VERSION}.tar.bz2
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
DIR=/tmp/ffmpeg && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
|
./configure \
|
||||||
|
--prefix=${PREFIX} \
|
||||||
|
--disable-autodetect \
|
||||||
|
--disable-debug \
|
||||||
|
--disable-doc \
|
||||||
|
--disable-ffplay \
|
||||||
|
--disable-ffprobe \
|
||||||
|
--disable-openssl \
|
||||||
|
--disable-postproc \
|
||||||
|
--disable-pixelutils \
|
||||||
|
--disable-network \
|
||||||
|
--enable-shared \
|
||||||
|
--disable-static \
|
||||||
|
--enable-gpl \
|
||||||
|
--enable-small \
|
||||||
|
--enable-version3 \
|
||||||
|
--enable-nonfree \
|
||||||
|
--enable-libmp3lame \
|
||||||
|
--enable-libopenjpeg \
|
||||||
|
--enable-libopus \
|
||||||
|
--enable-libvorbis \
|
||||||
|
--disable-everything \
|
||||||
|
--enable-decoder=aac*,ac3*,flac,mp3*,libopus,pcm*,libvorbis,wavpack,wma*,libopenjpg,png,tiff \
|
||||||
|
--enable-encoder=libmp3lame,libopus,libvorbis \
|
||||||
|
--enable-demuxer=aiff,asf,flac,ipod,ogg,matroska,mp3,mp4,wav,wv,webm \
|
||||||
|
--enable-muxer=ogg,matroska,mp3,webm \
|
||||||
|
--enable-protocol=file,pipe \
|
||||||
|
--enable-filter=aresample \
|
||||||
|
--extra-libs=-ldl && \
|
||||||
|
make && \
|
||||||
|
make install && \
|
||||||
|
make distclean
|
||||||
|
|
||||||
|
# WT
|
||||||
|
RUN \
|
||||||
|
DIR=/tmp/wt && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
|
curl -sLO https://github.com/emweb/wt/archive/${WT_VERSION}.tar.gz && \
|
||||||
|
tar -x --strip-components=1 -f ${WT_VERSION}.tar.gz
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
DIR=/tmp/wt && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=${PREFIX} && \
|
||||||
|
make && \
|
||||||
|
make install
|
||||||
|
|
||||||
|
# ImageMagick
|
||||||
|
RUN \
|
||||||
|
DIR=/tmp/imagemagick && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
|
curl -sLO https://github.com/ImageMagick/ImageMagick6/archive/${IMAGEMAGICK6_VERSION}.tar.gz && \
|
||||||
|
tar -x --strip-components=1 -f ${IMAGEMAGICK6_VERSION}.tar.gz
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
DIR=/tmp/imagemagick && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
|
./configure \
|
||||||
|
--prefix=${PREFIX} \
|
||||||
|
--enable-shared=yes \
|
||||||
|
--disable-static \
|
||||||
|
--disable-docs \
|
||||||
|
--with-magick-plus-plus \
|
||||||
|
--without-zstd \
|
||||||
|
--without-dps \
|
||||||
|
--without-autotrace \
|
||||||
|
--without-dps \
|
||||||
|
--without-fftw \
|
||||||
|
--without-flif \
|
||||||
|
--without-fpx \
|
||||||
|
--without-djvu \
|
||||||
|
--without-fontconfig \
|
||||||
|
--without-freetype \
|
||||||
|
--without-raqm \
|
||||||
|
--without-gslib \
|
||||||
|
--without-gvc \
|
||||||
|
--without-heic \
|
||||||
|
--without-jbig \
|
||||||
|
--with-jpeg \
|
||||||
|
--without-jxl \
|
||||||
|
--without-lcms \
|
||||||
|
--with-openjp2 \
|
||||||
|
--without-lqr \
|
||||||
|
--with-lzma \
|
||||||
|
--without-openexr \
|
||||||
|
--without-pango \
|
||||||
|
--with-png \
|
||||||
|
--without-raw \
|
||||||
|
--without-rsvg \
|
||||||
|
--with-tiff \
|
||||||
|
--without-webp \
|
||||||
|
--without-wmf \
|
||||||
|
--without-xml && \
|
||||||
|
make && \
|
||||||
|
make install && \
|
||||||
|
make distclean
|
||||||
|
|
||||||
|
# libpstreams
|
||||||
|
RUN \
|
||||||
|
DIR=/tmp/libpstreams && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
|
curl -sLO https://sourceforge.net/projects/pstreams/files/pstreams/Release%201.0/pstreams-${PSTREAMS_VERSION}.tar.gz && \
|
||||||
|
tar -x --strip-components=1 -f pstreams-${PSTREAMS_VERSION}.tar.gz && \
|
||||||
|
prefix=${PREFIX} make install prefix=/ DESTDIR=${PREFIX}
|
||||||
|
|
||||||
|
# LMS
|
||||||
|
RUN \
|
||||||
|
DIR=/tmp/lms && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
|
curl -sLO https://github.com/epoupon/lms/archive/v${LMS_VERSION}.tar.gz && \
|
||||||
|
tar -x --strip-components=1 -f v${LMS_VERSION}.tar.gz
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
DIR=/tmp/lms && mkdir -p ${DIR} && cd ${DIR} && \
|
||||||
|
autoreconf -vfi && \
|
||||||
|
PKG_CONFIG_PATH=/tmp/install/lib/pkgconfig CXXFLAGS="-O2 -I${PREFIX}/include" LDFLAGS="-L${PREFIX}/lib -Wl,--rpath-link=${PREFIX}/lib" ./configure --prefix=${PREFIX} && \
|
||||||
|
make && \
|
||||||
|
make install && \
|
||||||
|
make distclean && \
|
||||||
|
mkdir -p ${PREFIX}/etc/ && \
|
||||||
|
cp conf/lms.conf ${PREFIX}/etc
|
||||||
|
|
||||||
|
# Now copy all the stuff installed in a new folder (/tmp/fakeroot/)
|
||||||
|
RUN \
|
||||||
|
mkdir -p /tmp/fakeroot/bin && \
|
||||||
|
for bin in ${PREFIX}/bin/ffmpeg ${PREFIX}/bin/lms; \
|
||||||
|
do \
|
||||||
|
strip --strip-all $bin && \
|
||||||
|
cp $bin /tmp/fakeroot/bin/; \
|
||||||
|
done && \
|
||||||
|
for lib in ${PREFIX}/lib/*.so; \
|
||||||
|
do \
|
||||||
|
strip --strip-all $lib; \
|
||||||
|
done && \
|
||||||
|
cp -r ${PREFIX}/lib /tmp/fakeroot/lib && \
|
||||||
|
cp -r ${PREFIX}/share /tmp/fakeroot/share && \
|
||||||
|
LD_LIBRARY_PATH=/tmp/fakeroot/lib /tmp/fakeroot/bin/ffmpeg -buildconf
|
||||||
|
|
||||||
|
## Release Stage
|
||||||
|
FROM alpine:3.10 AS release
|
||||||
|
MAINTAINER Emeric Poupon <itmfr@yahoo.fr>
|
||||||
|
|
||||||
|
ARG RUNTIME_PACKAGES=" \
|
||||||
|
openssl \
|
||||||
|
libjpeg-turbo \
|
||||||
|
openjpeg \
|
||||||
|
libpng \
|
||||||
|
tiff \
|
||||||
|
libogg \
|
||||||
|
opus \
|
||||||
|
libvorbis \
|
||||||
|
lame \
|
||||||
|
zlib \
|
||||||
|
boost-filesystem \
|
||||||
|
boost-program_options \
|
||||||
|
boost-system \
|
||||||
|
boost-thread \
|
||||||
|
libconfig++ \
|
||||||
|
taglib \
|
||||||
|
libgomp"
|
||||||
|
|
||||||
|
ARG LMS_USER=lms
|
||||||
|
ARG LMS_GROUP=lms
|
||||||
|
|
||||||
|
RUN apk add --no-cache --update ${RUNTIME_PACKAGES}
|
||||||
|
|
||||||
|
RUN addgroup -S ${LMS_GROUP} && \
|
||||||
|
adduser -S -H ${LMS_USER} && \
|
||||||
|
adduser ${LMS_USER} ${LMS_GROUP} && \
|
||||||
|
mkdir -p /var/lms && chown -R ${LMS_USER}:${LMS_GROUP} /var/lms
|
||||||
|
|
||||||
|
VOLUME /var/lms
|
||||||
|
VOLUME /music
|
||||||
|
VOLUME /usr/local/etc
|
||||||
|
|
||||||
|
USER ${LMS_USER}:${LMS_GROUP}
|
||||||
|
|
||||||
|
COPY --from=build /tmp/fakeroot/ /usr
|
||||||
|
COPY --from=build /tmp/fakeroot/share/lms/lms.conf /etc/lms.conf
|
||||||
|
|
||||||
|
EXPOSE 5082
|
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/bin/lms"]
|
||||||
|
|
||||||
@@ -46,11 +46,6 @@
|
|||||||
using namespace Database;
|
using namespace Database;
|
||||||
|
|
||||||
static const std::string genreClusterName {"GENRE"};
|
static const std::string genreClusterName {"GENRE"};
|
||||||
// Files are always reported to be in the same format
|
|
||||||
static const std::size_t reportedBitrate {128};
|
|
||||||
static const std::string reportedFileSuffix {"mp3"};
|
|
||||||
static const Av::Encoding reportedEncoding {Av::Encoding::MP3};
|
|
||||||
static const Av::Encoding transcodeEncoding {Av::Encoding::MP3};
|
|
||||||
static const std::string reportedStarredDate {"2000-01-01T00:00:00"};
|
static const std::string reportedStarredDate {"2000-01-01T00:00:00"};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -362,7 +357,9 @@ getTrackPath(const Track::pointer& track)
|
|||||||
{
|
{
|
||||||
std::string path;
|
std::string path;
|
||||||
|
|
||||||
auto release {track->getRelease()};
|
// The track path has to be relative from the root
|
||||||
|
|
||||||
|
const auto release {track->getRelease()};
|
||||||
if (release)
|
if (release)
|
||||||
{
|
{
|
||||||
auto artists {release->getReleaseArtists()};
|
auto artists {release->getReleaseArtists()};
|
||||||
@@ -382,7 +379,12 @@ getTrackPath(const Track::pointer& track)
|
|||||||
if (track->getTrackNumber())
|
if (track->getTrackNumber())
|
||||||
path += std::to_string(*track->getTrackNumber()) + "-";
|
path += std::to_string(*track->getTrackNumber()) + "-";
|
||||||
|
|
||||||
return path + makeNameFilesystemCompatible(track->getName()) + "." + reportedFileSuffix;
|
path += makeNameFilesystemCompatible(track->getName());
|
||||||
|
|
||||||
|
if (track->getPath().has_extension())
|
||||||
|
path += track->getPath().extension();
|
||||||
|
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
@@ -400,7 +402,20 @@ trackToResponseNode(const Track::pointer& track, Session& dbSession, const User:
|
|||||||
trackResponse.setAttribute("discNumber", std::to_string(*track->getDiscNumber()));
|
trackResponse.setAttribute("discNumber", std::to_string(*track->getDiscNumber()));
|
||||||
if (track->getYear())
|
if (track->getYear())
|
||||||
trackResponse.setAttribute("year", std::to_string(*track->getYear()));
|
trackResponse.setAttribute("year", std::to_string(*track->getYear()));
|
||||||
trackResponse.setAttribute("size", std::to_string(reportedBitrate * 1000 / 8 * std::chrono::duration_cast<std::chrono::seconds>(track->getDuration()).count()));
|
|
||||||
|
trackResponse.setAttribute("path", getTrackPath(track));
|
||||||
|
{
|
||||||
|
std::error_code ec;
|
||||||
|
const auto fileSize {std::filesystem::file_size(track->getPath(), ec)};
|
||||||
|
if (!ec)
|
||||||
|
trackResponse.setAttribute("size", std::to_string(fileSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (track->getPath().has_extension())
|
||||||
|
{
|
||||||
|
auto extension {track->getPath().extension()};
|
||||||
|
trackResponse.setAttribute("suffix", extension.string().substr(1));
|
||||||
|
}
|
||||||
|
|
||||||
trackResponse.setAttribute("coverArt", IdToString({Id::Type::Track, track.id()}));
|
trackResponse.setAttribute("coverArt", IdToString({Id::Type::Track, track.id()}));
|
||||||
|
|
||||||
@@ -420,11 +435,7 @@ trackToResponseNode(const Track::pointer& track, Session& dbSession, const User:
|
|||||||
trackResponse.setAttribute("parent", IdToString({Id::Type::Release, track->getRelease().id()}));
|
trackResponse.setAttribute("parent", IdToString({Id::Type::Release, track->getRelease().id()}));
|
||||||
}
|
}
|
||||||
|
|
||||||
trackResponse.setAttribute("path", getTrackPath(track));
|
|
||||||
trackResponse.setAttribute("bitRate", std::to_string(reportedBitrate));
|
|
||||||
trackResponse.setAttribute("duration", std::to_string(std::chrono::duration_cast<std::chrono::seconds>(track->getDuration()).count()));
|
trackResponse.setAttribute("duration", std::to_string(std::chrono::duration_cast<std::chrono::seconds>(track->getDuration()).count()));
|
||||||
trackResponse.setAttribute("suffix", reportedFileSuffix);
|
|
||||||
trackResponse.setAttribute("contentType", Av::encodingToMimetype(reportedEncoding));
|
|
||||||
trackResponse.setAttribute("type", "music");
|
trackResponse.setAttribute("type", "music");
|
||||||
|
|
||||||
if (user->hasStarredTrack(track))
|
if (user->hasStarredTrack(track))
|
||||||
@@ -1686,6 +1697,21 @@ handleUpdatePlaylistRequest(RequestContext& context)
|
|||||||
return Response::createOkResponse();
|
return Response::createOkResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
Av::Encoding
|
||||||
|
userTranscodeFormatToAvEncoding(AudioFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case AudioFormat::MP3: return Av::Encoding::MP3;
|
||||||
|
case AudioFormat::OGG_OPUS: return Av::Encoding::OGG_OPUS;
|
||||||
|
case AudioFormat::MATROSKA_OPUS: return Av::Encoding::MATROSKA_OPUS;
|
||||||
|
case AudioFormat::OGG_VORBIS: return Av::Encoding::OGG_VORBIS;
|
||||||
|
case AudioFormat::WEBM_VORBIS: return Av::Encoding::WEBM_VORBIS;
|
||||||
|
default: return Av::Encoding::OGG_OPUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
std::shared_ptr<Av::Transcoder>
|
std::shared_ptr<Av::Transcoder>
|
||||||
createTranscoder(RequestContext& context)
|
createTranscoder(RequestContext& context)
|
||||||
@@ -1695,34 +1721,44 @@ createTranscoder(RequestContext& context)
|
|||||||
|
|
||||||
// Optional params
|
// Optional params
|
||||||
std::optional<std::size_t> maxBitRate {getParameterAs<std::size_t>(context.parameters, "maxBitRate")};
|
std::optional<std::size_t> maxBitRate {getParameterAs<std::size_t>(context.parameters, "maxBitRate")};
|
||||||
|
std::optional<std::string> format {getParameterAs<std::string>(context.parameters, "format")};
|
||||||
|
|
||||||
|
Av::TranscodeParameters parameters {};
|
||||||
|
parameters.stripMetadata = false; // Since it can be cached and some players read the metadata from the downloaded file
|
||||||
|
|
||||||
std::filesystem::path trackPath;
|
std::filesystem::path trackPath;
|
||||||
{
|
{
|
||||||
auto transaction {context.dbSession.createSharedTransaction()};
|
auto transaction {context.dbSession.createSharedTransaction()};
|
||||||
|
|
||||||
User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
|
{
|
||||||
if (!user)
|
auto track {Track::getById(context.dbSession, id.value)};
|
||||||
throw UserNotAuthorizedError {};
|
if (!track)
|
||||||
|
throw RequestedDataNotFoundError {};
|
||||||
|
|
||||||
// "If set to zero, no limit is imposed"
|
trackPath = track->getPath();
|
||||||
if (!maxBitRate || *maxBitRate == 0)
|
}
|
||||||
maxBitRate = user->getAudioTranscodeBitrate() / 1000;
|
|
||||||
|
|
||||||
*maxBitRate = clamp(*maxBitRate, std::size_t {48}, user->getMaxAudioTranscodeBitrate() / 1000);
|
{
|
||||||
|
const User::pointer user {User::getByLoginName(context.dbSession, context.userName)};
|
||||||
|
if (!user)
|
||||||
|
throw UserNotAuthorizedError {};
|
||||||
|
|
||||||
auto track {Track::getById(context.dbSession, id.value)};
|
// format = "raw" => no transcode. Other format values will be ignored
|
||||||
if (!track)
|
const bool transcode {(!format || (format && *format != "raw")) && user->getAudioTranscodeEnable()};
|
||||||
throw RequestedDataNotFoundError {};
|
if (transcode)
|
||||||
|
{
|
||||||
|
// "If set to zero, no limit is imposed"
|
||||||
|
if (!maxBitRate || *maxBitRate == 0)
|
||||||
|
maxBitRate = user->getAudioTranscodeBitrate() / 1000;
|
||||||
|
|
||||||
trackPath = track->getPath();
|
*maxBitRate = clamp(*maxBitRate, std::size_t {48}, user->getMaxAudioTranscodeBitrate() / 1000);
|
||||||
|
|
||||||
|
parameters.bitrate = *maxBitRate * 1000;
|
||||||
|
parameters.encoding = userTranscodeFormatToAvEncoding(user->getAudioTranscodeFormat());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Av::TranscodeParameters parameters {};
|
|
||||||
|
|
||||||
parameters.stripMetadata = false; // Since it can be cached and some players read the metadata from the downloaded file
|
|
||||||
parameters.bitrate = *maxBitRate * 1000;
|
|
||||||
parameters.encoding = transcodeEncoding;
|
|
||||||
|
|
||||||
return std::make_shared<Av::Transcoder>(trackPath, parameters);
|
return std::make_shared<Av::Transcoder>(trackPath, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,6 +120,13 @@ Transcoder::start()
|
|||||||
args.emplace_back("ogg");
|
args.emplace_back("ogg");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Encoding::MATROSKA_OPUS:
|
||||||
|
args.emplace_back("-acodec");
|
||||||
|
args.emplace_back("libopus");
|
||||||
|
args.emplace_back("-f");
|
||||||
|
args.emplace_back("matroska");
|
||||||
|
break;
|
||||||
|
|
||||||
case Encoding::OGG_VORBIS:
|
case Encoding::OGG_VORBIS:
|
||||||
args.emplace_back("-acodec");
|
args.emplace_back("-acodec");
|
||||||
args.emplace_back("libvorbis");
|
args.emplace_back("libvorbis");
|
||||||
@@ -134,7 +141,6 @@ Transcoder::start()
|
|||||||
args.emplace_back("webm");
|
args.emplace_back("webm");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ namespace Av {
|
|||||||
|
|
||||||
struct TranscodeParameters
|
struct TranscodeParameters
|
||||||
{
|
{
|
||||||
std::optional<Encoding> encoding; // If not set, no transcoding is performed
|
std::optional<Encoding> encoding; // If not set, no transcoding is performed
|
||||||
std::size_t bitrate {128000};
|
std::size_t bitrate {128000};
|
||||||
std::optional<std::size_t> stream; // Id of the stream to be transcoded (auto detect by default)
|
std::optional<std::size_t> stream; // Id of the stream to be transcoded (auto detect by default)
|
||||||
std::optional<std::chrono::seconds> offset {};
|
std::optional<std::chrono::seconds> offset;
|
||||||
bool stripMetadata {true};
|
bool stripMetadata {true};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+9
-12
@@ -23,21 +23,18 @@
|
|||||||
|
|
||||||
namespace Av {
|
namespace Av {
|
||||||
|
|
||||||
std::string encodingToMimetype(Encoding encoding)
|
const char* encodingToMimetype(Encoding encoding)
|
||||||
{
|
{
|
||||||
static const std::map<Encoding, std::string> encodings
|
switch (encoding)
|
||||||
{
|
{
|
||||||
{Encoding::MP3, "audio/mpeg"},
|
case Encoding::MP3: return "audio/mpeg";
|
||||||
{Encoding::OGG_VORBIS, "audio/ogg"},
|
case Encoding::OGG_OPUS: return "audio/opus";
|
||||||
{Encoding::OGG_OPUS, "audio/opus"},
|
case Encoding::MATROSKA_OPUS: return "audio/x-matroska";
|
||||||
{Encoding::WEBM_VORBIS, "audio/webm"},
|
case Encoding::OGG_VORBIS: return "audio/ogg";
|
||||||
};
|
case Encoding::WEBM_VORBIS: return "audio/webm";
|
||||||
|
}
|
||||||
|
|
||||||
auto it {encodings.find(encoding)};
|
throw AvException("Invalid encoding");
|
||||||
if (it == encodings.end())
|
|
||||||
throw AvException("Invalid encoding");
|
|
||||||
|
|
||||||
return it->second;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-5
@@ -34,13 +34,14 @@ class AvException : public LmsException
|
|||||||
enum class Encoding
|
enum class Encoding
|
||||||
{
|
{
|
||||||
// Values are important and must not be changed
|
// Values are important and must not be changed
|
||||||
MP3 = 0,
|
MP3,
|
||||||
OGG_OPUS = 1,
|
OGG_OPUS,
|
||||||
OGG_VORBIS = 2,
|
MATROSKA_OPUS,
|
||||||
WEBM_VORBIS = 3,
|
OGG_VORBIS,
|
||||||
|
WEBM_VORBIS,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string encodingToMimetype(Encoding encoding);
|
const char* encodingToMimetype(Encoding encoding);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ enum class AudioFormat
|
|||||||
OGG_OPUS = 2,
|
OGG_OPUS = 2,
|
||||||
OGG_VORBIS = 3,
|
OGG_VORBIS = 3,
|
||||||
WEBM_VORBIS = 4,
|
WEBM_VORBIS = 4,
|
||||||
|
MATROSKA_OPUS = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
using Bitrate = std::size_t;
|
using Bitrate = std::size_t;
|
||||||
|
|||||||
@@ -212,6 +212,7 @@ class SettingsModel : public Wt::WFormModel
|
|||||||
_transcodeFormatModel = std::make_shared<ValueStringModel<AudioFormat>>();
|
_transcodeFormatModel = std::make_shared<ValueStringModel<AudioFormat>>();
|
||||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.mp3"), AudioFormat::MP3);
|
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.mp3"), AudioFormat::MP3);
|
||||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.ogg_opus"), AudioFormat::OGG_OPUS);
|
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.ogg_opus"), AudioFormat::OGG_OPUS);
|
||||||
|
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.matroska_opus"), AudioFormat::MATROSKA_OPUS);
|
||||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.ogg_vorbis"), AudioFormat::OGG_VORBIS);
|
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.ogg_vorbis"), AudioFormat::OGG_VORBIS);
|
||||||
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.webm_vorbis"), AudioFormat::WEBM_VORBIS);
|
_transcodeFormatModel->add(Wt::WString::tr("Lms.Settings.transcoding.webm_vorbis"), AudioFormat::WEBM_VORBIS);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,9 @@ AudioResource::handleRequest(const Wt::Http::Request& request,
|
|||||||
case Database::AudioFormat::OGG_OPUS:
|
case Database::AudioFormat::OGG_OPUS:
|
||||||
parameters.encoding = Av::Encoding::OGG_OPUS;
|
parameters.encoding = Av::Encoding::OGG_OPUS;
|
||||||
break;
|
break;
|
||||||
|
case Database::AudioFormat::MATROSKA_OPUS:
|
||||||
|
parameters.encoding = Av::Encoding::MATROSKA_OPUS;
|
||||||
|
break;
|
||||||
case Database::AudioFormat::OGG_VORBIS:
|
case Database::AudioFormat::OGG_VORBIS:
|
||||||
parameters.encoding = Av::Encoding::OGG_VORBIS;
|
parameters.encoding = Av::Encoding::OGG_VORBIS;
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user