-
Notifications
You must be signed in to change notification settings - Fork 9
/
index.js
executable file
·118 lines (97 loc) · 3.83 KB
/
index.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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/usr/bin/env node
var ytdl = require('ytdl-core')
var Speaker = require('speaker')
var youtubeSearch = require('youtube-crawler')
var debug = require('debug')('yt-term')
var pcmAudio = require('./lib/pcm-audio')
var asciiVideo = require('./lib/ascii-video')
// command line options
var argv = require('minimist')(process.argv.slice(2), {
// TODO: maybe use c to alias color instead of contrast
alias: { l: 'link', i: 'invert', c: 'contrast', w: 'width', m: 'mute', h: 'help' },
boolean: ['invert', 'mute', 'color', 'help']
})
var wrongType = typeof argv.c === 'string' || typeof argv.w === 'string'
if (argv.help || (argv._.length <= 0 && !argv.link) || wrongType) {
printUsage()
} else if (argv.link) {
// play from youtube link
console.log('Playing:', argv.link)
play(argv.link)
} else {
// search youtube and play the first result
var query = argv._.join(' ')
youtubeSearch(query, function (err, results) {
if (err) return console.error(err)
console.log('Playing:', results[0].title)
play(results[0].link)
})
}
function play (url) {
ytdl.getInfo(url, function (err, info) {
if (err) return console.error(err)
if (!argv.mute) playAudio(info)
playVideo(info)
})
}
function playVideo (info) {
// low resolution video only, webm prefered
var videoItems = info.formats
.filter(function (format) { return format.resolution === '144p' && format.audioBitrate === null })
.sort(function (a, b) { return a.container === 'webm' ? -1 : 1 })
// lowest resolution
var video = videoItems[0]
debug('Video format: %s [%s] (%s) %sfps', video.resolution, video.size, video.encoding, video.fps)
// TODO: maybe it is needed to null-check video.size and m, and default to '256x144'
var size = video.size.match(/^(\d+)x(\d+)$/)
var videoInfo = {
url: video.url,
width: size[1],
height: size[2]
}
var videoOptions = {
// TODO: some (old?) videos have fps incorrectly set to 1.
fps: argv.fps /* || video.fps */ || 12,
// TODO: width does not work well if video height is larger than terminal window
width: argv.width || process.stdout.columns || 80,
contrast: (argv.contrast || 35) * 2.55, // percent to byte
invert: !argv.invert,
color: argv.color
}
// play video as ascii
asciiVideo(videoInfo, videoOptions)
}
function playAudio (info) {
// audio only, sorted by quality
var audioItems = info.formats
.filter(function (format) { return format.resolution === null })
.sort(function (a, b) { return b.audioBitrate - a.audioBitrate })
// highest bitrate
var audio = audioItems[0]
debug('Audio quality: %s (%s)', audio.audioBitrate + 'kbps', audio.audioEncoding)
var speaker = new Speaker()
var updateSpeaker = function (codec) {
speaker.channels = codec.audio_details[2] === 'mono' ? 1 : 2
speaker.sampleRate = parseInt(codec.audio_details[1].match(/\d+/)[0], 10)
}
// play audio
pcmAudio(audio.url).on('codecData', updateSpeaker).pipe(speaker)
}
function printUsage () {
console.log('Youtube Terminal v' + require('./package.json').version)
console.log()
console.log('Usage: youtube-terminal [options] "search query"')
console.log()
console.log('Options:')
console.log()
console.log(' -l, --link [url] Use YouTube link instead of searching')
console.log(' -i, --invert Invert brightness, recommended on white background')
console.log(' --color Use 16 terminal colors [experimental]')
console.log(' -c, --contrast [percent] Adjust video contrast [default: 35]')
console.log(' -w, --width [number] ASCII video character width')
console.log(' -m, --mute Disable audio playback')
console.log(' --fps [number] Adjust playback frame rate')
console.log(' -h, --help Display this usage information')
console.log()
process.exit(0)
}