Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 44 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![ESLint Status](https://github.com/gkozlenko/node-media-server/actions/workflows/eslint.yml/badge.svg)](https://github.com/gkozlenko/node-media-server/actions/workflows/eslint.yml)
[![GitHub License](https://img.shields.io/github/license/gkozlenko/node-video-lib.svg)](https://github.com/gkozlenko/node-video-lib/blob/master/LICENSE)

Node.js Media Server / VOD / HLS / DRM
Node.js Media Server / VOD / HLS / Encryption

## Installation

Expand All @@ -19,46 +19,55 @@ Config is located in the `config/index.js` file.

```javascript
const config = {
// Host and port for bidding
host: '0.0.0.0',
port: 3000,
server: {
// Timeout for graceful shutdown (in milliseconds)
shutdownTimeout: 10000,
},

// Path to static files
publicPath: path.resolve('./public'),
// Path to video files
mediaPath: path.resolve('./media'),
// Path to index files
indexPath: path.resolve('./index'),
// Path to log files
logsPath: path.resolve('./logs'),
workers: {
// Number of media worker threads
count: require('os').cpus().length,
// Timeout for worker graceful shutdown
shutdownTimeout: 5000,
},

// Video chunk duration (in seconds)
fragmentDuration: 10,
web: {
// Host and port for the web server
host: '0.0.0.0',
port: 3000,
// Path to static files
staticPath: path.resolve('./public'),
// Timeout for worker requests (in milliseconds)
workerTimeout: 1000,
// Timeout for web server graceful shutdown
shutdownTimeout: 5000,
},

// DRM configuration
drmEnabled: false,
drmSeed: 'DRM SEED',
logger: {
// Path to log files
logPath: path.resolve('./logs'),
// Log level (available levels: 'debug', 'info', 'warn' and 'error')
level: 'debug',
// Maximum log size in bytes
maxSize: 50 * 1024 * 1024,
// How many rotated log files to keep
maxFiles: 10,
},

// Logger configuration
logLevel: 'debug', // 'info', 'warn' and 'error' also available
logSize: 50 * 1024 * 1024, // maximum log size in bytes
logKeep: 10, // how many rotated log files to keep
media: {
// Video chunk duration (in seconds)
fragmentDuration: 5,
// Path to video files
mediaPath: path.resolve('./media'),

shutdownInterval: 1000,
// Indexing configuration
indexEnabled: true,
// Path to index files
indexPath: path.resolve('./index'),

workers: {
// Server Worker
web: {
enabled: true,
count: require('os').cpus().length,
shutdownTimeout: 5000,
},
// Movie indexer Worker
indexer: {
enabled: true,
count: 1,
timeout: 5000,
},
// Encryption configuration
encryptionEnabled: false,
encryptionSeed: 'CIPHER SEED',
},
};
```
Expand Down
29 changes: 29 additions & 0 deletions components/cipher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
'use strict';

const config = require('../config');
const crypto = require('crypto');

function cipherKey(name) {
return crypto.createHash('sha256')
.update(`${name}.${config.media.encryptionSeed}.key`)
.digest()
.subarray(0, 16);
}

function cipherIv(name) {
Comment thread
gkozlenko marked this conversation as resolved.
return crypto.createHash('sha256')
.update(`${name}.${config.media.encryptionSeed}.iv`)
.digest()
.subarray(0, 16);
}

function encryptChunk(name, buffer) {
let cipher = crypto.createCipheriv('aes-128-cbc', cipherKey(name), cipherIv(name));
Comment thread
gkozlenko marked this conversation as resolved.
return Buffer.concat([cipher.update(buffer), cipher.final()]);
}

module.exports = {
cipherKey,
cipherIv,
encryptChunk,
};
26 changes: 18 additions & 8 deletions components/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,37 @@ class NotFoundError extends HttpError {

}

class ForbiddenError extends HttpError {
class RequestTimeoutError extends HttpError {

constructor(message) {
super(403, message);
this.name = 'ForbiddenError';
super(408, message);
this.name = 'RequestTimeoutError';
}

}
Comment on lines +22 to 29
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The ForbiddenError and UnauthorizedError classes were removed. It's important to ensure that any code that previously relied on these specific error types has been updated to handle the new error structure or that their functionality is no longer needed. If these HTTP status codes (401 Unauthorized, 403 Forbidden) are still relevant for the application's logic, new custom errors or a more generic HttpError should be used with appropriate status codes.


class UnauthorizedError extends HttpError {
class InternalServerError extends HttpError {

constructor(message) {
super(401, message);
this.name = 'UnauthorizedError';
super(500, message);
this.name = 'InternalServerError';
}

}

class ServiceUnavailableError extends HttpError {

constructor(message) {
super(503, message);
this.name = 'ServiceUnavailableError';
}

}

module.exports = {
HttpError,
NotFoundError,
ForbiddenError,
UnauthorizedError,
RequestTimeoutError,
InternalServerError,
ServiceUnavailableError,
};
65 changes: 0 additions & 65 deletions components/indexer.js

This file was deleted.

30 changes: 15 additions & 15 deletions components/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,33 @@ const winston = require('winston');
const path = require('path');

const logFormat = winston.format.printf(({ level, message, timestamp, label, stack }) => {
return `[${timestamp}] [${level.toUpperCase()}] ${label || 'app'} - ${stack || message}`;
const logMessage = stack ? `${message}\n${stack}` : message;
return `[${timestamp}] [${level.toUpperCase()}] ${label || 'app'} - ${logMessage}`;
});

const baseFormat = winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.splat(),
logFormat,
);

function createFileTransport(filename, level) {
return new winston.transports.File({
filename: path.join(config.logsPath, filename),
filename: path.join(config.logger.logPath, filename),
level: level,
maxsize: config.logSize,
maxFiles: config.logKeep,
maxsize: config.logger.maxSize,
maxFiles: config.logger.maxFiles,
tailable: true,
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.splat(),
logFormat,
),
format: baseFormat,
});
}

const rootLogger = winston.createLogger({
level: config.logLevel,
level: config.logger.level,
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.splat(),
logFormat,
),
format: baseFormat,
}),
createFileTransport('debug.log', 'debug'),
createFileTransport('info.log', 'info'),
Expand Down
Loading