diff --git a/README b/README index 2379dfc6..dca3d7e7 100644 --- a/README +++ b/README @@ -1,17 +1,17 @@ LMS - Lightweight Media Server -LMS is a free media streaming software, released under the GPLv3 license. +LMS is a self-hosted media streaming software, released under the GPLv3 license. It allows you to access your music and your videos using an https web interface. Features: +- Winamp-like interface, suited for large databases +- Audio/Video transcode for maximum interoperability and low bandwith requirements - User management -- Fast "Winamp-style" interface -- Audio/Video transcode (including downsampling), based on the libav project (see https://www.libav.org/). -- Custom API to access data using Protocol Buffers format over TCP/SSL (see https://github.com/google/protobuf/ and src/remote/proto) +- API available in Protocol Buffers (see src/remote/proto/messages.proto) Please note LMS is still under development and will gain more features in the future. -An android application is being developped as well. +A mobile application based on Kivy is being developped as well. LMS is written entirely in C++. Therefore, it is suitable to run on embedded devices, where space and/or memory are limited. Please note some media files may require significant CPU usage to be transcoded. @@ -37,7 +37,7 @@ II) BUILD III) Setting up SSL materials (required by the SSL server and the web server) -This is just a self-signed certificate example, you can set up a real CA if you want to. +This is just a self signed certificate example, you could do use a CA if you want. Generate a dh file: # openssl dhparam -out dh2048.pem 2048 @@ -59,3 +59,9 @@ You must also link Wt's docroot (usually located in /usr/share/Wt) into that dir Depending on your SSL parameters, you may be asked for the PEM passphrase to unlock the private key. +V) LINKS + +- Wt (http://www.webtoolkit.eu/) +- bootstrap3 (http://getbootstrap.com/) +- libav project (https://www.libav.org/). +- Protocol Buffers (https://github.com/google/protobuf/) diff --git a/TODO b/TODO index 5c205ccb..1dabca79 100644 --- a/TODO +++ b/TODO @@ -22,7 +22,7 @@ - OGG metadata -> properly handle metadata nested in the audio stream [Transcode] -- some early end of playback spotted on flac files +- some early end of playback spotted on flac files. Spotted on old firefox versions? [UI] - handle internationalization @@ -31,6 +31,7 @@ - "signal not exposed" problem if a user logout and login again. bad resource destruction? [user/transcoding] - Prefered codecs for audio/video? + - use a slider instead of a combo box for the bitrates [admin/Users] - do not activate the update period/start time when 'admin' is checked [admin/Database update] @@ -39,10 +40,12 @@ [Audio] - Style eveything nicely... - - MediaPlayer: move slider using js (http://redmine.webtoolkit.eu/boards/2/topics/7924?r=8478) + - MediaPlayer: move the slider using js (http://redmine.webtoolkit.eu/boards/2/topics/7924?r=8478) - TrackView : handle duration > 1 hour - TrackView : Reselect the current selected item when displaying the updated search results - Add covers in the release filter? + - Add a download button to get the current playlist in a streamed zip file + - Add keyboard shortcuts [Video] - implement a decent mediaplayer diff --git a/src/logger/Logger.cpp b/src/logger/Logger.cpp index 828ab817..41b6db0f 100644 --- a/src/logger/Logger.cpp +++ b/src/logger/Logger.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -89,6 +89,7 @@ Logger::init(const Config& config) boost::log::keywords::file_name = config.logPath + std::string(".%N"), boost::log::keywords::rotation_size = 10 * 1024 * 1024, boost::log::keywords::open_mode = std::ios_base::app, + boost::log::keywords::auto_flush = true, boost::log::keywords::format = ( boost::log::expressions::stream << boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "[%Y-%m-%d %H:%M:%S]") diff --git a/src/remote/proto/messages.proto b/src/remote/proto/messages.proto index 30bef5f2..b9b2f704 100644 --- a/src/remote/proto/messages.proto +++ b/src/remote/proto/messages.proto @@ -5,6 +5,16 @@ import "media.proto"; package Remote; +// Every request sent by the client must be a ClientMessage +// The server will answer using a ServerMessage +// +// The client must authenticate first before querying data +// If an expected message is received, the connection is dropped by the server +// +// A header is to be added before the serialized protobuf message in order to keep +// track of where one message ends and the next begins. +// Format: +// MAGIC (4 bytes, 0xDE, 0xAD, 0xBE, 0xEF)- SIZE (4 bytes, big endian encoded) - serialized proto message message ClientMessage { enum Type diff --git a/src/ui/approot/templates.xml b/src/ui/approot/templates.xml index ff0344b7..c4f1bf71 100644 --- a/src/ui/approot/templates.xml +++ b/src/ui/approot/templates.xml @@ -341,9 +341,7 @@
-
-
${name}
-
+
${name}
diff --git a/src/ui/audio/desktop/PlayQueue.cpp b/src/ui/audio/desktop/PlayQueue.cpp index 721813f9..8d600fc0 100644 --- a/src/ui/audio/desktop/PlayQueue.cpp +++ b/src/ui/audio/desktop/PlayQueue.cpp @@ -34,7 +34,7 @@ #include "PlayQueue.hpp" -static const int NameRole = Wt::UserRole; +static const int TrackInfoRole = Wt::UserRole; namespace { @@ -220,10 +220,11 @@ TrackSelector::setSize(std::size_t size) refreshPositions(); } -struct Name +struct TrackInfo { Wt::WString track; Wt::WString artist; + Wt::WString release; }; class PlayQueueItemDelegate : public Wt::WItemDelegate @@ -235,13 +236,13 @@ class PlayQueueItemDelegate : public Wt::WItemDelegate { Wt::WWidget* res; - if (!index.data(NameRole).empty()) + if (!index.data(TrackInfoRole).empty()) { - Name name = boost::any_cast(index.data(NameRole)); + TrackInfo trackInfo = boost::any_cast(index.data(TrackInfoRole)); Wt::WContainerWidget *container = new Wt::WContainerWidget(); - Wt::WText* track = new Wt::WText(name.track, Wt::PlainText, container); - Wt::WText* artist = new Wt::WText(name.artist, Wt::PlainText, container); + Wt::WText* track = new Wt::WText(trackInfo.track, Wt::PlainText, container); + Wt::WText* artist = new Wt::WText(trackInfo.artist + " - " + trackInfo.release, Wt::PlainText, container); artist->setInline(false); track->setInline(false); @@ -382,10 +383,11 @@ PlayQueue::addTracks(const std::vector& trackIds) coverUrl = "images/unknown-cover.jpg"; _model->setData(dataRow, COLUMN_ID_COVER, coverUrl, Wt::DecorationRole); - Name name; - name.track = Wt::WString::fromUTF8(track->getName()); - name.artist = Wt::WString::fromUTF8(track->getArtistName()); - _model->setData(dataRow, COLUMN_ID_NAME, name, NameRole); + TrackInfo trackInfo; + trackInfo.track = Wt::WString::fromUTF8(track->getName()); + trackInfo.artist = Wt::WString::fromUTF8(track->getArtistName()); + trackInfo.release = Wt::WString::fromUTF8(track->getReleaseName()); + _model->setData(dataRow, COLUMN_ID_NAME, trackInfo, TrackInfoRole); } } diff --git a/src/ui/video/VideoWidget.cpp b/src/ui/video/VideoWidget.cpp index 566a392f..16ff470f 100644 --- a/src/ui/video/VideoWidget.cpp +++ b/src/ui/video/VideoWidget.cpp @@ -45,17 +45,6 @@ VideoWidget::search(const std::string& searchText) //TODO } -void -VideoWidget::backToList(void) -{ - if (_mediaPlayer) - delete _mediaPlayer; - - _videoDbWidget->setHidden(false); - -} - - void VideoWidget::playVideo(boost::filesystem::path p) { @@ -100,8 +89,11 @@ VideoWidget::playVideo(boost::filesystem::path p) parameters.setBitrate(Transcode::Stream::Audio, 0/*audioBitrate*/); parameters.setBitrate(Transcode::Stream::Video, 0/*videoBitrate*/); - _mediaPlayer = new VideoMediaPlayerWidget(parameters, this); - _mediaPlayer->close().connect(this, &VideoWidget::backToList); + VideoMediaPlayerWidget *mediaPlayer = new VideoMediaPlayerWidget(parameters, this); + mediaPlayer->close().connect(std::bind([=] () { + _videoDbWidget->setHidden(false); + delete mediaPlayer; + })); _videoDbWidget->setHidden(true); }