« get me outta code hell

index.js - mtui - Music Text User Interface - user-friendly command line music player
about summary refs log tree commit diff
path: root/index.js
blob: c366ed72eb976e282d5c93ab85ec260951218392 (plain)
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
#!/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(error.stack)
  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)
  }

  appElement.on('quitRequested', () => {
    process.stdout.write(ansi.cleanCursor())
    process.exit(0)
  })

  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()

  process.stdout.write(ansi.startTrackingMouse())

  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)
})