-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathvideo.js
78 lines (63 loc) · 2.76 KB
/
video.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"use strict";
const ytdl = require('ytdl-core');
const Speaker = require('speaker');
const Throttle = require('stream-throttle').Throttle;
const pcmAudio = require('youtube-terminal/lib/pcm-audio');
const ffmpeg = require('youtube-terminal/lib/ffmpeg');
const RawImageStream = require('youtube-terminal/lib/raw-image-stream');
const cursor = require('../lib/Cursor').create();
const CHARACTERS = ' .,:;i1tfLCG08@'.split('');
function imageToAscii(data, width, height) {
var contrastFactor = 2.95;
var ascii = '';
for (var y = 0; y < height; y += 2) {
for (var x = 0; x < width; x++) {
var offset = (y * width + x) * 3;
var r = Math.max(0, Math.min(contrastFactor * (data[offset] - 128) + 128, 255));
var g = Math.max(0, Math.min(contrastFactor * (data[offset + 1] - 128) + 128, 255));
var b = Math.max(0, Math.min(contrastFactor * (data[offset + 2] - 128) + 128, 255));
var brightness = 1 - (0.299 * r + 0.587 * g + 0.114 * b) / 255;
ascii += CHARACTERS[Math.round(brightness * 14)];
}
}
return ascii;
}
function playVideo(info) {
const video = info.formats.filter(format => format.resolution === '144p' && format.audioBitrate === null).sort((a, b) => a.container === 'webm' ? -1 : 1)[0];
const m = video.size.match(/^(\d+)x(\d+)$/);
const videoSize = {width: m[1], height: m[2]};
const frameHeight = Math.round(cursor._height * 2);
const frameWidth = Math.round(frameHeight * (videoSize.width / videoSize.height));
const frameSize = frameWidth * frameHeight * 3;
ffmpeg.rawImageStream(video.url, {fps: 30, width: frameWidth})
.on('start', () => cursor.saveScreen().reset())
.on('end', () => process.exit(0))
.pipe(new Throttle({rate: frameSize * 30}))
.pipe(new RawImageStream(frameSize))
.on('data', function (frameData) {
var ascii = imageToAscii(frameData, frameWidth, frameHeight);
for (var y = 0; y < frameHeight; y++) {
for (var x = 0; x < frameWidth; x++) {
cursor
.moveTo(x + (cursor._width / 2 - frameWidth / 2), y)
.write(ascii[y * frameWidth + x] || '');
}
}
cursor.flush();
});
}
function playAudio(info) {
const audio = info.formats.filter(format => format.resolution === null).sort((a, b) => b.audioBitrate - a.audioBitrate)[0];
const speaker = new Speaker();
const updateSpeaker = codec => {
speaker.channels = codec.audio_details[2] === 'mono' ? 1 : 2;
speaker.sampleRate = parseInt(codec.audio_details[1].match(/\d+/)[0], 10)
};
pcmAudio(audio.url).on('codecData', updateSpeaker).pipe(speaker);
}
ytdl.getInfo('https://www.youtube.com/watch?v=Hiqn1Ur32AE', (error, info) => {
if (error) return console.error(error);
playVideo(info);
playAudio(info);
});
process.on('SIGTERM', () => cursor.restoreScreen());