Merge branch 'develop'

Conflicts:
	README
	src/ui/approot/templates.xml
This commit is contained in:
emeric
2015-03-05 19:38:15 +01:00
7 changed files with 47 additions and 35 deletions
+12 -6
View File
@@ -1,17 +1,17 @@
LMS - Lightweight Media Server 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. It allows you to access your music and your videos using an https web interface.
Features: Features:
- Winamp-like interface, suited for large databases
- Audio/Video transcode for maximum interoperability and low bandwith requirements
- User management - User management
- Fast "Winamp-style" interface - API available in Protocol Buffers (see src/remote/proto/messages.proto)
- 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)
Please note LMS is still under development and will gain more features in the future. 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. 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. 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) 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: Generate a dh file:
# openssl dhparam -out dh2048.pem 2048 # 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. 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/)
+5 -2
View File
@@ -22,7 +22,7 @@
- OGG metadata -> properly handle metadata nested in the audio stream - OGG metadata -> properly handle metadata nested in the audio stream
[Transcode] [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] [UI]
- handle internationalization - handle internationalization
@@ -31,6 +31,7 @@
- "signal not exposed" problem if a user logout and login again. bad resource destruction? - "signal not exposed" problem if a user logout and login again. bad resource destruction?
[user/transcoding] [user/transcoding]
- Prefered codecs for audio/video? - Prefered codecs for audio/video?
- use a slider instead of a combo box for the bitrates
[admin/Users] [admin/Users]
- do not activate the update period/start time when 'admin' is checked - do not activate the update period/start time when 'admin' is checked
[admin/Database update] [admin/Database update]
@@ -39,10 +40,12 @@
[Audio] [Audio]
- Style eveything nicely... - 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 : handle duration > 1 hour
- TrackView : Reselect the current selected item when displaying the updated search results - TrackView : Reselect the current selected item when displaying the updated search results
- Add covers in the release filter? - Add covers in the release filter?
- Add a download button to get the current playlist in a streamed zip file
- Add keyboard shortcuts
[Video] [Video]
- implement a decent mediaplayer - implement a decent mediaplayer
+2 -1
View File
@@ -23,7 +23,7 @@
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp> #include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp> #include <boost/log/sinks/text_file_backend.hpp>
#include </usr/include/boost/log/keywords/filter.hpp> #include <boost/log/keywords/filter.hpp>
#include <boost/log/utility/setup/file.hpp> #include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp> #include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/console.hpp> #include <boost/log/utility/setup/console.hpp>
@@ -89,6 +89,7 @@ Logger::init(const Config& config)
boost::log::keywords::file_name = config.logPath + std::string(".%N"), boost::log::keywords::file_name = config.logPath + std::string(".%N"),
boost::log::keywords::rotation_size = 10 * 1024 * 1024, boost::log::keywords::rotation_size = 10 * 1024 * 1024,
boost::log::keywords::open_mode = std::ios_base::app, boost::log::keywords::open_mode = std::ios_base::app,
boost::log::keywords::auto_flush = true,
boost::log::keywords::format = ( boost::log::keywords::format = (
boost::log::expressions::stream boost::log::expressions::stream
<< boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "[%Y-%m-%d %H:%M:%S]") << boost::log::expressions::format_date_time< boost::posix_time::ptime >("TimeStamp", "[%Y-%m-%d %H:%M:%S]")
+10
View File
@@ -5,6 +5,16 @@ import "media.proto";
package Remote; 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 message ClientMessage
{ {
enum Type enum Type
+1 -3
View File
@@ -341,9 +341,7 @@
<message id="mobile-artist-res"> <message id="mobile-artist-res">
<div class="row"> <div class="row">
<div class="col-xs-12"> <div class="col-xs-12 mobile-search-entry">${name}</div>
<div class="mobile-search-entry">${name}</div>
</div>
</div> </div>
</message> </message>
+12 -10
View File
@@ -34,7 +34,7 @@
#include "PlayQueue.hpp" #include "PlayQueue.hpp"
static const int NameRole = Wt::UserRole; static const int TrackInfoRole = Wt::UserRole;
namespace { namespace {
@@ -220,10 +220,11 @@ TrackSelector::setSize(std::size_t size)
refreshPositions(); refreshPositions();
} }
struct Name struct TrackInfo
{ {
Wt::WString track; Wt::WString track;
Wt::WString artist; Wt::WString artist;
Wt::WString release;
}; };
class PlayQueueItemDelegate : public Wt::WItemDelegate class PlayQueueItemDelegate : public Wt::WItemDelegate
@@ -235,13 +236,13 @@ class PlayQueueItemDelegate : public Wt::WItemDelegate
{ {
Wt::WWidget* res; Wt::WWidget* res;
if (!index.data(NameRole).empty()) if (!index.data(TrackInfoRole).empty())
{ {
Name name = boost::any_cast<Name>(index.data(NameRole)); TrackInfo trackInfo = boost::any_cast<TrackInfo>(index.data(TrackInfoRole));
Wt::WContainerWidget *container = new Wt::WContainerWidget(); Wt::WContainerWidget *container = new Wt::WContainerWidget();
Wt::WText* track = new Wt::WText(name.track, Wt::PlainText, container); Wt::WText* track = new Wt::WText(trackInfo.track, Wt::PlainText, container);
Wt::WText* artist = new Wt::WText(name.artist, Wt::PlainText, container); Wt::WText* artist = new Wt::WText(trackInfo.artist + " - " + trackInfo.release, Wt::PlainText, container);
artist->setInline(false); artist->setInline(false);
track->setInline(false); track->setInline(false);
@@ -382,10 +383,11 @@ PlayQueue::addTracks(const std::vector<Database::Track::id_type>& trackIds)
coverUrl = "images/unknown-cover.jpg"; coverUrl = "images/unknown-cover.jpg";
_model->setData(dataRow, COLUMN_ID_COVER, coverUrl, Wt::DecorationRole); _model->setData(dataRow, COLUMN_ID_COVER, coverUrl, Wt::DecorationRole);
Name name; TrackInfo trackInfo;
name.track = Wt::WString::fromUTF8(track->getName()); trackInfo.track = Wt::WString::fromUTF8(track->getName());
name.artist = Wt::WString::fromUTF8(track->getArtistName()); trackInfo.artist = Wt::WString::fromUTF8(track->getArtistName());
_model->setData(dataRow, COLUMN_ID_NAME, name, NameRole); trackInfo.release = Wt::WString::fromUTF8(track->getReleaseName());
_model->setData(dataRow, COLUMN_ID_NAME, trackInfo, TrackInfoRole);
} }
} }
+5 -13
View File
@@ -45,17 +45,6 @@ VideoWidget::search(const std::string& searchText)
//TODO //TODO
} }
void
VideoWidget::backToList(void)
{
if (_mediaPlayer)
delete _mediaPlayer;
_videoDbWidget->setHidden(false);
}
void void
VideoWidget::playVideo(boost::filesystem::path p) 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::Audio, 0/*audioBitrate*/);
parameters.setBitrate(Transcode::Stream::Video, 0/*videoBitrate*/); parameters.setBitrate(Transcode::Stream::Video, 0/*videoBitrate*/);
_mediaPlayer = new VideoMediaPlayerWidget(parameters, this); VideoMediaPlayerWidget *mediaPlayer = new VideoMediaPlayerWidget(parameters, this);
_mediaPlayer->close().connect(this, &VideoWidget::backToList); mediaPlayer->close().connect(std::bind([=] () {
_videoDbWidget->setHidden(false);
delete mediaPlayer;
}));
_videoDbWidget->setHidden(true); _videoDbWidget->setHidden(true);
} }