Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rtmpdump.exe outdated #1

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
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
10 changes: 10 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# editorconfig.org
root = true

[*.js]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
10 changes: 10 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"env": {
"browser": true
},
"globals": {
},
"rules": {
"quotes": [2, "single"]
}
}
4 changes: 4 additions & 0 deletions .jscsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"preset": "google",
"maximumLineLength": null
}
23 changes: 8 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@ capturebate-node

capturebate-node lets you follow and archive your favorite models' shows on chaturbate.com

Requirements
==========
(Debian 7, minimum)
### Requirements

[RTMPDump(ksv)](https://github.com/BurntSushi/rtmpdump-ksv) used to capture the streams.
[RTMPDump(ksv)](https://github.com/sstativa/rtmpdump-ksv) used to capture the streams.

[Node.js](https://nodejs.org/download/) used to run capturebate-node, hence the name.

[ffmpeg](https://www.ffmpeg.org/download.html) compiled with support for `libmp3lame` & `libspeex` audio for converting the output files.

Setup
===========
### Setup

Install requirements, run `npm install` in the same folder as main.js is.

Expand All @@ -24,21 +21,17 @@ Be mindful when capturing many streams at once to have plenty of space on disk a

Before you can start capturing streams you first need to [follow](https://i.imgur.com/o9QyAVC.png) the models you want on the site, once you've done this you're ready to start capturebate-node by running `node main.js`

Running & Output
===========
### Running & Output

To start capturing streams you need to run `node main.js` I reccomend you do this in [screen](https://www.gnu.org/software/screen/) as that'll keep running if you lose connection to the machine or otherwise close your shell.
To start capturing streams you need to run `node main.js` I recommend you do this in [screen](https://www.gnu.org/software/screen/) as that'll keep running if you lose connection to the machine or otherwise close your shell.

Standard output should look something this when recording streams:

[2015-05-16T00:19:02] capturebate-node started
[2015-05-16T00:19:08] eeeveee is now online, starting rtmpdump process

Encoding
===========

Once you've captured some streams you're going to need to convert the audio to have them play nice in most media players. This is where ffmpeg comes in, there is no need to convert the video so this doesn't take too long. To convert individual files do `ffmpeg -i input.flv -vcodec copy -acodec libmp3lame output.mp4` this will convert the speex audio to mp3 and change the container to mp4 (stream is h264)
### Converting video files

If you want to batch convert your captured streams run `find ./ -name '*.flv' -execdir mkdir converted_bates \;; for file in *.flv; do ffmpeg -i "$file" -vcodec copy -acodec libmp3lame "converted_bates/${file%.flv}.mp4"; done` from the directory you capture to.
There is a simple script to convert `.flv` files. Just edit `convert.yml` file and set proper values for `srcDirectory` (should be the same with `completeDirectory` of `config.yml`) and `dstDirectory`, and run `node convert.js` in separate console window.

If you don't want to do any conversion you can install the [speex audio codec](http://speex.org/downloads/) which is a huge pain in the ass to get working correctly under linux/VLC.
> Note for Windows users: You should copy `ffmpeg.exe` file into the same directory as `main.js` is.
9 changes: 7 additions & 2 deletions config.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
username: testingallthethings # Your Chaturbate username
password: 2YY84bKlOC79KNQ # Your Chaturbate password, I promise not to do anything bad with it
captureDirectory: captures/ # Where you want the captures to be saved to
modelScanInterval: 10 # In seconds, how often capturebate-node checks for newly-online models
captureDirectory: captures # Where you want the captures to be saved to
completeDirectory: complete # Where you want the "finalized" captures to be moved to as soon as model stops streaming
modelScanInterval: 30 # In seconds, how often capturebate-node checks for newly-online models
debug: false
rtmpDebug: false
minFileSizeMb: 10
dateFormat: YYYYMMDD-HHmmss
createModelDirectory: false
208 changes: 208 additions & 0 deletions convert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
'use strict';

var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
var yaml = require('js-yaml');
var colors = require('colors');
var childProcess = require('child_process');
var mkdirp = require('mkdirp');
var mkdirpAsync = Promise.promisify(mkdirp);
var path = require('path');
var moment = require('moment');
var Queue = require('promise-queue');
var filewalker = require('filewalker');
var JSONStream = require('JSONStream');

var config = yaml.safeLoad(fs.readFileSync('convert.yml', 'utf8'));

var srcDirectory = path.resolve(config.srcDirectory || './complete');
var dstDirectory = path.resolve(config.dstDirectory || './converted');
var dirScanInterval = config.dirScanInterval || 300;
var maxConcur = config.maxConcur || 1;

Queue.configure(Promise.Promise);

var queue = new Queue(maxConcur, Infinity);

function getCurrentDateTime() {
return moment().format('MM/DD/YYYY - HH:mm:ss');
}

function printMsg(msg) {
console.log(colors.gray(`[${getCurrentDateTime()}]`), msg);
}

function printErrorMsg(msg) {
console.log(colors.gray(`[${getCurrentDateTime()}]`), colors.red('[ERROR]'), msg);
}

function getFiles() {
let files = [];

return new Promise((resolve, reject) => {
filewalker(srcDirectory, { maxPending: 1, matchRegExp: /(\.ts|\.flv)$/ })
.on('file', (p, stats) => {
// select only "not hidden" files and not empty files (>10KBytes)
if (!p.match(/(^\.|\/\.)/) && stats.size > 10240) {
// push path relative to srcDirectory
files.push(p);
}
})
.on('done', () => {
resolve(files);
})
.walk();
});
}

function getAudioCodec(srcFile) {
return new Promise((resolve, reject) => {
let audioCodec = '';
let spawnArguments = [
'-v', 'error',
'-select_streams', 'a:0',
'-show_streams',
'-print_format', 'json',
srcFile
];

let ffprobeProcess = childProcess.spawn('ffprobe', spawnArguments);

ffprobeProcess.stdout.pipe(JSONStream.parse('streams.0')).on('data', data => {
audioCodec = data.codec_name;
});

ffprobeProcess.on('close', code => {
if (code !== 0) {
reject(`Failed to get audio codec from ${srcFile}`);
} else {
resolve(audioCodec);
}
});
}).timeout(5000); // 5 seconds
}

function getSpawnArguments(srcFile, dstFile) {
return getAudioCodec(srcFile)
.then(audioCodec => (audioCodec === 'aac')
? [ // aac
'-i', srcFile,
'-y',
'-hide_banner',
'-loglevel', 'panic',
'-movflags', '+faststart',
'-c:v', 'copy',
'-c:a', 'copy',
'-bsf:a', 'aac_adtstoasc',
'-copyts',
'-start_at_zero',
dstFile
]
: [ // speex or something else
'-i', srcFile,
'-y',
'-hide_banner',
'-loglevel', 'panic',
'-movflags', '+faststart',
'-c:v', 'copy',
'-c:a', 'aac',
'-b:a', '64k',
dstFile
]
);
}

function convertFile(srcFile) {
let startTs = moment();
let src = path.join(srcDirectory, srcFile);

let dstPath = path.resolve(path.dirname(path.join(dstDirectory, srcFile)));
let dstFile = path.basename(srcFile, path.extname(srcFile)) + '.mp4';

let tempDst = path.join(dstPath, `~${dstFile}`);
let dst = path.join(dstPath, dstFile);

return mkdirpAsync(dstPath)
.then(() => getSpawnArguments(src, tempDst))
.then(spawnArguments => new Promise((resolve, reject) => {
printMsg(`Starting ${colors.green(srcFile)}...`);
// printMsg('ffmpeg ' + spawnArguments.join(' '));

let ffmpegProcess = childProcess.spawn('ffmpeg', spawnArguments);

ffmpegProcess.on('close', code => {
if (code !== 0) {
reject(`Failed to convert ${srcFile}`);
} else {
let mtime;

fs.statAsync(src)
.then(stats => {
// remember "modification time" of original file
mtime = Math.ceil(stats.mtime.getTime() / 1000);
})
.then(() => config.deleteAfter ? fs.unlinkAsync(src) : fs.renameAsync(src, `${src}.bak`))
.then(() => fs.renameAsync(tempDst, dst))
.then(() => fs.utimesAsync(dst, mtime, mtime))
.then(() => {
let duration = moment.duration(moment().diff(startTs)).asSeconds().toString();

printMsg(`Finished ${colors.green(srcFile)} after ${colors.magenta(duration)} s`);

resolve();
})
.catch(err => {
reject(err.toString());
});
}
});
}));
}

function mainLoop() {
let startTs = moment().unix();

Promise
.try(() => getFiles())
.then(files => new Promise((resolve, reject) => {
printMsg(files.length + ' file(s) to convert');

if (files.length === 0) {
resolve();
} else {
files.forEach(file => {
queue
.add(() => convertFile(file))
.then(() => {
if ((queue.getPendingLength() + queue.getQueueLength()) === 0) {
resolve();
}
})
.catch(err => {
printErrorMsg(err);
});
});
}
}))
.catch(err => {
if (err) {
printErrorMsg(err);
}
})
.finally(() => {
let seconds = startTs - moment().unix() + dirScanInterval;

if (seconds < 5) {
seconds = 5;
}

printMsg(`Done, will scan the folder in ${seconds} seconds`);

setTimeout(mainLoop, seconds * 1000);
});
}

mkdirp.sync(srcDirectory);
mkdirp.sync(dstDirectory);

mainLoop();
5 changes: 5 additions & 0 deletions convert.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
srcDirectory: complete # directory where you store your .ts or .flv files
dstDirectory: converted # directory where do you want to store your .mp4 files
dirScanInterval: 300 # in seconds, min: 5 seconds
deleteAfter: true # if you want to keep the original files then set to false
maxConcur: 2 # how many files to convert simultaneously
Binary file added librtmp.dll
Binary file not shown.
Loading