We're now completely getting rid of vlc-api.

- Hopefully fixes audio volume not being set to 50% at startup.
- Volume now is in the range of 0 to 200 (percentage, so goes from 0% to 200% for those numbers).
- Made message displaying volume change display the actual level set.
- We're no longer providing an HTTP interface.
- Now stores metadata info in a separate variable for later retrieval by input MRL.
- Now prints when player reaches end of playlist.
- Now supports toggle-pausing using "pause" command.
- Should fix playlist not being able to kick off on "play" if stopped.
- Removed vlc-api dependency.

Relates to issues #17, #10 and #7 (partially).
develop
Icedream 2015-10-27 01:14:55 +01:00
parent ac3531a875
commit 394b16ab5e
3 changed files with 56 additions and 113 deletions

View File

@ -46,13 +46,16 @@ await services.find("pulseaudio").start defer err
if err if err
log.warn "PulseAudio could not start up, audio may not act as expected!" log.warn "PulseAudio could not start up, audio may not act as expected!"
# VLC HTTP API # VLC via WebChimera.js
await services.find("vlc").start defer err vlcService = services.find("vlc")
await vlcService.start defer err, vlc
if err if err
log.warn "VLC could not start up!" log.warn "VLC could not start up!"
await module.exports.shutdown defer() await module.exports.shutdown defer()
process.exit 1 process.exit 1
vlc = services.find("vlc").instance
# Cached information for tracks in playlist
vlcMediaInfo = {}
# TeamSpeak3 # TeamSpeak3
ts3clientService = services.find("ts3client") ts3clientService = services.find("ts3client")
@ -60,6 +63,17 @@ ts3clientService = services.find("ts3client")
ts3clientService.on "started", (ts3proc) => ts3clientService.on "started", (ts3proc) =>
ts3query = ts3clientService.query ts3query = ts3clientService.query
# VLC event handling
vlc.onPlaying () =>
info = vlcMediaInfo[vlc.playlist.items[vlc.playlist.currentItem].mrl]
ts3query.sendtextmessage 2, 0, "Now playing [URL=#{info.originalUrl}]#{info.title}[/URL]."
vlc.onPaused () => ts3query.sendtextmessage 2, 0, "Paused."
vlc.onForward () => ts3query.sendtextmessage 2, 0, "Fast-forwarding..."
vlc.onBackward () => ts3query.sendtextmessage 2, 0, "Rewinding..."
vlc.onEncounteredError () => log.error "VLC has encountered an error! You will need to restart the bot.", arguments
vlc.onEndReached () => ts3query.sendtextmessage 2, 0, "End of playlist reached."
vlc.onStopped () => ts3query.sendtextmessage 2, 0, "Stopped."
ts3query.currentScHandlerID = 1 ts3query.currentScHandlerID = 1
ts3query.mydata = {} ts3query.mydata = {}
@ -145,7 +159,8 @@ ts3clientService.on "started", (ts3proc) =>
switch name.toLowerCase() switch name.toLowerCase()
when "pause" when "pause"
vlc.status.pause() # now we can toggle-pause playback this easily! yay!
vlc.togglePause()
return return
when "play" when "play"
inputBB = paramline.trim() inputBB = paramline.trim()
@ -153,7 +168,7 @@ ts3clientService.on "started", (ts3proc) =>
# we gonna interpret play without a url as an attempt to unpause the current song # we gonna interpret play without a url as an attempt to unpause the current song
if input.length <= 0 if input.length <= 0
vlc.status.resume() vlc.play()
return return
# only allow playback from file if it's a preconfigured alias # only allow playback from file if it's a preconfigured alias
@ -168,11 +183,7 @@ ts3clientService.on "started", (ts3proc) =>
# TODO: permission system to check if uid is allowed to play this url or alias # TODO: permission system to check if uid is allowed to play this url or alias
await vlc.status.empty defer(err) vlc.playlist.clear()
if err
log.warn "Couldn't empty VLC playlist", err
ts3query.sendtextmessage args.targetmode, invoker.id, "Sorry, an error occurred. Try again later."
return
# let's give youtube-dl a shot! # let's give youtube-dl a shot!
await youtubedl.getInfo input, [ await youtubedl.getInfo input, [
@ -189,24 +200,13 @@ ts3clientService.on "started", (ts3proc) =>
if not info.url? if not info.url?
info.url = input info.url = input
info.title = input # URL as title info.title = input # URL as title
info.originalUrl = input
vlcMediaInfo[info.url] = info
await vlc.status.play info.url, defer(err) # play it in VLC
if err vlc.play info.url
vlc.status.empty()
log.warn "VLC API returned an error when trying to play", err
ts3query.sendtextmessage args.targetmode, invoker.id, "Something seems to be wrong with the specified media. Try checking the URL/path you provided?"
return
ts3query.sendtextmessage args.targetmode, invoker.id, "Now playing [URL=#{input}]#{info.title}[/URL]."
when "next" when "next"
await vlc.status.next defer(err) vlc.playlist.next()
if err
vlc.status.empty()
log.warn "VLC API returned an error when trying to skip current song", err
ts3query.sendtextmessage args.targetmode, invoker.id, "This didn't work. Does the playlist have multiple songs?"
return
ts3query.sendtextmessage args.targetmode, invoker.id, "Playing next song in the playlist."
when "enqueue", "add", "append" when "enqueue", "add", "append"
inputBB = paramline.trim() inputBB = paramline.trim()
input = (removeBB paramline).trim() input = (removeBB paramline).trim()
@ -242,36 +242,25 @@ ts3clientService.on "started", (ts3proc) =>
if not info.url? if not info.url?
info.url = input info.url = input
info.title = input # URL as title info.title = input # URL as title
info.originalUrl = input
vlcMediaInfo[info.url] = info
await vlc.status.enqueue info.url, defer(err) # add it in VLC
if err vlc.playlist.add info.url
vlc.status.empty()
log.warn "VLC API returned an error when trying to play", err
ts3query.sendtextmessage args.targetmode, invoker.id, "Something seems to be wrong with the specified media. Try checking the URL/path you provided?"
return
ts3query.sendtextmessage args.targetmode, invoker.id, "Added [URL=#{input}]#{info.title}[/URL] to the playlist." ts3query.sendtextmessage args.targetmode, invoker.id, "Added [URL=#{input}]#{info.title}[/URL] to the playlist."
# TODO: Do we need to make sure that vlc.playlist.mode is not set to "Single" here or is that handled automatically?
when "stop" when "stop"
await vlc.status.stop defer(err) vlc.stop()
vlc.status.empty()
ts3query.sendtextmessage args.targetmode, invoker.id, "Stopped playback."
when "vol" when "vol"
vol = parseInt paramline vol = parseInt paramline
if paramline.trim().length <= 0 or vol > 511 or vol < 0 if paramline.trim().length <= 0 or vol > 200 or vol < 0
ts3query.sendtextmessage args.targetmode, invoker.id, "[B]vol <number>[/B] - takes a number between 0 (0%) and 1024 (400%) to set the volume. 100% is 256. Defaults to 128 (50%) on startup." ts3query.sendtextmessage args.targetmode, invoker.id, "[B]vol <number>[/B] - takes a number between 0 (0%) and 200 (200%) to set the volume. 100% is 100. Defaults to 50 (50%) on startup."
return return
await vlc.status.volume paramline, defer(err) vlc.audio.volume = vol
ts3query.sendtextmessage args.targetmode, invoker.id, "Volume set to #{vol}%."
if err
log.warn "Failed to set volume", err
ts3query.sendtextmessage args.targetmode, invoker.id, "That unfortunately didn't work out."
return
ts3query.sendtextmessage args.targetmode, invoker.id, "Volume set."
when "changenick" when "changenick"
nick = if paramline.length > params[0].length then paramline else params[0] nick = if paramline.length > params[0].length then paramline else params[0]
if nick.length < 3 or nick.length > 30 if nick.length < 3 or nick.length > 30

View File

@ -39,7 +39,6 @@
"string.prototype.startswith": "^0.2.0", "string.prototype.startswith": "^0.2.0",
"sync": "^0.2.5", "sync": "^0.2.5",
"valid-url": "^1.0.9", "valid-url": "^1.0.9",
"vlc-api": "0.0.0",
"webchimera.js": "^0.1.38", "webchimera.js": "^0.1.38",
"which": "^1.1.2", "which": "^1.1.2",
"winston": "^1.0.1", "winston": "^1.0.1",

View File

@ -1,88 +1,43 @@
spawn = require("child_process").spawn spawn = require("child_process").spawn
services = require("../services") services = require("../services")
config = require("../config") config = require("../config")
VLCApi = require("vlc-api") wc = require("webchimera.js")
StreamSplitter = require("stream-splitter") StreamSplitter = require("stream-splitter")
require_bin = require("../require_bin")
vlcBinPath = require_bin "vlc"
module.exports = class VLCService extends services.Service module.exports = class VLCService extends services.Service
dependencies: [ dependencies: [
"pulseaudio" "pulseaudio"
] ]
constructor: -> super "VLC", constructor: -> super "VLC",
###
# Starts an instance of VLC and keeps it ready for service.
###
start: (cb) -> start: (cb) ->
if @_process if @_instance
cb? null, @_process cb? null, @_instance
return return
calledCallback = false calledCallback = false
proc = null instance = wc.createPlayer [
doStart = null
doStart = () =>
await services.find("pulseaudio").start defer(err)
if err
throw new Error "Dependency pulseaudio failed."
proc = spawn vlcBinPath, [
"-I", "http",
"--http-host", config.get("vlc-host"),
"--http-port", config.get("vlc-port"),
"--http-password", config.get("vlc-password")
"--aout", "pulse", "--aout", "pulse",
"--volume", "128", # 50% volume
"--no-video" "--no-video"
], ]
stdio: ['ignore', 'pipe', 'pipe'] instance.audio.volume = 50
detached: true
# logging @_instance = instance
stdoutTokenizer = proc.stdout.pipe StreamSplitter "\n" cb? null, @_instance
stdoutTokenizer.encoding = "utf8";
stdoutTokenizer.on "token", (token) =>
token = token.trim() # get rid of \r
@log.debug token
stderrTokenizer = proc.stderr.pipe StreamSplitter "\n"
stderrTokenizer.encoding = "utf8";
stderrTokenizer.on "token", (token) =>
token = token.trim() # get rid of \r
@log.debug token
proc.on "exit", () =>
if @state == "stopping"
return
if not calledCallback
calledCallback = true
@log.warn "VLC terminated unexpectedly during startup."
cb? new Error "VLC terminated unexpectedly."
@log.warn "VLC terminated unexpectedly, restarting."
doStart()
@_process = proc
doStart()
setTimeout (() =>
if not calledCallback
calledCallback = true
@instance = new VLCApi
host: ":#{encodeURIComponent config.get("vlc-password")}@#{config.get("vlc-host")}",
port: config.get("vlc-port")
cb? null, @instance), 1500 # TODO: Use some more stable condition
###
# Shuts down the VLC instance.
###
stop: (cb) -> stop: (cb) ->
if not @_process if not @_instance
cb?() cb?()
return return
@instance = null # TODO: Is there even a proper way to shut this down?
@_instance = null
@_process.kill()
await @_process.once "exit", defer()
cb?() cb?()