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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
#!/usr/bin/env node
// omg I am tired of code
const { AppElement } = require('./ui')
const { updatePlaylistFormat } = require('./playlist-utils')
const { getAllCrawlersForArg } = require('./crawlers')
const fs = require('fs')
const util = require('util')
const processSmartPlaylist = require('./smart-playlist')
const ansi = require('./tui-lib/util/ansi')
const CommandLineInterfacer = require('./tui-lib/util/CommandLineInterfacer')
const EventEmitter = require('events')
const Flushable = require('./tui-lib/util/Flushable')
const Root = require('./tui-lib/ui/Root')
const readFile = util.promisify(fs.readFile)
// Hack to get around errors when piping many things to stdout/err
// (from general-util promisifyProcess)
process.stdout.setMaxListeners(Infinity)
process.stderr.setMaxListeners(Infinity)
process.on('unhandledRejection', error => {
console.error(ansi.setForeground(ansi.C_RED) + "** There was an uncatched error! **" + ansi.resetAttributes())
console.error("Don't worry, your music files are all okay.")
console.error("This just means there was a bug in mtui.")
console.error("In order to verify that the program won't run weirdly, it has stopped.")
console.error(ansi.setForeground(ansi.C_RED) + "Error stack:" + ansi.resetAttributes())
console.error(error.stack)
console.error(ansi.setForeground(ansi.C_RED) + "Error object:" + ansi.resetAttributes())
console.error(error)
console.error("(End of error log.)")
process.stdout.write(ansi.cleanCursor())
process.exit(1)
})
async function main() {
const interfacer = new CommandLineInterfacer()
const root = new Root(interfacer)
const appElement = new AppElement()
root.addChild(appElement)
root.select(appElement)
const result = await appElement.setup()
if (result.error) {
console.error(result.error)
process.exit(1)
}
const cleanTerminal = () => {
process.stdout.write(ansi.cleanCursor())
process.stdout.write(ansi.disableAlternateScreen())
}
const dirtyTerminal = () => {
process.stdout.write(ansi.enableAlternateScreen())
process.stdout.write(ansi.startTrackingMouse())
}
appElement.on('quitRequested', () => {
cleanTerminal()
process.exit(0)
})
appElement.on('suspendRequested', () => {
cleanTerminal()
process.kill(process.pid, 'SIGTSTP')
})
let grouplike = {
name: 'My ~/Music Library',
comment: (
'(Add songs and folders to ~/Music to make them show up here,' +
' or pass mtui your own playlist.json file!)'),
source: ['crawl-local', process.env.HOME + '/Music']
}
grouplike = await processSmartPlaylist(grouplike)
appElement.tabber.currentElement.loadGrouplike(grouplike)
root.select(appElement)
// Check size, now that we're about to display.
const size = await interfacer.getScreenSize()
root.w = size.width
root.h = size.height
root.fixAllLayout()
dirtyTerminal()
process.on('SIGCONT', () => {
flushable.resizeScreen({lines: flushable.screenLines, cols: flushable.screenCols})
process.stdin.setRawMode(false)
process.stdin.setRawMode(true)
dirtyTerminal()
})
const flushable = new Flushable(process.stdout, true)
flushable.resizeScreen(size)
flushable.shouldShowCompressionStatistics = process.argv.includes('--show-ansi-stats')
flushable.write(ansi.clearScreen())
flushable.flush()
interfacer.on('resize', newSize => {
root.w = newSize.width
root.h = newSize.height
flushable.resizeScreen(newSize)
root.fixAllLayout()
})
const loadPlaylists = async () => {
for (let i = 2; i < process.argv.length; i++) {
await appElement.handlePlaylistSource(process.argv[i], true)
}
}
const loadPlaylistPromise = loadPlaylists()
if (process.argv.includes('--stress-test')) {
await loadPlaylistPromise
const w = 80
const h = 40
flushable.resizeScreen({lines: w, cols: h})
root.w = w
root.h = h
root.fixAllLayout()
const XXstress = func => '[disabled]'
const stress = func => {
const start = Date.now()
let n = 0
while (Date.now() < start + 1000) {
func()
n++
}
return n
}
const nRenderAndFlush = stress(() => {
root.renderTo(flushable)
flushable.flush()
})
const nFixAllLayout = stress(() => {
root.fixAllLayout()
})
const listings = appElement.tabber.tabberElements
const lastListing = listings[listings.length - 1]
const nBuildItems = stress(() => {
lastListing.buildItems()
})
process.stdout.write(ansi.cleanCursor() + ansi.clearScreen() + '\n')
console.log('# of times we can render & flush:', nRenderAndFlush)
console.log('# of times we can fix all layout:', nFixAllLayout)
console.log('# of times we can build items:', nBuildItems)
process.exit(0)
return
}
setInterval(() => {
root.renderTo(flushable)
flushable.flush()
}, 50)
}
main().catch(err => {
console.error(err)
process.exit(1)
})
|