This commit is contained in:
Christoph Johannsdotter
2016-01-25 21:38:04 +01:00
parent a434cce64f
commit dfe18c8613
24 changed files with 502 additions and 493 deletions

View File

@@ -1,3 +1,21 @@
## Changes from 0.1.0-RC3 to 0.1.0-RC4
* added https sources to Dockerfiles (thx @ [nodiscc](https://github.com/nodiscc))
* fixed visualisation problem of new RTMP/RTSP endpoint (JSONDB processing)
* optimized input-process (removed generateOutputRTMPPath)
* cleaned up NPM packages
* added ESLint insteed of JSHint+ first ruleset
* first code optimizations (ecma5 -> ecma6)
* optimized RaspiCam-Hack for Raspberry Pi 1
* updated NPM packages and node
* refactored FFmpeg-Fluent integration by own fork (reduces process-output)
* fixed output-process (generated high CPU load on arm)
* fixed preview player (no play-icon, didn't stop when the modalbox is closed)
##### Merged pull requests:
* Fixed ARM typo @ [Vyacheslav Shevchenko](https://github.com/bliz937)
## Changes from 0.1.0-RC2 to 0.1.0-RC3
* fixed links to docs

View File

@@ -1,17 +1,18 @@
FROM node:4.2.3-slim
FROM node:4.2.6-slim
MAINTAINER datarhei <info@datarhei.org>
ENV FFMPEG_VERSION 2.8.1
ENV YASM_VERSION 1.3.0
ENV LAME_VERSION 3.99.5
ENV LAME_VERSION 3_99_5
ENV NGINX_VERSION 1.8.0
ENV NGINX_RTMP_VERSION 1.1.7
ENV SRC /usr/local
ENV LD_LIBRARY_PATH ${SRC}/lib
ENV PKG_CONFIG_PATH ${SRC}/lib/pkgconfig
ENV BUILDDEPS "autoconf automake gcc g++ libtool make nasm zlib1g-dev libssl-dev xz-utils cmake perl build-essential libpcre3-dev unzip"
ENV BUILDDEPS "autoconf automake gcc g++ libtool make nasm zlib1g-dev libssl-dev xz-utils cmake perl build-essential libpcre3-dev"
RUN rm -rf /var/lib/apt/lists/* && \
apt-get update && \
@@ -19,7 +20,7 @@ RUN rm -rf /var/lib/apt/lists/* && \
# yasm
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -Os http://www.tortall.net/projects/yasm/releases/yasm-${YASM_VERSION}.tar.gz && \
curl -LOks https://www.tortall.net/projects/yasm/releases/yasm-${YASM_VERSION}.tar.gz && \
tar xzvf yasm-${YASM_VERSION}.tar.gz && \
cd yasm-${YASM_VERSION} && \
./configure --prefix="$SRC" --bindir="${SRC}/bin" && \
@@ -40,9 +41,9 @@ RUN DIR=$(mktemp -d) && cd ${DIR} && \
# libmp3lame
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -L -Os http://downloads.sourceforge.net/project/lame/lame/${LAME_VERSION%.*}/lame-${LAME_VERSION}.tar.gz && \
tar xzvf lame-${LAME_VERSION}.tar.gz && \
cd lame-${LAME_VERSION} && \
curl -LOks https://github.com/rbrito/lame/archive/RELEASE__${LAME_VERSION}.tar.gz && \
tar xzvf RELEASE__${LAME_VERSION}.tar.gz && \
cd lame-RELEASE__${LAME_VERSION} && \
./configure --prefix="${SRC}" --bindir="${SRC}/bin" --disable-shared --enable-nasm && \
make && \
make install && \
@@ -51,7 +52,7 @@ RUN DIR=$(mktemp -d) && cd ${DIR} && \
# ffmpeg
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -Os http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz && \
curl -LOks https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz && \
tar xzvf ffmpeg-${FFMPEG_VERSION}.tar.gz && \
cd ffmpeg-${FFMPEG_VERSION} && \
./configure --prefix="${SRC}" --extra-cflags="-I${SRC}/include" --extra-ldflags="-L${SRC}/lib" --bindir="${SRC}/bin" \
@@ -70,13 +71,12 @@ RUN ffmpeg -buildconf
# nginx-rtmp
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -LOks http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
tar -zxvf nginx-${NGINX_VERSION}.tar.gz && \
curl -LOks https://github.com/arut/nginx-rtmp-module/archive/master.zip && \
unzip master.zip && \
rm master.zip && \
cd nginx-${NGINX_VERSION} && \
./configure --with-http_ssl_module --add-module=../nginx-rtmp-module-master && \
curl -LOks https://github.com/nginx/nginx/archive/release-${NGINX_VERSION}.tar.gz && \
tar xzvf release-${NGINX_VERSION}.tar.gz && \
curl -LOks https://github.com/arut/nginx-rtmp-module/archive/v${NGINX_RTMP_VERSION}.tar.gz && \
tar xzvf v${NGINX_RTMP_VERSION}.tar.gz && \
cd nginx-release-${NGINX_VERSION} && \
auto/configure --with-http_ssl_module --add-module=../nginx-rtmp-module-${NGINX_RTMP_VERSION} && \
make && \
make install && \
rm -rf ${DIR}

View File

@@ -2,30 +2,30 @@ FROM resin/rpi-raspbian:jessie
MAINTAINER datarhei <info@datarhei.org>
ENV NODE_VERSION 4.2.3
ENV NPM_VERSION 2.14.7
ENV NODE_VERSION 4.2.6
ENV NPM_VERSION 2.14.12
ENV FFMPEG_VERSION 2.8.1
ENV YASM_VERSION 1.3.0
ENV LAME_VERSION 3.99.5
ENV LAME_VERSION 3_99_5
ENV NGINX_VERSION 1.8.0
ENV NGINX_RTMP_VERSION 1.1.7
ENV SRC /usr/local
ENV LD_LIBRARY_PATH ${SRC}/lib
ENV PKG_CONFIG_PATH ${SRC}/lib/pkgconfig
ENV BUILDDEPS "autoconf automake gcc g++ libtool make nasm zlib1g-dev libssl-dev xz-utils cmake perl build-essential libpcre3-dev unzip"
ENV BUILDDEPS "autoconf automake gcc g++ libtool make nasm zlib1g-dev libssl-dev xz-utils cmake perl build-essential libpcre3-dev"
RUN rm -rf /var/lib/apt/lists/* && \
apt-get update && \
apt-get install -y curl wget git libpcre3 tar ${BUILDDEPS}
apt-get install -y curl git libpcre3 tar ${BUILDDEPS}
# node
RUN DIR=$(mktemp -d) && cd ${DIR} && \
set -x && \
wget "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-armv6l.tar.gz" && \
curl -LOks https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-armv6l.tar.gz && \
tar -xzf "node-v$NODE_VERSION-linux-armv6l.tar.gz" -C /usr/local --strip-components=1 && \
rm "node-v$NODE_VERSION-linux-armv6l.tar.gz" && \
npm install -g npm@"$NPM_VERSION" --unsafe-perm && \
npm cache clear && \
npm config set unsafe-perm true -g --unsafe-perm && \
@@ -33,7 +33,7 @@ RUN DIR=$(mktemp -d) && cd ${DIR} && \
# yasm
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -Os http://www.tortall.net/projects/yasm/releases/yasm-${YASM_VERSION}.tar.gz && \
curl -LOks https://www.tortall.net/projects/yasm/releases/yasm-${YASM_VERSION}.tar.gz && \
tar xzvf yasm-${YASM_VERSION}.tar.gz && \
cd yasm-${YASM_VERSION} && \
./configure --prefix="$SRC" --bindir="${SRC}/bin" && \
@@ -54,9 +54,9 @@ RUN DIR=$(mktemp -d) && cd ${DIR} && \
# libmp3lame
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -L -Os http://downloads.sourceforge.net/project/lame/lame/${LAME_VERSION%.*}/lame-${LAME_VERSION}.tar.gz && \
tar xzvf lame-${LAME_VERSION}.tar.gz && \
cd lame-${LAME_VERSION} && \
curl -LOks https://github.com/rbrito/lame/archive/RELEASE__${LAME_VERSION}.tar.gz && \
tar xzvf RELEASE__${LAME_VERSION}.tar.gz && \
cd lame-RELEASE__${LAME_VERSION} && \
./configure --prefix="${SRC}" --bindir="${SRC}/bin" --disable-shared --enable-nasm && \
make && \
make install && \
@@ -65,7 +65,7 @@ RUN DIR=$(mktemp -d) && cd ${DIR} && \
# ffmpeg
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -Os http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz && \
curl -LOks https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz && \
tar xzvf ffmpeg-${FFMPEG_VERSION}.tar.gz && \
cd ffmpeg-${FFMPEG_VERSION} && \
./configure --prefix="${SRC}" --extra-cflags="-I${SRC}/include" --extra-ldflags="-L${SRC}/lib" --bindir="${SRC}/bin" \
@@ -84,21 +84,22 @@ RUN ffmpeg -buildconf
# nginx-rtmp
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -LOks http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
tar -zxvf nginx-${NGINX_VERSION}.tar.gz && \
curl -LOks https://github.com/arut/nginx-rtmp-module/archive/master.zip && \
unzip master.zip && \
rm master.zip && \
cd nginx-${NGINX_VERSION} && \
./configure --with-http_ssl_module --add-module=../nginx-rtmp-module-master && \
curl -LOks https://github.com/nginx/nginx/archive/release-${NGINX_VERSION}.tar.gz && \
tar xzvf release-${NGINX_VERSION}.tar.gz && \
curl -LOks https://github.com/arut/nginx-rtmp-module/archive/v${NGINX_RTMP_VERSION}.tar.gz && \
tar xzvf v${NGINX_RTMP_VERSION}.tar.gz && \
cd nginx-release-${NGINX_VERSION} && \
auto/configure --with-http_ssl_module --add-module=../nginx-rtmp-module-${NGINX_RTMP_VERSION} && \
make && \
make install && \
rm -rf ${DIR}
RUN apt-get purge -y --auto-remove ${BUILDDEPS} && \
apt-get install -y --force-yes git && \
rm -rf /tmp/*
RUN apt-get update && \
apt-get install -y --force-yes git
COPY . /restreamer
WORKDIR /restreamer

View File

@@ -2,30 +2,30 @@ FROM armbuild/debian:jessie
MAINTAINER datarhei <info@datarhei.org>
ENV NODE_VERSION 4.2.3
ENV NPM_VERSION 2.14.7
ENV NODE_VERSION 4.2.6
ENV NPM_VERSION 2.14.12
ENV FFMPEG_VERSION 2.8.1
ENV YASM_VERSION 1.3.0
ENV LAME_VERSION 3.99.5
ENV LAME_VERSION 3_99_5
ENV NGINX_VERSION 1.8.0
ENV NGINX_RTMP_VERSION 1.1.7
ENV SRC /usr/local
ENV LD_LIBRARY_PATH ${SRC}/lib
ENV PKG_CONFIG_PATH ${SRC}/lib/pkgconfig
ENV BUILDDEPS "autoconf automake gcc g++ libtool make nasm zlib1g-dev libssl-dev xz-utils cmake perl build-essential libpcre3-dev unzip"
ENV BUILDDEPS "autoconf automake gcc g++ libtool make nasm zlib1g-dev libssl-dev xz-utils cmake perl build-essential libpcre3-dev"
RUN rm -rf /var/lib/apt/lists/* && \
apt-get update && \
apt-get install -y curl wget git libpcre3 tar ${BUILDDEPS}
apt-get install -y curl git libpcre3 tar ${BUILDDEPS}
# node
RUN DIR=$(mktemp -d) && cd ${DIR} && \
set -x && \
wget "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-armv7l.tar.gz" && \
curl -LOks https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-armv7l.tar.gz && \
tar -xzf "node-v$NODE_VERSION-linux-armv7l.tar.gz" -C /usr/local --strip-components=1 && \
rm "node-v$NODE_VERSION-linux-armv7l.tar.gz" && \
npm install -g npm@"$NPM_VERSION" --unsafe-perm && \
npm cache clear && \
npm config set unsafe-perm true -g --unsafe-perm && \
@@ -33,7 +33,7 @@ RUN DIR=$(mktemp -d) && cd ${DIR} && \
# yasm
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -Os http://www.tortall.net/projects/yasm/releases/yasm-${YASM_VERSION}.tar.gz && \
curl -LOks https://www.tortall.net/projects/yasm/releases/yasm-${YASM_VERSION}.tar.gz && \
tar xzvf yasm-${YASM_VERSION}.tar.gz && \
cd yasm-${YASM_VERSION} && \
./configure --prefix="$SRC" --bindir="${SRC}/bin" && \
@@ -54,9 +54,9 @@ RUN DIR=$(mktemp -d) && cd ${DIR} && \
# libmp3lame
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -L -Os http://downloads.sourceforge.net/project/lame/lame/${LAME_VERSION%.*}/lame-${LAME_VERSION}.tar.gz && \
tar xzvf lame-${LAME_VERSION}.tar.gz && \
cd lame-${LAME_VERSION} && \
curl -LOks https://github.com/rbrito/lame/archive/RELEASE__${LAME_VERSION}.tar.gz && \
tar xzvf RELEASE__${LAME_VERSION}.tar.gz && \
cd lame-RELEASE__${LAME_VERSION} && \
./configure --prefix="${SRC}" --bindir="${SRC}/bin" --disable-shared --enable-nasm && \
make && \
make install && \
@@ -65,7 +65,7 @@ RUN DIR=$(mktemp -d) && cd ${DIR} && \
# ffmpeg
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -Os http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz && \
curl -LOks https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.gz && \
tar xzvf ffmpeg-${FFMPEG_VERSION}.tar.gz && \
cd ffmpeg-${FFMPEG_VERSION} && \
./configure --prefix="${SRC}" --extra-cflags="-I${SRC}/include" --extra-ldflags="-L${SRC}/lib" --bindir="${SRC}/bin" \
@@ -84,13 +84,12 @@ RUN ffmpeg -buildconf
# nginx-rtmp
RUN DIR=$(mktemp -d) && cd ${DIR} && \
curl -LOks http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz && \
tar -zxvf nginx-${NGINX_VERSION}.tar.gz && \
curl -LOks https://github.com/arut/nginx-rtmp-module/archive/master.zip && \
unzip master.zip && \
rm master.zip && \
cd nginx-${NGINX_VERSION} && \
./configure --with-http_ssl_module --add-module=../nginx-rtmp-module-master && \
curl -LOks https://github.com/nginx/nginx/archive/release-${NGINX_VERSION}.tar.gz && \
tar xzvf release-${NGINX_VERSION}.tar.gz && \
curl -LOks https://github.com/arut/nginx-rtmp-module/archive/v${NGINX_RTMP_VERSION}.tar.gz && \
tar xzvf v${NGINX_RTMP_VERSION}.tar.gz && \
cd nginx-release-${NGINX_VERSION} && \
auto/configure --with-http_ssl_module --add-module=../nginx-rtmp-module-${NGINX_RTMP_VERSION} && \
make && \
make install && \
rm -rf ${DIR}

View File

@@ -10,14 +10,14 @@ Datarhei/Restreamer offers smart free video streaming in real time. Stream H.264
- <a target= "_blank" href="https://www.docker.com/">Docker</a> and <a target= "_blank" href="https://kitematic.com/">Kitematic (Docker-Toolbox)</a> optimizations and very easy installation
##Roadmap
- RC4 (planned 1.25.2016)
- refactoring ffmpeg-fluent integration
- nginx process monitor
- cleanup packages
- optimizing ui (jsonDB processing, input-validation, clappr-integration)
- RC5 (planned 2.1.2016)
- code optimizations (full ecma6, lint, jsdocs)
- start writing tests
- input-validation
- optimizing scope of stats
- cleanup JSONDB
- start restructuring code
- cleanup packages
- NGINX process monitor
##Documentation
Documentation is available on [Datarhei/Restreamer GitHub pages](https://datarhei.github.io/restreamer/).

View File

@@ -1,6 +1,6 @@
{
"name": "Restreamer",
"version": "0.1.0-RC3",
"version": "0.1.0-RC4",
"license": "Apache-2.0",
"dependencies": {
"bootstrap": "3.3.6",

View File

@@ -63,7 +63,7 @@ module.exports = function(grunt) {
},
//temp workaround - https://github.com/clappr/clappr/issues/709
clappr: {
command: 'git clone https://github.com/clappr/clappr.git bin/webserver/public/libs/clappr'
command: 'git clone https://github.com/clappr/clappr bin/webserver/public/libs/clappr'
}
},
babel: {
@@ -83,19 +83,10 @@ module.exports = function(grunt) {
}
},
pkg: grunt.file.readJSON('package.json'),
jshint: {
all: {
src: [
'src/*.js',
'src/classes/*.js',
'src/webserver/*.js',
'src/webserver/**/*.js',
'src/webserver/**/**/*.js',
'src/webserver/**/**/**/*.js'
],
eslint: {
all: ['src/**/*.js'],
options: {
jshintrc: '.jshintrc'
}
configFile: '.eslintrc.json'
}
},
csslint: {
@@ -130,17 +121,11 @@ module.exports = function(grunt) {
}
}
},
env: {
test: {
NODE_ENV: 'test'
},
secure: {
NODE_ENV: 'secure'
}
}
});
// Load NPM tasks
/*
Load NPM tasks
*/
require('load-grunt-tasks')(grunt);
grunt.task.registerTask('loadConfig', 'Task that loads the config into a grunt option.', function() {
grunt.config.set('es6Src', files.es6Src);
@@ -148,15 +133,14 @@ module.exports = function(grunt) {
grunt.config.set('stylesheets', files.stylesheets);
});
grunt.loadNpmTasks('grunt-shell');
grunt.loadNpmTasks('grunt-contrib-copy')
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('gruntify-eslint');
grunt.loadNpmTasks('grunt-contrib-watch');
/*
Helper tasks to keep overview
*/
//css lint
grunt.registerTask('lint', ['csslint', 'jshint']);
// lint
grunt.registerTask('lint', ['csslint', 'eslint']);
// clear old bin folder and create new one
grunt.registerTask('clearOldBuild', ['shell:removeOldBinFolder', 'shell:createBinFolder']);
// install frontendlibraries (atm through bower)
@@ -178,5 +162,5 @@ module.exports = function(grunt) {
// rebuild and run
grunt.registerTask('run-clean', ['build', 'run']);
// update code and run
grunt.registerTask("run-update-code", ['loadConfig','babel','shell:copyStatics', 'minifyFrontendFiles', 'run'])
grunt.registerTask('run-update-code', ['loadConfig','babel','shell:copyStatics', 'minifyFrontendFiles', 'run'])
};

View File

@@ -1,6 +1,6 @@
{
"name": "Restreamer",
"version": "0.1.0-RC3",
"version": "0.1.0-RC4",
"description": "Allows you to do h.264 real-time video streaming on your website without a streaming provider",
"author": "datarhei.org",
"license": "Apache-2.0",
@@ -8,54 +8,35 @@
"start": "node ./bin/start"
},
"dependencies": {
"app-root-path": "1.0.0",
"async": "~1.5.0",
"body-parser": "1.14.2",
"bytes": "^2.1.0",
"colors": "^1.1.2",
"compression": "~1.6.0",
"connect-flash": "^0.1.1",
"cookie-parser": "1.4.0",
"debug": "2.2.0",
"dockerode": "2.2.8",
"ejs": "2.3.4",
"exec": "^0.2.1",
"express": "4.13.3",
"express-session": "^1.12.1",
"fluent-ffmpeg": "git://github.com/christophjohannsdotter/node-fluent-ffmpeg#FIX_stderr-variable_of_infinite_growth",
"i18n": "0.5.0",
"fluent-ffmpeg": "git://github.com/datarhei/node-fluent-ffmpeg",
"jsonschema": "^1.0.2",
"moment-timezone": "^0.5.0",
"morgan": "1.6.1",
"node-json-db": "^0.5.1",
"passport": "^0.3.2",
"passport-local": "^1.0.0",
"q": "1.4.1",
"q-io": "~1.13.1",
"request": "2.67.0",
"sequent": "0.1.2",
"serve-favicon": "2.3.0",
"shortid": "2.2.4",
"socket.io": "1.4.0"
},
"devDependencies": {
"babel-preset-es2015": "^6.1.2",
"chai": "^3.3.0",
"grunt": "^0.4.5",
"grunt-babel": "^6.0.0",
"grunt-concurrent": "~2.1.0",
"grunt-contrib-copy": "0.8",
"grunt-contrib-csslint": "^0.5.0",
"grunt-contrib-cssmin": "~0.14.0",
"grunt-contrib-jshint": "~0.11.3",
"grunt-contrib-uglify": "~0.11.0",
"grunt-contrib-watch": "^0.6.1",
"grunt-env": "~0.4.1",
"grunt-ng-annotate": "~1.0.1",
"grunt-nodemon": "~0.4.1",
"grunt-nw-builder": "^2.0.0",
"grunt-shell": "^1.1.2",
"jshint-stylish": "^2.0.0",
"gruntify-eslint": "^1.3.0",
"load-grunt-tasks": "~3.4.0"
}
}

30
run.sh
View File

@@ -1,12 +1,36 @@
#!/bin/bash
cpu=$(uname -m | cut -c 1-3)
if [ "${MODE}" == "RASPICAM" ] && [ "$cpu" == "arm" ];
CPU_TYPE=$(uname -m | cut -c 1-3)
if [ "${MODE}" == "RASPICAM" ] && [ "$CPU_TYPE" == "arm" ];
then
echo "/opt/vc/lib" > /etc/ld.so.conf.d/00-vmcs.conf
ldconfig
npm start &
sleep 20
NGINX_RUNNING=0
until [ "$NGINX_RUNNING" = "1" ]; do
if pgrep "nginx" > /dev/null
then
NGINX_RUNNING=1
else
NGINX_RUNNING=0
sleep 5
fi
done
/opt/vc/bin/raspivid -t 0 -w 1280 -h 720 -fps 25 -b 500000 -o - | ffmpeg -i - -c copy -f flv rtmp://127.0.0.1:1935/live/raspicam.stream
elif [ "${MODE}" == "USBCAM" ];
then
apt-get update && apt-get install -y v4l-utils libv4l-0
npm start &
NGINX_RUNNING=0
until [ "$NGINX_RUNNING" = "1" ]; do
if pgrep "nginx" > /dev/null
then
NGINX_RUNNING=1
else
NGINX_RUNNING=0
sleep 5
fi
done
ffmpeg -f v4l2 -r 25 -s 1280x720 -i /dev/video0 -f flv rtmp://127.0.0.1:1935/live/usb.stream
else
npm start
fi

View File

@@ -7,16 +7,14 @@
'use strict';
const moment = require("moment-timezone");
require("colors");
const moment = require('moment-timezone');
const LEVEL_ERROR = 1;
const LEVEL_WARN = 2;
const LEVEL_INFO = 3;
const LEVEL_DEBUG = 4;
//set default timezone to use the timezone before the default values are
process.env.TIMEZONE = process.env.TIMEZONE ? process.env.TIMEZONE : "Europe/Berlin";
process.env.TIMEZONE = process.env.TIMEZONE ? process.env.TIMEZONE : 'Europe/Berlin';
/**
* Class for logger
@@ -51,11 +49,11 @@ class Logger {
return;
}
if (context === false){
context = "";
context = '';
}else{
context = "(" + context + ")";
context = '(' + context + ')';
}
var str = "[" + (moment().tz(process.env.TIMEZONE).format('DD-MM-YYYY HH:mm:ss.SSS')) + "] [" + type + "] " + message + " " + context;
var str = '[' + (moment().tz(process.env.TIMEZONE).format('DD-MM-YYYY HH:mm:ss.SSS')) + '] [' + type + '] ' + message + ' ' + context;
if (process.env.LOGGER_LEVEL === '4') {
console.log(str[color]);
} else {
@@ -76,7 +74,7 @@ class Logger {
alertGui = false;
}
if (process.env.LOGGER_LEVEL >= LEVEL_INFO) {
return this.stdout(message, context, "INFO", "blue");
return this.stdout(message, context, 'INFO', 'blue');
}
if (alertGui){
//todo: if alertGui is activated on frontend and websocketcontroller, insert emit here
@@ -96,7 +94,7 @@ class Logger {
alertGui = false;
}
if (process.env.LOGGER_LEVEL >= LEVEL_WARN) {
return this.stdout(message, context, "WARN", "orange");
return this.stdout(message, context, 'WARN', 'orange');
}
if (alertGui) {
//todo: if alertGui is activated on frontend and websocketcontroller, insert emit here
@@ -116,7 +114,7 @@ class Logger {
alertGui = false;
}
if (process.env.LOGGER_LEVEL >= LEVEL_DEBUG) {
return this.stdout(message, context, "DEBUG", "yellow");
return this.stdout(message, context, 'DEBUG', 'yellow');
}
if (alertGui){
//todo: if alertGui is activated on frontend and websocketcontroller, insert emit here
@@ -137,7 +135,7 @@ class Logger {
alertGui = false;
}
if (process.env.LOGGER_LEVEL >= LEVEL_ERROR) {
return this.stdout(message, context, "ERROR", "red");
return this.stdout(message, context, 'ERROR', 'red');
}
if (alertGui){
//todo: if alertGui is activated on frontend and websocketcontroller, insert emit here

View File

@@ -5,34 +5,25 @@
* @license Apache-2.0
*/
const config = require("../../conf/live.json");
const Logger = require("./Logger");
const logger = new Logger("Restreamer");
const WebsocketsController = require("./WebsocketController");
const config = require('../../conf/live.json');
const Logger = require('./Logger');
const logger = new Logger('Restreamer');
const WebsocketsController = require('./WebsocketController');
const FfmpegCommand = require('fluent-ffmpeg');
const Q = require("q");
const Q = require('q');
/**
* class Restreamer creates and manages streams through ffmpeg
*/
class Restreamer{
/**
* generate output rtmp-path from config-file
* @returns {string}
*/
static generateOutputRTMPPath (){
var nginx = config.nginx.streaming;
return "rtmp://" + nginx.ip + ":" + nginx.rtmp_port + nginx.rtmp_path + "live.stream";
}
/**
* generate output hls-path from config-file
* @returns {string}
*/
static generateOutputHLSPath (){
var nginx = config.nginx.streaming;
return "rtmp://" + nginx.ip + ":" + nginx.rtmp_port + nginx.hls_path + "live.stream";
return 'rtmp://' + nginx.ip + ':' + nginx.rtmp_port + nginx.rtmp_hls_path + 'live.stream';
}
/**
@@ -40,8 +31,8 @@ class Restreamer{
* @returns {string}
*/
static generateSnapshotPath (){
const path = require("path");
return path.join(__dirname, "..", "webserver", "public", "images", "live.jpg");
const path = require('path');
return path.join(__dirname, '..', 'webserver', 'public', 'images', 'live.jpg');
}
/**
@@ -51,14 +42,14 @@ class Restreamer{
static fetchSnapshot(firstSnapshot){
if (Restreamer.data.states.repeatToLocalNginx.type === 'connected' || firstSnapshot){
var command = new FfmpegCommand(Restreamer.generateOutputRTMPPath());
var command = new FfmpegCommand(Restreamer.generateOutputHLSPath());
command.output(Restreamer.generateSnapshotPath());
command.outputOption(config.ffmpeg.options.snapshot);
command.on('error', (error)=> {
logger.error("Error on fetching snapshot: " + error.toString());
logger.error('Error on fetching snapshot: ' + error.toString());
});
command.on("end", () =>{
logger.info("updated snapshot");
command.on('end', () =>{
logger.info('updated snapshot');
Q.delay(process.env.SNAPSHOT_REFRESH_INTERVAL).then(function(){
Restreamer.fetchSnapshot(false);
});
@@ -72,13 +63,13 @@ class Restreamer{
* @param {string} processName
*/
static stopStream(processName){
logger.info("stopStream " + processName);
Restreamer.updateState(processName, "stopped");
logger.info('stopStream ' + processName);
Restreamer.updateState(processName, 'stopped');
var processHasBeenSpawned = typeof Restreamer.data.processes[processName].kill === 'function';
if(processHasBeenSpawned){
Restreamer.data.processes[processName].kill();
Restreamer.data.processes[processName] = {
state: "not_connected"
state: 'not_connected'
};
}
}
@@ -88,26 +79,26 @@ class Restreamer{
* after the applicatoin has been killed or stuff
*/
static restoreFFMpegProcesses(){
var JsonDB = require("node-json-db");
var JsonDB = require('node-json-db');
var db = new JsonDB(config.jsondb, true, false);
try {
Restreamer.data.addresses = db.getData("/addresses");
Restreamer.data.states = db.getData("/states");
Restreamer.data.userActions = db.getData("/userActions");
Restreamer.data.addresses = db.getData('/addresses');
Restreamer.data.states = db.getData('/states');
Restreamer.data.userActions = db.getData('/userActions');
//check if the srcAddress has been repeated to Local Nginx
var repeatToLocalNginxIsConnected = Restreamer.data.states.repeatToLocalNginx.type === 'connected';
var repeatToLocalNginxIsConnecting = Restreamer.data.states.repeatToLocalNginx.type === 'connecting';
var repeatToOptionalOutputIsConnected = Restreamer.data.states.repeatToOptionalOutput.type === 'connected';
var repeatToOptionalOutputIsConnecting = Restreamer.data.states.repeatToOptionalOutput.type === 'connecting';
if (Restreamer.data.addresses.srcAddress && (!!repeatToLocalNginxIsConnected || !!repeatToLocalNginxIsConnecting)) {
Restreamer.startStream(Restreamer.data.addresses.srcAddress, "repeatToLocalNginx");
Restreamer.startStream(Restreamer.data.addresses.srcAddress, 'repeatToLocalNginx');
}
if (Restreamer.data.addresses.optionalOutputAddress && (!!repeatToOptionalOutputIsConnected || !!repeatToOptionalOutputIsConnecting)) {
Restreamer.startStream(Restreamer.data.addresses.srcAddress, "repeatToOptionalOutput", Restreamer.data.addresses.optionalOutputAddress);
Restreamer.startStream(Restreamer.data.addresses.srcAddress, 'repeatToOptionalOutput', Restreamer.data.addresses.optionalOutputAddress);
}
}
catch(error){
logger.error("error restoring ffmpeg process: " + error);
logger.error('error restoring ffmpeg process: ' + error);
}
}
@@ -115,23 +106,23 @@ class Restreamer{
* write JSON file for persistency
*/
static writeToDB(){
var JsonDB = require("node-json-db");
var JsonDB = require('node-json-db');
var db = new JsonDB(config.jsondb, true, false);
db.push("/", Restreamer.extractDataOfStreams());
db.push('/', Restreamer.extractDataOfStreams());
}
/**
* send websocket event to GUI to update the state of the streams
*/
static updateStreamDataOnGui(){
WebsocketsController.emitToNamespace("/", "updateStreamData", Restreamer.extractDataOfStreams());
WebsocketsController.emitToNamespace('/', 'updateStreamData', Restreamer.extractDataOfStreams());
}
/**
* send websocket event to GUI to update the state of the streams
*/
static updateProgressOnGui(){
WebsocketsController.emitToNamespace("/", "updateProgress", Restreamer.data.progresses);
WebsocketsController.emitToNamespace('/', 'updateProgress', Restreamer.data.progresses);
}
/**
@@ -159,10 +150,10 @@ class Restreamer{
var ffmpegOptions;
if(data.streams.length > 1) {
ffmpegOptions = config.ffmpeg.options.native_h264;
logger.debug("Selected ffmpeg.option: native_h264");
logger.debug(`Selected ffmpeg.option: native_h264`);
}else{
ffmpegOptions = config.ffmpeg.options.native_h264_soundless_aac;
logger.debug("Selected ffmpeg.option: native_h264_soundless_aac");
logger.debug(`Selected ffmpeg.option: native_h264_soundless_aac`);
}
for (let option of ffmpegOptions){
ffmpegCommand.outputOption(option);
@@ -181,7 +172,7 @@ class Restreamer{
* @return {string} name of the new state
*/
static updateState(processName, state, message){
logger.debug("Update state of '" + processName + "' from state '" + Restreamer.data.states[processName].type + "' to state '" + state + "'");
logger.debug(`Update state of "${processName}" from state "${Restreamer.data.states[processName].type}" to state "${state}"`);
Restreamer.data.states[processName] = {
type: state,
message: message
@@ -198,7 +189,7 @@ class Restreamer{
* @return {string} name of the new user action
*/
static updateUserAction(processName, action){
logger.debug("Set useraction of '" + processName + "' from '" + Restreamer.data.userActions[processName] + "' to '" + action + "'");
logger.debug(`Set useraction of "${processName}" from "${Restreamer.data.userActions[processName]}" to "${action}"`);
Restreamer.data.userActions[processName] = action;
Restreamer.writeToDB();
Restreamer.updateStreamDataOnGui();
@@ -214,7 +205,7 @@ class Restreamer{
* @param {string} retryCounter current value of the retry counter (startStream retries automatically if anything fails)
*/
static startStream(src, streamType, optionalOutput, retryCounter){
logger.info("start stream " + streamType);
logger.info(`Start stream "${streamType}"`);
//update the retry counter for the streamType
Restreamer.data.retryCounter[streamType] = typeof retryCounter === 'undefined' ? 0 : retryCounter;
@@ -233,34 +224,32 @@ class Restreamer{
// check if the user has clicked 'stop' meanwhile, so the startStream process has to be skipped
if (Restreamer.data.userActions[streamType] === 'stop'){
logger.debug("skipping 'startStream' since 'stopped' has been clicked");
logger.debug(`Skipping "startStream" since "stopped" has been clicked`);
return;
}
// update the state on the frontend
Restreamer.updateState(streamType, "connecting");
// update the state and src address on the frontend
Restreamer.data.addresses.srcAddress = src;
Restreamer.updateState(streamType, 'connecting');
//repeat to local nginx server
if (repeatToLocalNginx){
command = new FfmpegCommand(src, {
outputLineLimit: 50
outputLineLimit: 1
});
//add outputs to the ffmpeg stream
addOutputPromise = Restreamer.addOutput(command, Restreamer.generateOutputRTMPPath())
.then(function(){
return Restreamer.addOutput(command, Restreamer.generateOutputHLSPath());
})
addOutputPromise = Restreamer.addOutput(command, Restreamer.generateOutputHLSPath())
.catch(function(error){
logger.error("error adding one or more outputs: " + error.toString);
logger.error(`Error adding one or more outputs: ${error.toString}`);
});
//repeat to optional output
}else if (repeatToOptionalOutput){
command = new FfmpegCommand(Restreamer.generateOutputRTMPPath(src, {
outputLineLimit: 50
}));
command = new FfmpegCommand(Restreamer.generateOutputHLSPath(), {
outputLineLimit: 1
});
Restreamer.data.addresses.optionalOutputAddress = optionalOutput;
addOutputPromise = Restreamer.addOutput(command, optionalOutput);
}
@@ -273,12 +262,11 @@ class Restreamer{
*/
.on('start', function(commandLine){
if (Restreamer.data.userActions[streamType] === 'stop'){
logger.debug("skipping on 'start' event of ffmpeg command since 'stopped' has been clicked");
logger.debug(`Skipping on "start" event of FFmpeg command since "stopped" has been clicked`);
return;
}else{
logger.debug("FFMPEG spawned: " + commandLine);
logger.debug(`FFmpeg spawned: ${commandLine}`);
Restreamer.data.processes[streamType] = command;
Restreamer.data.addresses.srcAddress = src;
//fetch snapshot only, if repeated to local nginx
if (repeatToLocalNginx){
Restreamer.fetchSnapshot(true);
@@ -290,20 +278,20 @@ class Restreamer{
ERROR HANDLER
*/
.on('error', (error)=>{
if (error.toString().indexOf("SIGKILL") > -1) {
Restreamer.updateState(streamType, "disconnected");
logger.info("FFMPEG streaming stopped for " + streamType);
if (error.toString().indexOf('SIGKILL') > -1) {
Restreamer.updateState(streamType, 'disconnected');
logger.info(`FFmpeg streaming stopped for "${streamType}"`);
}else{
Restreamer.data.retryCounter[streamType]++;
if (Restreamer.data.retryCounter[streamType] <= config.ffmpeg.monitor.retries) {
if (Restreamer.data.userActions[streamType] === 'stop'){
logger.debug("skipping retry since 'stopped' has been clicked");
logger.debug(`Skipping retry since "stopped" has been clicked`);
return;
}else{
Restreamer.updateState(streamType, "error", error.toString());
logger.info("Retrying ffmpeg connection to " + src + " after " + config.ffmpeg.monitor.restart_wait + "ms");
Restreamer.updateState(streamType, 'error', error.toString());
logger.info(`Retrying FFmpeg connection to "${src}" after "${config.ffmpeg.monitor.restart_wait} ms`);
Q.delay(config.ffmpeg.monitor.restart_wait).then(function () {
logger.info("Retry ffmpeg connection to " + src + " retrycounter:" + Restreamer.data.retryCounter[streamType]);
logger.info(`Retry FFmpeg connection to "${src}" retrycounter: ${Restreamer.data.retryCounter[streamType]}`);
Restreamer.startStream(src, streamType, optionalOutput, Restreamer.data.retryCounter[streamType]);
});
}
@@ -315,16 +303,16 @@ class Restreamer{
STREAMING ENDED
*/
.on('end',()=>{
Restreamer.updateState(streamType, "disconnected");
Restreamer.updateState(streamType, 'disconnected');
Restreamer.data.retryCounter[streamType]++;
if (Restreamer.data.retryCounter[streamType] <= config.ffmpeg.monitor.retries) {
if (Restreamer.data.userActions[streamType] === 'stop'){
logger.debug("skipping retry since 'stopped' has been clicked");
logger.debug(`Skipping retry since "stopped" has been clicked`);
return;
}else{
logger.info("Retrying ffmpeg connection to " + src + " after " + config.ffmpeg.monitor.restart_wait + "ms");
logger.info(`Retrying FFmpeg connection to "${src}" after "${config.ffmpeg.monitor.restart_wait}" ms`);
Q.delay(config.ffmpeg.monitor.restart_wait).then(function () {
logger.info("Retry ffmpeg connection to " + src + " retrycounter:" + Restreamer.data.retryCounter[streamType]);
logger.info(`Retry FFmpeg connection to "${src}" retrycounter: ${Restreamer.data.retryCounter[streamType]}`);
Restreamer.startStream(src, streamType, optionalOutput, Restreamer.data.retryCounter[streamType]);
});
}
@@ -336,21 +324,21 @@ class Restreamer{
*/
var progressMethod = function (progress){
if (Restreamer.data.states[streamType].type === 'connecting'){
Restreamer.updateState(streamType, "connected");
Restreamer.updateState(streamType, 'connected');
}
Restreamer.data.progresses[streamType] = progress;
Restreamer.updateProgressOnGui();
command.removeAllListeners("progress");
command.removeAllListeners('progress');
};
command.on('progress', progressMethod);
setInterval(()=>{
if(command.listeners("progress").length === 0) {
if(command.listeners('progress').length === 0) {
command.on('progress', progressMethod);
}
}, 1000);
command.exec();
}).catch(function(error){
logger.error("Error starting ffmpeg command: " + error.toString());
logger.error(`Error starting FFmpeg command: ${error.toString()}`);
});
}
@@ -358,20 +346,20 @@ class Restreamer{
* binded on app-start to bind websocketevents of Restreamer
*/
static bindWebsocketEvents(){
WebsocketsController.addOnConnectionEventToNamespace("/", function(socket){
socket.on("startStream", (options)=> {
Restreamer.updateUserAction(options.streamType, "start");
WebsocketsController.addOnConnectionEventToNamespace('/', function(socket){
socket.on('startStream', (options)=> {
Restreamer.updateUserAction(options.streamType, 'start');
Restreamer.startStream(options.src, options.streamType, options.optionalOutput);
});
socket.on("stopStream", (streamType)=>{
Restreamer.updateUserAction(streamType, "stop");
socket.on('stopStream', (streamType)=>{
Restreamer.updateUserAction(streamType, 'stop');
Restreamer.stopStream(streamType);
});
socket.on("checkForAppUpdates", ()=>{
const app = require("../webserver/app");
socket.emit("checkForAppUpdatesResult", app.get("updateAvailable"));
socket.on('checkForAppUpdates', ()=>{
const app = require('../webserver/app');
socket.emit('checkForAppUpdatesResult', app.get('updateAvailable'));
});
socket.on("checkStates", Restreamer.updateStreamDataOnGui);
socket.on('checkStates', Restreamer.updateStreamDataOnGui);
});
}
@@ -400,26 +388,26 @@ Restreamer.data = {
},
states: {
repeatToLocalNginx: {
type: "disconnected",
message: ""
type: 'disconnected',
message: ''
},
repeatToOptionalOutput: {
type: "disconnected",
message: ""
type: 'disconnected',
message: ''
}
},
userActions: {
repeatToLocalNginx: "start",
repeatToOptionalOutput: "start"
repeatToLocalNginx: 'start',
repeatToOptionalOutput: 'start'
},
processes: {
//overwritten with ffmpeg process if stream has been started
repeatToLocalNginx: {
state: "not_connected"
state: 'not_connected'
},
//overwritten with ffmpeg process if stream has been started
repeatToOptionalOutput: {
state: "not_connected"
state: 'not_connected'
}
},
progresses: {
@@ -429,8 +417,8 @@ Restreamer.data = {
repeatToOptionalOutput: {}
},
addresses: {
srcAddress: "",
optionalOutputAddress: ""
srcAddress: '',
optionalOutputAddress: ''
}
};

View File

@@ -6,10 +6,8 @@
*/
'use strict';
const async = require("async");
const Logger = require("../classes/Logger");
const logger = new Logger("WebsocketsController");
const Logger = require('../classes/Logger');
const logger = new Logger('WebsocketsController');
/**
@@ -26,9 +24,9 @@ class WebsocketsController {
* @param {object} data data to emit to the client event listener
*/
static emitToNamespace(namespace, event, data) {
var app = require("../webserver/app");
app.get("websocketsReady").promise.then(function (io) {
logger.debug("websocket got event " + event + " to namespace " + namespace + "","Websockets");
var app = require('../webserver/app');
app.get('websocketsReady').promise.then(function (io) {
logger.debug('websocket got event ' + event + ' to namespace ' + namespace + '','Websockets');
io.of(namespace).emit(event, data);
});
}
@@ -39,10 +37,10 @@ class WebsocketsController {
* @param {function} callback
*/
static addOnConnectionEventToNamespace(namespace, callback) {
var app = require("../webserver/app");
app.get("websocketsReady").promise.then(function (io) {
var app = require('../webserver/app');
app.get('websocketsReady').promise.then(function (io) {
var nsp = io.of(namespace);
nsp.on("connection", function (socket) {
nsp.on('connection', function (socket) {
callback(socket);
});
});
@@ -52,15 +50,15 @@ class WebsocketsController {
* bind default events of all classes that are using websocketevents
*/
static bindDefaultEvents() {
WebsocketsController.addOnConnectionEventToNamespace("/", function (socket) {
socket.on("testConnection", (options)=> {
var packageJson = require("../../package.json");
socket.emit("connection", packageJson.version);
WebsocketsController.addOnConnectionEventToNamespace('/', function (socket) {
socket.on('getVersion', (options)=> {
var packageJson = require('../../package.json');
socket.emit('version', packageJson.version);
});
var app = require("../webserver/app");
socket.emit("publicIp", app.get("publicIp"));
var app = require('../webserver/app');
socket.emit('publicIp', app.get('publicIp'));
});
require("./Restreamer").bindWebsocketEvents();
require('./Restreamer').bindWebsocketEvents();
}
}

View File

@@ -10,59 +10,60 @@
/*
requirements
*/
const Logger = require("./classes/Logger");
const logger = new Logger("start");
const EnvVar = require("./classes/EnvVar");
const packageJson = require("../package.json");
const Logger = require('./classes/Logger');
const logger = new Logger('start');
const EnvVar = require('./classes/EnvVar');
const packageJson = require('../package.json');
const app = require('./webserver/app');
const config = require('../conf/live.json');
const Q = require("q");
const Q = require('q');
if(process.env.CREATE_HEAPDUMPS === 'true'){
const heapdump = require('heapdump');
setInterval(function(){
heapdump.writeSnapshot('heapdump/' + Date.now() + '.heapsnapshot');
logger.info(`CREATE HEAPDUMP`);
heapdump.writeSnapshot('/restreamer/heapdump/' + Date.now() + '.heapsnapshot');
}, 60000);
}
if (typeof process.env.LOGGER_LEVEL === 'undefined'){
process.env.LOGGER_LEVEL = "3";
process.env.LOGGER_LEVEL = '3';
}
/*
init simple_streamer with environments
*/
logger.info(" _ _ _ _ ", false);
logger.info(" __| | __ _| |_ __ _ _ __| |___ ___(_)", false);
logger.info(" / _ |/ _ | __/ _ | __| _ |/ _ | |", false);
logger.info("| (_| | (_| | || (_| | | | | | | __/| |", false);
logger.info("|_____|_____|_||_____|_| |_| |_|____||_|", false);
logger.info("", false);
logger.info("Restreamer v" + packageJson.version, false);
logger.info("", false);
logger.info("ENVIRONMENTS", false);
logger.info("More informations in our Docs", false);
logger.info("", false);
logger.info(' _ _ _ _ ', false);
logger.info(' __| | __ _| |_ __ _ _ __| |___ ___(_)', false);
logger.info(' / _ |/ _ | __/ _ | __| _ |/ _ | |', false);
logger.info('| (_| | (_| | || (_| | | | | | | __/| |', false);
logger.info('|_____|_____|_||_____|_| |_| |_|____||_|', false);
logger.info('', false);
logger.info('Restreamer v' + packageJson.version, false);
logger.info('', false);
logger.info('ENVIRONMENTS', false);
logger.info('More informations in our Docs', false);
logger.info('', false);
// define environment variables
var env_vars = [];
env_vars.push(new EnvVar("NODEJS_PORT", false, 3000, "Webserver port of application"));
env_vars.push(new EnvVar("LOGGER_LEVEL", true, "3", "Logger level to defined, what should be logged"));
env_vars.push(new EnvVar("TIMEZONE", true, "Europe/Berlin", "Set the timezone"));
env_vars.push(new EnvVar("SNAPSHOT_REFRESH_INTERVAL", false, 60000, "Interval to create a new Snapshot"));
env_vars.push(new EnvVar("CREATE_HEAPDUMPS", false, "false", "Create Heapdumps of application"));
env_vars.push(new EnvVar('NODEJS_PORT', false, 3000, 'Webserver port of application'));
env_vars.push(new EnvVar('LOGGER_LEVEL', true, '3', 'Logger level to defined, what should be logged'));
env_vars.push(new EnvVar('TIMEZONE', true, 'Europe/Berlin', 'Set the timezone'));
env_vars.push(new EnvVar('SNAPSHOT_REFRESH_INTERVAL', false, 60000, 'Interval to create a new Snapshot'));
env_vars.push(new EnvVar('CREATE_HEAPDUMPS', false, 'false', 'Create Heapdumps of application'));
// manage all environments
var killProcess = false;
for (let e of env_vars){
if(typeof process.env[e.name] !== 'undefined'){
logger.info("ENV \"" + e.name + "=" + process.env[e.name] + "\", " + e.description);
logger.info(`ENV "${e.name}=${process.env[e.name]}"`, e.description);
}else if(e.required === true){
logger.error("No value set for env " + e.name + ", but it is required");
logger.error(`No value set for env "${e.name}", but it is required`);
killProcess = true;
}else{
process.env[e.name] = e.defaultValue;
logger.info("ENV \"" + e.name + "=" + process.env[e.name] + "\", set to default-value!, " + e.description);
logger.info(`ENV "${e.name}=${process.env[e.name]}", set to default-value!`, e.description);
}
}
@@ -72,68 +73,68 @@ if(killProcess === true){
process.exit();
}, 500);
}
logger.info("", false);
logger.info('', false);
/**
* check if the data from jsondb is valid against the Restreamer jsondb schema
* @returns {promise}
*/
const checkJsonDb = function(){
logger.info("checking jsondb file...");
logger.info(`Checking jsondb file...`);
var Validator = require('jsonschema').Validator;
var fs = require("fs");
var path = require("path");
var fs = require('fs');
var path = require('path');
var schemadata;
var dbdata;
var deferred = Q.defer();
var readSchema = Q.nfcall(fs.readFile, path.join(__dirname, "../", "conf", "jsondb_v1_schema.json"));
var readDBFile = Q.nfcall(fs.readFile, path.join(__dirname, "../", "db", "v1.json"));
var readSchema = Q.nfcall(fs.readFile, path.join(__dirname, '../', 'conf', 'jsondb_v1_schema.json'));
var readDBFile = Q.nfcall(fs.readFile, path.join(__dirname, '../', 'db', 'v1.json'));
readSchema
.then(function(s){
schemadata = JSON.parse(s.toString("utf8"));
schemadata = JSON.parse(s.toString('utf8'));
return readDBFile;
})
.then(function(d){
dbdata = JSON.parse(d.toString("utf8"));
dbdata = JSON.parse(d.toString('utf8'));
var v = new Validator();
var instance = dbdata;
var schema = schemadata;
var validateResult = v.validate(instance, schema);
if (validateResult.errors.length > 0){
logger.debug("validation error of v1.db: " + JSON.stringify(validateResult.errors));
logger.debug(`Validation error of v1.db: ${JSON.stringify(validateResult.errors)}`);
throw new Error(JSON.stringify(validateResult.errors));
}else{
logger.debug("v1.db is valid");
logger.debug(`"v1.db" is valid`);
deferred.resolve();
}
}).catch(function(error){
logger.debug("error reading v1.db:" + error.toString());
logger.debug(`Error reading "v1.db": ${error.toString()}`);
var defaultStructure = {
addresses: {
srcAddress: "",
optionalOutputAddress: ""
srcAddress: '',
optionalOutputAddress: ''
},
states: {
repeatToLocalNginx: {
type: "stopped"
type: 'stopped'
},
repeatToOptionalOutput: {
type: "stopped"
type: 'stopped'
}
},
userActions:{
repeatToLocalNginx: "stop",
repeatToOptionalOutput: "stop"
repeatToLocalNginx: 'stop',
repeatToOptionalOutput: 'stop'
},
progresses: {
repeatToLocalNginx: {},
repeatToOptionalOutput: {}
}
};
fs.writeFileSync(path.join(__dirname, "../", "db", "v1.json"), JSON.stringify(defaultStructure));
fs.writeFileSync(path.join(__dirname, '../', 'db', 'v1.json'), JSON.stringify(defaultStructure));
deferred.resolve();
});
return deferred.promise;
@@ -144,7 +145,7 @@ const checkJsonDb = function(){
* @returns {promise}
*/
const startNginxRTMPServer = function startNginxRTMPServer(){
logger.info("starting nginx server....", "start.nginx");
logger.info(`Starting nginx server...`, 'start.nginx');
var deferred = Q.defer();
const command = config.nginx.exec;
const spawn = require('child_process').spawn;
@@ -158,15 +159,15 @@ const startNginxRTMPServer = function startNginxRTMPServer(){
* @returns {promise}
*/
const startWebserver = function startWebserver(){
logger.info("starting webserver...");
logger.info(`Starting webserver...`);
var deferred = Q.defer();
app.set('port', process.env.NODEJS_PORT);
var server = app.listen(app.get('port'), function () {
require("./classes/WebsocketController").bindDefaultEvents();
app.set("io", require('socket.io')(server));
app.set("server", server.address());
app.get("websocketsReady").resolve(app.get("io")); //promise to determine if the webserver has been started to avoid ws binding before
logger.info("Webserver running on port " + process.env.NODEJS_PORT, "start.webserver");
require('./classes/WebsocketController').bindDefaultEvents();
app.set('io', require('socket.io')(server));
app.set('server', server.address());
app.get('websocketsReady').resolve(app.get('io')); //promise to determine if the webserver has been started to avoid ws binding before
logger.info(`Webserver running on port ${process.env.NODEJS_PORT}`, 'start.webserver');
deferred.resolve(server.address().port);
});
return deferred.promise;
@@ -176,13 +177,13 @@ const startWebserver = function startWebserver(){
* get the public ip of the server, on that the Restreamer is running
*/
const getPublicIp = function(){
logger.info("Getting public ip...", "start.publicip");
var exec = require("child_process").exec;
exec("public-ip", (err, stdout, stderr)=> {
logger.info(`Getting public ip...`, 'start.publicip');
var exec = require('child_process').exec;
exec('public-ip', (err, stdout, stderr)=> {
if (err){
console.log(err);
logger.error(err);
}
app.set("publicIp", stdout.split("\n")[0] );
app.set('publicIp', stdout.split('\n')[0] );
});
};
@@ -191,9 +192,9 @@ const getPublicIp = function(){
* @returns {promise}
*/
const restoreFFMPEGProcesses = function(){
logger.info("Restoring ffmpeg processes...", "start.restore");
logger.info(`Restoring FFmpeg processes...`, 'start.restore');
var deferred = Q.defer();
const Restreamer = require("./classes/Restreamer");
const Restreamer = require('./classes/Restreamer');
Restreamer.restoreFFMpegProcesses();
deferred.resolve();
return deferred.promise;
@@ -209,7 +210,7 @@ startNginxRTMPServer()
.then(restoreFFMPEGProcesses)
.then(getPublicIp)
.catch(function(error){
logger.error("error starting webserver and nginx for application: " + error);
logger.error(`Error starting webserver and nginx for application: ${error}`);
setTimeout(()=> {
process.exit();
}, 500);

View File

@@ -17,26 +17,25 @@ const flash = require('connect-flash');
//express
const express = require('express');
const session = require('express-session');
const i18n = require('i18n');
const favicon = require('serve-favicon');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const compression = require("compression");
const compression = require('compression');
//other
const path = require('path');
const Q = require("q");
const request = require("request");
const crypto = require("crypto");
const Q = require('q');
const request = require('request');
const crypto = require('crypto');
/*
modules
*/
const packageJson = require("../../package.json");
const Logger = require("../classes/Logger");
const logger = new Logger("Webserver");
const packageJson = require('../../package.json');
const Logger = require('../classes/Logger');
const logger = new Logger('Webserver');
//middlewares
const expressLogger = require("./middlewares/expressLogger");
const expressLogger = require('./middlewares/expressLogger');
//create express app
const app = express();
@@ -49,27 +48,17 @@ app.use(session({
resave: false,
saveUninitialized: true})); // session secret
//create promise for "websockets ready"
app.set("websocketsReady", Q.defer());
//create promise for 'websockets ready'
app.set('websocketsReady', Q.defer());
//add passport auth
app.use(passport.initialize());
app.use(flash()); // use connect-flash for flash messages stored in session
app.use(passport.session()); // persistent login sessions
require("./config/passport")(passport);
//configure i18n
i18n.configure({
locales:['en','de'],
directory: __dirname + '/locales',
defaultLocale: 'en',
cookie: 'locale',
updateFiles: false
});
require('./config/passport')(passport);
//configure express app
app.use(favicon(__dirname + '/public/images/favicon.ico'));
app.use(i18n.init);
app.use(bodyParser.json());
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
@@ -78,7 +67,7 @@ app.use(cookieParser());
app.use(compression());
app.set('json spaces', 4);
require("./controllers/index")(app, passport);
require('./controllers/index')(app, passport);
app.use('/', expressLogger);
// catch 404 and forward to error handler
@@ -98,7 +87,7 @@ app.use(function(err, req, res, next) {
});
var checkForAppUpdates = function(){
logger.debug("checking app for updates...");
logger.debug(`Checking app for updates...`);
const url = 'http://datarhei.org/apps.json';
request(url, function (error, response, body) {
if (!error && response.statusCode === 200) {
@@ -106,15 +95,15 @@ var checkForAppUpdates = function(){
var updateAvailable = false;
if (updateCheck.restreamer.version === packageJson.version){
updateAvailable = false;
logger.debug("checking app for updates successful. update is not available (remote: " + updateCheck.restreamer.version + ", local: " + packageJson.version + ")");
logger.debug(`Checking app for updates successful. Update is not available (remote: ${updateCheck.restreamer.version}, local: ${packageJson.version})`);
}else{
updateAvailable = updateCheck.restreamer.version;
logger.debug("checking app for updates successful. update is available (remote: " + updateCheck.restreamer.version + ", local: " + packageJson.version + ")");
logger.debug(`Checking app for updates successful. Update is available (remote: ${updateCheck.restreamer.version}, local: ${packageJson.version})`);
}
logger.info("checking app for updates successful.");
app.set("updateAvailable", updateAvailable);
logger.info(`Checking app for updates successful`);
app.set('updateAvailable', updateAvailable);
} else {
logger.info("Update check failed", false);
logger.info(`Update check failed`, false);
}
});
};

View File

@@ -6,7 +6,7 @@
*/
var LocalStrategy = require('passport-local').Strategy;
var auth = require("../../../conf/live.json").auth;
var auth = require('../../../conf/live.json').auth;
if (process.env.RESTREAMER_USERNAME) {
var username = process.env.RESTREAMER_USERNAME;
@@ -42,7 +42,7 @@ module.exports = (passport) => {
*/
done(null, auth);
}else{
done(null, false, req.flash("wrong password or wrong user"));
done(null, false, req.flash('wrong password or wrong user'));
}
}));
};

View File

@@ -5,28 +5,28 @@
* @license Apache-2.0
*/
const express = require("express");
const path = require("path");
const Restreamer = require("../../classes/Restreamer");
const express = require('express');
const path = require('path');
const Restreamer = require('../../classes/Restreamer');
module.exports = (app, passport) => {
//static paths
app.use("/css", express.static(path.join(__dirname, '../', 'public', 'css')));
app.use("/libs", express.static(path.join(__dirname, '../', 'public', 'libs')));
app.use("/dist", express.static(path.join(__dirname, '../', 'public', 'dist')));
app.use("/help", express.static( path.join(__dirname, '../', 'public', 'help')));
app.use("/images", express.static( path.join(__dirname, '../', 'public', 'images')));
app.use("/locales", express.static(path.join(__dirname, '../', 'public', 'locales')));
app.use("/crossdomain.xml",express.static( path.join(__dirname, '../', 'public', 'crossdomain.xml')));
app.use("/player.html", express.static(path.join(__dirname, '../', 'public', 'player.html')));
app.get("/main.html", (req, res)=>{
app.use('/css', express.static(path.join(__dirname, '../', 'public', 'css')));
app.use('/libs', express.static(path.join(__dirname, '../', 'public', 'libs')));
app.use('/dist', express.static(path.join(__dirname, '../', 'public', 'dist')));
app.use('/help', express.static( path.join(__dirname, '../', 'public', 'help')));
app.use('/images', express.static( path.join(__dirname, '../', 'public', 'images')));
app.use('/locales', express.static(path.join(__dirname, '../', 'public', 'locales')));
app.use('/crossdomain.xml',express.static( path.join(__dirname, '../', 'public', 'crossdomain.xml')));
app.use('/player.html', express.static(path.join(__dirname, '../', 'public', 'player.html')));
app.get('/main.html', (req, res)=>{
if (req.isAuthenticated()){
res.sendFile(path.join(__dirname, '../', 'public', 'main.html'));
} else {
res.sendFile(path.join(__dirname, '../', 'public', 'login.html'));
}
});
app.get("/", (req, res)=>{
app.get('/', (req, res)=>{
res.sendFile(path.join(__dirname, '../', 'public', 'index.html'));
});
/* Handle Login POST */
@@ -37,19 +37,19 @@ module.exports = (app, passport) => {
failureFlash : true
})
);
app.get("/logout", (req, res)=>{
app.get('/logout', (req, res)=>{
req.logout();
res.redirect("/");
res.redirect('/');
});
//small get.api
app.get("/v1/states", (req, res)=>{
app.get('/v1/states', (req, res)=>{
var states = Restreamer.data.states;
res.json({
repeat_to_local_nginx: states.repeatToLocalNginx,
repeat_to_optional_output: states.repeatToOptionalOutput
});
});
app.get("/v1/progresses", (req, res)=>{
app.get('/v1/progresses', (req, res)=>{
var progresses = Restreamer.data.progresses;
res.json({
repeat_to_local_nginx: {

View File

@@ -8,8 +8,8 @@
'use strict';
const bytes = require('bytes');
const Logger = require("../../classes/Logger");
const logger = new Logger("webserver");
const Logger = require('../../classes/Logger');
const logger = new Logger('webserver');
module.exports = (req, res, next)=>{
req._startTime = new Date();
@@ -24,9 +24,9 @@ module.exports = (req, res, next)=>{
var duration = ('new Date' - 'req._startTime');
var url = (req.originalUrl || req.url);
var method = req.method;
logger.debug(method + " \"" + url + "\" " + code + " " + duration + " " + req.ip + " " + len, "Webserver");
logger.debug(method + ' \'' + url + '\' ' + code + ' ' + duration + ' ' + req.ip + ' ' + len, 'Webserver');
};
res.on("finish", log);
res.on("close", log);
res.on('finish', log);
res.on('close', log);
next();
};

View File

@@ -17,12 +17,12 @@ datarheiApp.config(function ($translateProvider) {
});
$translateProvider.useSanitizeValueStrategy('escape');
$translateProvider.preferredLanguage("en_US");
$translateProvider.preferredLanguage('en_US');
});
datarheiApp.config(function($stateProvider, $urlRouterProvider){
// For any unmatched url, redirect to /
$urlRouterProvider.otherwise("/");
$urlRouterProvider.otherwise('/');
$stateProvider
.state('main',{
@@ -43,7 +43,7 @@ datarheiApp.config(function($stateProvider, $urlRouterProvider){
datarheiApp.filter('inArray', function($filter){
return function(list, arrayFilter, element){
if(arrayFilter){
return $filter("filter")(list, function(listItem){
return $filter('filter')(list, function(listItem){
return arrayFilter.indexOf(listItem[element]) !== -1;
});
}

View File

@@ -10,9 +10,9 @@ window.datarheiApp.controller('languageCtrl',['$scope','$translate', function($s
$scope.switchLanguage = function(locale){
$scope.currentLocale = locale;
$translate.use(locale).then(function(){
window.Logger.log("INFO", "Switched language to " + locale);
window.Logger.log('INFO', 'Switched language to ' + locale);
}, function(error){
window.Logger.error("INFO", "Switching language to " + locale + " failed: " + error);
window.Logger.error('INFO', 'Switching language to ' + locale + ' failed: ' + error);
});
};
$scope.langIs = function(locale){

View File

@@ -14,86 +14,10 @@
window.datarheiApp.controller('mainCtrl',['ws', '$scope', '$location', '$rootScope', '$translate', function(ws, $scope, $location, $rootScope, $translate) {
//binding just once
var setup = false;
$translate.use("en_US");
var player = null;
$scope.reStreamerData = {
states: {
repeatToLocalNginx: {
type: ""
},
repeatToOptionalOutput: {
type: ""
}
},
userActions: {
repeatToLocalNginx: "",
repeatToOptionalOutput: ""
},
addresses: {
optionalOutputAddress: "",
srcAddress: ""
}
};
$rootScope.windowLocationPort = $location.port();
$scope.optionalOutput = "";
$scope.showStopButton = function(streamType){
return $scope.reStreamerData.userActions[streamType] === "start";
};
$scope.showStartButton = function(streamType){
return $scope.reStreamerData.userActions[streamType] === "stop";
};
$scope.nginxRepeatStreamConnecting = function(){
return $scope.reStreamerData.states.repeatToLocalNginx.type === "connecting";
};
$scope.nginxRepeatStreamConnected = function(){
return $scope.reStreamerData.states.repeatToLocalNginx.type === "connected";
};
$scope.nginxRepeatStreamError = function(){
return $scope.reStreamerData.states.repeatToLocalNginx.type === "error";
};
$scope.optionalOutputConnecting = function(){
return $scope.reStreamerData.states.repeatToOptionalOutput.type === "connecting";
};
$scope.optionalOutputConnected = function(){
return $scope.reStreamerData.states.repeatToOptionalOutput.type === "connected";
};
$scope.optionalOutputError = function(){
return $scope.reStreamerData.states.repeatToOptionalOutput.type === "error";
};
/*
Configure Websockets
*/
//connect to namespace /
ws = ws("/");
ws.emit('testConnection');
//check states of hls and rtmp stream
ws.emit('checkStates');
ws.emit('checkForAppUpdates');
//prohibit double binding of events
if (!setup) {
/**
* test websockets connection (should print below message to browser console if it works)
*/
ws.on("connection", function (version) {
$rootScope.version = version;
window.Logger.log("INFO", "Datarhei " + version + " websockets connected");
var player = new Clappr.Player({
const initClappr = function() {
player = new Clappr.Player({
source: "http://" + window.location.hostname + ":" + window.location.port + "/hls/live.stream.m3u8",
parentId: "#player",
baseUrl: '/libs/clappr/dist/',
@@ -102,21 +26,121 @@ window.datarheiApp.controller('mainCtrl',['ws', '$scope', '$location', '$rootSco
height: "100%",
width: "100%"
});
};
$translate.use('en_US');
$scope.reStreamerData = {
states: {
repeatToLocalNginx: {
type: ''
},
repeatToOptionalOutput: {
type: ''
}
},
userActions: {
repeatToLocalNginx: '',
repeatToOptionalOutput: ''
},
addresses: {
optionalOutputAddress: '',
srcAddress: ''
}
};
$rootScope.windowLocationPort = $location.port();
$scope.optionalOutput = '';
$scope.showStopButton = function(streamType){
return $scope.reStreamerData.userActions[streamType] === 'start';
};
$scope.showStartButton = function(streamType){
return $scope.reStreamerData.userActions[streamType] === 'stop';
};
$scope.nginxRepeatStreamConnecting = function(){
return $scope.reStreamerData.states.repeatToLocalNginx.type === 'connecting';
};
$scope.nginxRepeatStreamConnected = function(){
return $scope.reStreamerData.states.repeatToLocalNginx.type === 'connected';
};
$scope.nginxRepeatStreamError = function(){
return $scope.reStreamerData.states.repeatToLocalNginx.type === 'error';
};
$scope.optionalOutputConnecting = function(){
return $scope.reStreamerData.states.repeatToOptionalOutput.type === 'connecting';
};
$scope.optionalOutputConnected = function(){
return $scope.reStreamerData.states.repeatToOptionalOutput.type === 'connected';
};
$scope.optionalOutputError = function(){
return $scope.reStreamerData.states.repeatToOptionalOutput.type === 'error';
};
$scope.openPlayer = function(){
if (player === null) {
initClappr();
}
$("#player-modal").modal("show");
$('#player-modal').on('hide.bs.modal', function (e) {
player.stop();
$('#player-modal').off('hide.bs.modal');
$("#player-modal").modal("hide");
return e.preventDefault();
})
};
/*
Configure Websockets
*/
//connect to namespace /
ws = ws('/');
ws.emit("getVersion");
//check states of hls and rtmp stream
ws.emit('checkStates');
//check for app updates
ws.emit('checkForAppUpdates');
//prohibit double binding of events
if (!setup) {
/**
* test websockets connection (should print below message to browser console if it works)
*/
ws.on('version', function (version) {
$rootScope.version = version;
window.Logger.log('INFO', 'Datarhei ' + version + ' websockets connected');
});
ws.on("updateProgress", function(progresses){
ws.on('updateProgress', function(progresses){
$scope.reStreamerData.progresses = progresses;
});
ws.on("publicIp", function(publicIp){
ws.on('publicIp', function(publicIp){
$rootScope.publicIp = publicIp;
});
ws.on("updateStreamData", function(reStreamerData) {
ws.on('updateStreamData', function(reStreamerData) {
$scope.reStreamerData = reStreamerData;
if ($scope.showStopButton('repeatToOptionalOutput')) {
$scope.activateOptionalOutput = true; //checkbox;
}
});
ws.on("checkForAppUpdatesResult", function(result){
ws.on('checkForAppUpdatesResult', function(result){
$rootScope.checkForAppUpdatesResult = result;
});
}
@@ -126,7 +150,7 @@ window.datarheiApp.controller('mainCtrl',['ws', '$scope', '$location', '$rootSco
if($scope.activateOptionalOutput === true){
optionalOutput = $scope.reStreamerData.addresses.optionalOutputAddress;
}
ws.emit("startStream", {
ws.emit('startStream', {
src: $scope.reStreamerData.addresses.srcAddress,
streamType: streamType,
optionalOutput: optionalOutput
@@ -134,6 +158,6 @@ window.datarheiApp.controller('mainCtrl',['ws', '$scope', '$location', '$rootSco
};
$scope.stopStream = function(streamType){
ws.emit("stopStream", streamType);
ws.emit('stopStream', streamType);
};
}]);

View File

@@ -13,21 +13,21 @@ window.datarheiApp.factory('ws', [ '$rootScope', 'alertService', function ($root
var ws = function(namespace){
var wsHandler = {};
window.Logger.log ("WEBSOCKETS_NAMESPACE", "connect to namespace " + namespace);
window.Logger.log ('WEBSOCKETS_NAMESPACE', `connect to namespace ${namespace}`);
var socket;
if (namespace === "/"){
if (namespace === '/'){
socket = io.connect();
}else{
socket = io.connect(namespace);
}
wsHandler.emit = function (event, data, callback) {
window.Logger.log("WEBSOCKETS_OUT", "emit event '" + event + "'");
window.Logger.log('WEBSOCKETS_OUT', `emit event "${event}"`);
socket.emit(event, data);
return wsHandler;
};
wsHandler.on = function (event, callback) {
socket.on(event, function () {
window.Logger.log("WEBSOCKETS_IN", "got event '" + event + "'");
window.Logger.log('WEBSOCKETS_IN', `got event "${event}"`);
var args = arguments;
$rootScope.$apply(function () {
callback.apply(null, args);
@@ -39,7 +39,7 @@ window.datarheiApp.factory('ws', [ '$rootScope', 'alertService', function ($root
socket.removeListener(event, callback);
};
wsHandler
.on("alert", function(alert) {
.on('alert', function(alert) {
return alertService.add(alert);
});
return wsHandler;

View File

@@ -16,6 +16,6 @@ window.Logger = {
WEBSOCKETS_NAMESPACE: 'color: #00BF00; font-weight: bold'
},
log: function(level, message){
console.log("%c " +"[" + level + "] " + message, this.level[level]);
console.log('%c ' +'[' + level + '] ' + message, this.level[level]);
}
};

View File

@@ -59,7 +59,7 @@
</div>
<!-- Large modal -->
<div class="modal fade player-modal" tabindex="-1" role="dialog">
<div id="player-modal" class="modal fade" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">

View File

@@ -1,5 +1,5 @@
<!--
Repeat to local nginxt
Repeat to local nginx
-->
<div class="form-group">
<label>{{'input_titel' | translate}} (<a href="https://datarhei.github.io/restreamer/docs/references-rtmp-rtsp-video-source.html", target="_blank">{{'help_titel' | translate}}</a>):</label>
@@ -16,7 +16,11 @@
</div>
<div class="jumbotron progress-bar-danger progress-bar-striped" ng-if="nginxRepeatStreamError()">{{'process_failed' | translate}}</div>
<div class="form group" >
<p class="player-link" ng-if="nginxRepeatStreamConnected()"><a href="#" style="text-decoration:underline" data-toggle="modal" data-target=".player-modal">{{'player_link_titel' | translate}}</a></p>
<p class="player-link" ng-if="nginxRepeatStreamConnected()">
<a href="#" style="text-decoration:underline" ng-click="openPlayer()">
{{'player_link_titel' | translate}}
</a>
</p>
<div class="text-right">
<button type="button" class="btn btn-success" ng-if="showStartButton('repeatToLocalNginx')" ng-click="startStream('repeatToLocalNginx')">{{'button_start' | translate}}</button>
<button type="button" class="btn btn-danger" ng-if="showStopButton('repeatToLocalNginx')" ng-click="stopStream('repeatToLocalNginx')">{{'button_stop' | translate}}</button>