Experimental VLC support
VLC now works, but it ain't perfect.
This commit is contained in:
parent
a778cfd60a
commit
e03cfe12c3
@ -76,8 +76,7 @@ function detectchanges()
|
|||||||
|
|
||||||
if newfilepath ~= oldfilepath then
|
if newfilepath ~= oldfilepath then
|
||||||
oldfilepath = newfilepath
|
oldfilepath = newfilepath
|
||||||
notificationbuffer = notificationbuffer .. "filepath-change"..msgseperator..tostring(newfilepath)..msgterminator
|
notificationbuffer = notificationbuffer .. "filepath-change"..notificationmarker..msgterminator
|
||||||
notificationbuffer = notificationbuffer .. "file-length-change"..msgseperator..get_var("length")..msgterminator
|
|
||||||
end
|
end
|
||||||
|
|
||||||
notificationbuffer = notificationbuffer .. "playstate"..msgseperator..tostring(get_play_state())..msgterminator
|
notificationbuffer = notificationbuffer .. "playstate"..msgseperator..tostring(get_play_state())..msgterminator
|
||||||
@ -90,7 +89,7 @@ function detectchanges()
|
|||||||
|
|
||||||
if newinputstate ~= oldinputstate then
|
if newinputstate ~= oldinputstate then
|
||||||
oldinputstate = newinputstate
|
oldinputstate = newinputstate
|
||||||
notificationbuffer = "inputstate-change"..msgseperator..tostring(newinputstate)..msgterminator..notificationbuffer
|
notificationbuffer = notificationbuffer.."inputstate-change"..msgseperator..tostring(newinputstate)..msgterminator
|
||||||
end
|
end
|
||||||
|
|
||||||
return notificationbuffer
|
return notificationbuffer
|
||||||
@ -166,7 +165,7 @@ function set_var(vartoset, varvalue)
|
|||||||
return errormsg
|
return errormsg
|
||||||
end
|
end
|
||||||
|
|
||||||
h:listen( "localhost:"..port)
|
h:listen( "localhost:"..port)
|
||||||
-- h:listen( "*console" )
|
-- h:listen( "*console" )
|
||||||
|
|
||||||
function get_play_state()
|
function get_play_state()
|
||||||
@ -194,6 +193,14 @@ function get_filepath ()
|
|||||||
local item = vlc.input.item()
|
local item = vlc.input.item()
|
||||||
if item then
|
if item then
|
||||||
response = vlc.strings.decode_uri(item:uri())
|
response = vlc.strings.decode_uri(item:uri())
|
||||||
|
|
||||||
|
if (string.sub(response, 1, 8) == "file:///") then
|
||||||
|
response = string.gsub(response, "file:///","")
|
||||||
|
response = string.gsub(response, "/","\\")
|
||||||
|
else
|
||||||
|
response = ""
|
||||||
|
errormsg = noinput
|
||||||
|
end
|
||||||
else
|
else
|
||||||
errormsg = noinput
|
errormsg = noinput
|
||||||
end
|
end
|
||||||
@ -205,14 +212,34 @@ function get_filepath ()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function get_filename ()
|
function get_filename ()
|
||||||
|
|
||||||
|
local response
|
||||||
|
local index
|
||||||
|
local filename
|
||||||
|
filename = errormerge(get_filepath())
|
||||||
|
|
||||||
|
if(filename ~= nil) and (filename ~= "") and (filename ~= noinput) then
|
||||||
|
index = string.len(tostring(string.match(filename, ".*\\")))
|
||||||
|
if index then
|
||||||
|
response = string.sub(tostring(filename), index+1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
response = noinput
|
||||||
|
end
|
||||||
|
|
||||||
|
return response
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_duration ()
|
||||||
local response
|
local response
|
||||||
local errormsg
|
local errormsg
|
||||||
|
local item
|
||||||
local input = vlc.object.input()
|
local input = vlc.object.input()
|
||||||
|
|
||||||
if input then
|
if input then
|
||||||
local item = vlc.input.item()
|
local item = vlc.input.item()
|
||||||
if item then
|
if item then
|
||||||
response = item:name()
|
response = vlc.input.item():duration()
|
||||||
else
|
else
|
||||||
errormsg = noinput
|
errormsg = noinput
|
||||||
end
|
end
|
||||||
@ -223,6 +250,7 @@ function get_filename ()
|
|||||||
return response, errormsg
|
return response, errormsg
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function display_osd ( argument )
|
function display_osd ( argument )
|
||||||
local errormsg
|
local errormsg
|
||||||
local osdarray
|
local osdarray
|
||||||
@ -248,20 +276,19 @@ function do_command ( command, argument)
|
|||||||
local errormsg = ""
|
local errormsg = ""
|
||||||
local response = ""
|
local response = ""
|
||||||
|
|
||||||
local input = vlc.object.input()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if command == "get-interface-version" then response = "interface-version"..msgseperator..connectorversion..msgterminator
|
if command == "get-interface-version" then response = "interface-version"..msgseperator..connectorversion..msgterminator
|
||||||
|
elseif command == "get-duration" then response = "duration"..msgseperator..errormerge(get_duration())..msgterminator
|
||||||
|
elseif command == "get-filepath" then response = "filepath"..msgseperator..errormerge(get_filepath())..msgterminator
|
||||||
|
elseif command == "get-filename" then response = "filename"..msgseperator..errormerge(get_filename())..msgterminator
|
||||||
elseif command == "set-position" then errormsg = set_var("time", tonumber(argument))
|
elseif command == "set-position" then errormsg = set_var("time", tonumber(argument))
|
||||||
elseif command == "set-playstate" then errormsg = set_playstate(argument)
|
elseif command == "set-playstate" then errormsg = set_playstate(argument)
|
||||||
elseif command == "set-rate" then errormsg = set_var("rate", tonumber(argument))
|
elseif command == "set-rate" then errormsg = set_var("rate", tonumber(argument))
|
||||||
elseif command == "display-osd" then errormsg = display_osd(argument)
|
elseif command == "display-osd" then errormsg = display_osd(argument)
|
||||||
elseif command == "close-vlc" then misc.quit()
|
elseif command == "close-vlc" then vlc.misc.quit()
|
||||||
else errormsg = unknowncommand
|
else errormsg = unknowncommand
|
||||||
end
|
end
|
||||||
|
|
||||||
if (tostring(errormsg) ~= nil) and (errormsg ~= "") then
|
if (errormsg ~= nil) and (errormsg ~= "") then
|
||||||
response = command..errormarker..msgseperator..tostring(errormsg)..msgterminator
|
response = command..errormarker..msgseperator..tostring(errormsg)..msgterminator
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -269,6 +296,14 @@ function do_command ( command, argument)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function errormerge(argument, errormsg)
|
||||||
|
if (errormsg ~= nil) and (errormsg ~= "") then
|
||||||
|
do return errormsg end
|
||||||
|
end
|
||||||
|
|
||||||
|
return argument
|
||||||
|
end
|
||||||
|
|
||||||
function set_playstate(argument)
|
function set_playstate(argument)
|
||||||
local errormsg
|
local errormsg
|
||||||
local input = vlc.object.input()
|
local input = vlc.object.input()
|
||||||
@ -302,9 +337,7 @@ while not vlc.misc.should_die() do
|
|||||||
local responsebuffer
|
local responsebuffer
|
||||||
if not str then break end
|
if not str then break end
|
||||||
|
|
||||||
|
|
||||||
local safestr = string.gsub(tostring(str), "\r", "")
|
local safestr = string.gsub(tostring(str), "\r", "")
|
||||||
|
|
||||||
if client.inputbuffer == nil then client.inputbuffer = "" end
|
if client.inputbuffer == nil then client.inputbuffer = "" end
|
||||||
|
|
||||||
client.inputbuffer = client.inputbuffer .. safestr
|
client.inputbuffer = client.inputbuffer .. safestr
|
||||||
|
|||||||
@ -40,6 +40,7 @@ MPC_PATHS = [
|
|||||||
]
|
]
|
||||||
MPLAYER_PATHS = ["mplayer2"]
|
MPLAYER_PATHS = ["mplayer2"]
|
||||||
MPV_PATHS = ["mpv", "/opt/mpv/mpv"]
|
MPV_PATHS = ["mpv", "/opt/mpv/mpv"]
|
||||||
|
VLC_PATHS = ["C:\Program Files (x86)\VideoLAN\VLC\vlc.exe", "C:\Program Files\VideoLAN\VLC\vlc.exe"]
|
||||||
#Changing these is usually not something you're looking for
|
#Changing these is usually not something you're looking for
|
||||||
PLAYER_ASK_DELAY = 0.1
|
PLAYER_ASK_DELAY = 0.1
|
||||||
PING_MOVING_AVERAGE_WEIGHT = 0.85
|
PING_MOVING_AVERAGE_WEIGHT = 0.85
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
from syncplay.players.mplayer import MplayerPlayer
|
from syncplay.players.mplayer import MplayerPlayer
|
||||||
from syncplay.players.mpv import MpvPlayer
|
from syncplay.players.mpv import MpvPlayer
|
||||||
|
from syncplay.players.vlc import VlcPlayer
|
||||||
try:
|
try:
|
||||||
from syncplay.players.mpc import MPCHCAPIPlayer
|
from syncplay.players.mpc import MPCHCAPIPlayer
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@ -7,4 +8,4 @@ except ImportError:
|
|||||||
MPCHCAPIPlayer = DummyPlayer
|
MPCHCAPIPlayer = DummyPlayer
|
||||||
|
|
||||||
def getAvailablePlayers():
|
def getAvailablePlayers():
|
||||||
return [MPCHCAPIPlayer, MplayerPlayer, MpvPlayer]
|
return [MPCHCAPIPlayer, MplayerPlayer, MpvPlayer, VlcPlayer]
|
||||||
|
|||||||
254
syncplay/players/vlc.py
Normal file
254
syncplay/players/vlc.py
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
import subprocess
|
||||||
|
import re
|
||||||
|
import threading
|
||||||
|
from syncplay.players.basePlayer import BasePlayer
|
||||||
|
from syncplay import constants
|
||||||
|
from syncplay.messages import getMessage
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import socket
|
||||||
|
import asynchat, asyncore
|
||||||
|
|
||||||
|
class VlcPlayer(BasePlayer):
|
||||||
|
speedSupported = True
|
||||||
|
RE_ANSWER = re.compile(r"(?:^(?P<command>[a-zA-Z_]+)(?:\: )?(?P<argument>.*))")
|
||||||
|
VLC_MIN_PORT = 10000
|
||||||
|
VLC_MAX_PORT = 65000
|
||||||
|
SLAVE_ARGS = ['--extraintf=luaintf','--lua-intf=syncplay','-vvv']
|
||||||
|
|
||||||
|
random.seed()
|
||||||
|
vlcport = random.randrange(VLC_MIN_PORT, VLC_MAX_PORT)
|
||||||
|
SLAVE_ARGS.append('--lua-config=syncplay={{port=\"{}\"}}'.format(str(vlcport)))
|
||||||
|
|
||||||
|
def __init__(self, client, playerPath, filePath, args):
|
||||||
|
self._client = client
|
||||||
|
self._paused = None
|
||||||
|
self._duration = None
|
||||||
|
self._filename = None
|
||||||
|
self._filepath = None
|
||||||
|
self._readyforchange = True
|
||||||
|
self._updatenotification = False
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._listener = self.__Listener(self, playerPath, filePath, args)
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
self._client.ui.showMessage("Failed to load VLC")
|
||||||
|
self._client.stop(True)
|
||||||
|
return
|
||||||
|
|
||||||
|
self._listener.setDaemon(True)
|
||||||
|
self._listener.start()
|
||||||
|
|
||||||
|
self._durationAsk = threading.Event()
|
||||||
|
self._filenameAsk = threading.Event()
|
||||||
|
self._pathAsk = threading.Event()
|
||||||
|
|
||||||
|
self._positionAsk = threading.Event()
|
||||||
|
self._pausedAsk = threading.Event()
|
||||||
|
|
||||||
|
self._vlcready = threading.Event()
|
||||||
|
|
||||||
|
self._vlcready.wait()
|
||||||
|
self._preparePlayer()
|
||||||
|
|
||||||
|
def _fileUpdateClearEvents(self):
|
||||||
|
self._durationAsk.clear()
|
||||||
|
self._filenameAsk.clear()
|
||||||
|
self._pathAsk.clear()
|
||||||
|
|
||||||
|
def _fileUpdateWaitEvents(self):
|
||||||
|
self._durationAsk.wait()
|
||||||
|
self._filenameAsk.wait()
|
||||||
|
self._pathAsk.wait()
|
||||||
|
|
||||||
|
|
||||||
|
def _onFileUpdate(self):
|
||||||
|
self._fileUpdateClearEvents()
|
||||||
|
self._getFilename()
|
||||||
|
self._getFilepath()
|
||||||
|
self._getLength()
|
||||||
|
self._fileUpdateWaitEvents()
|
||||||
|
self._client.updateFile(self._filename, self._duration, self._filepath)
|
||||||
|
|
||||||
|
def _preparePlayer(self):
|
||||||
|
self.setPaused(self._client.getGlobalPaused())
|
||||||
|
self.setPosition(self._client.getGlobalPosition())
|
||||||
|
self._client.initPlayer(self)
|
||||||
|
self._readyforchange = True
|
||||||
|
|
||||||
|
def askForStatus(self):
|
||||||
|
if (self._updatenotification):
|
||||||
|
self._updatenotification = False
|
||||||
|
self._onFileUpdate()
|
||||||
|
|
||||||
|
self._positionAsk.clear()
|
||||||
|
self._pausedAsk.clear()
|
||||||
|
self._listener.sendLine(".")
|
||||||
|
self._positionAsk.wait()
|
||||||
|
self._pausedAsk.wait()
|
||||||
|
self._client.updatePlayerStatus(self._paused, self._position)
|
||||||
|
|
||||||
|
def displayMessage(self, message, duration = constants.OSD_DURATION):
|
||||||
|
self._listener.sendLine('display-osd: {}, {}, {}'.format('top-right', duration, message))
|
||||||
|
|
||||||
|
def setSpeed(self, value):
|
||||||
|
self._setProperty('speed', "{:.2f}".format(value))
|
||||||
|
self._listener.sendLine("set-rate: {:.2f}".format(value))
|
||||||
|
|
||||||
|
def setPosition(self, value):
|
||||||
|
self._position = value
|
||||||
|
self._listener.sendLine("set-position: {}".format(value))
|
||||||
|
|
||||||
|
def setPaused(self, value):
|
||||||
|
self._paused = value
|
||||||
|
self._listener.sendLine('set-playstate: {}'.format("paused" if value else "playing"))
|
||||||
|
|
||||||
|
def _getFilename(self):
|
||||||
|
self._listener.sendLine("get-filename")
|
||||||
|
|
||||||
|
def _getLength(self):
|
||||||
|
self._listener.sendLine("get-duration")
|
||||||
|
|
||||||
|
def _getFilepath(self):
|
||||||
|
self._listener.sendLine("get-filepath")
|
||||||
|
|
||||||
|
def _getPaused(self):
|
||||||
|
self._listener.sendLine(".")
|
||||||
|
|
||||||
|
def _getPosition(self):
|
||||||
|
self._listener.sendLine(".")
|
||||||
|
|
||||||
|
def lineReceived(self, line):
|
||||||
|
#print "received: {}".format(line)
|
||||||
|
if (line[:16] == "VLC media player"):
|
||||||
|
self._vlcready.set()
|
||||||
|
return
|
||||||
|
|
||||||
|
elif(line == "filepath-change-notification"):
|
||||||
|
if (self._readyforchange):
|
||||||
|
self._updatenotification = True
|
||||||
|
return
|
||||||
|
|
||||||
|
match = self.RE_ANSWER.match(line)
|
||||||
|
if not match:
|
||||||
|
return
|
||||||
|
name, value = match.group('command'), match.group('argument')
|
||||||
|
|
||||||
|
if (name == "filepath"):
|
||||||
|
if (value != "no-input"):
|
||||||
|
self._filepath = value
|
||||||
|
self._pathAsk.set()
|
||||||
|
|
||||||
|
|
||||||
|
elif(name == "duration"):
|
||||||
|
if (value != "no-input"):
|
||||||
|
self._duration = float(value)
|
||||||
|
print line
|
||||||
|
self._durationAsk.set() #
|
||||||
|
|
||||||
|
elif(name == "playstate"):
|
||||||
|
if(value == "no-input"):
|
||||||
|
self._paused = self._client.getGlobalPaused()
|
||||||
|
else:
|
||||||
|
self._paused = bool(value != 'playing')
|
||||||
|
self._pausedAsk.set()
|
||||||
|
|
||||||
|
elif(name == "position"):
|
||||||
|
if (value == "no-input"):
|
||||||
|
self._position = self._client.getGlobalPosition()
|
||||||
|
else:
|
||||||
|
self._position = float(value)
|
||||||
|
self._positionAsk.set()
|
||||||
|
|
||||||
|
elif(name == "get-interface-version"):
|
||||||
|
print name
|
||||||
|
|
||||||
|
elif(name == "play-error"):
|
||||||
|
print name
|
||||||
|
|
||||||
|
elif(name == "set-playstate-error"):
|
||||||
|
print name
|
||||||
|
|
||||||
|
elif(name == "set-rate-error"):
|
||||||
|
print name
|
||||||
|
|
||||||
|
elif(name == "display-osd-error"):
|
||||||
|
print name
|
||||||
|
|
||||||
|
elif(name == "filename"):
|
||||||
|
self._filename = value
|
||||||
|
self._filenameAsk.set()
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def run(client, playerPath, filePath, args):
|
||||||
|
vlc = VlcPlayer(client, VlcPlayer.getExpandedPath(playerPath), filePath, args)
|
||||||
|
return vlc
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getDefaultPlayerPathsList():
|
||||||
|
l = []
|
||||||
|
for path in constants.VLC_PATHS:
|
||||||
|
p = VlcPlayer.getExpandedPath(path)
|
||||||
|
if p:
|
||||||
|
l.append(p)
|
||||||
|
return l
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def isValidPlayerPath(path):
|
||||||
|
if("vlc" in path and VlcPlayer.getExpandedPath(path)):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getExpandedPath(playerPath):
|
||||||
|
if os.access(playerPath, os.X_OK):
|
||||||
|
return playerPath
|
||||||
|
for path in os.environ['PATH'].split(':'):
|
||||||
|
path = os.path.join(os.path.realpath(path), playerPath)
|
||||||
|
if os.access(path, os.X_OK):
|
||||||
|
return path
|
||||||
|
|
||||||
|
def drop(self):
|
||||||
|
self._listener.sendLine('close-vlc')
|
||||||
|
self._durationAsk.set()
|
||||||
|
self._filenameAsk.set()
|
||||||
|
self._pathAsk.set()
|
||||||
|
self._positionAsk.set()
|
||||||
|
self._vlcready.set()
|
||||||
|
self._pausedAsk.set()
|
||||||
|
self._client.stop(False)
|
||||||
|
|
||||||
|
class __Listener(threading.Thread, asynchat.async_chat):
|
||||||
|
def __init__(self, playerController, playerPath, filePath, args):
|
||||||
|
self.__playerController = playerController
|
||||||
|
call = [playerPath]
|
||||||
|
if(filePath):
|
||||||
|
call.append(filePath)
|
||||||
|
call.extend(playerController.SLAVE_ARGS)
|
||||||
|
if(args):
|
||||||
|
call.extend(args)
|
||||||
|
|
||||||
|
self.__process = subprocess.Popen(call)
|
||||||
|
threading.Thread.__init__(self, name="VLC Listener")
|
||||||
|
asynchat.async_chat.__init__(self)
|
||||||
|
self.set_terminator("\n")
|
||||||
|
self._ibuffer = []
|
||||||
|
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.connect(('localhost', self.__playerController.vlcport))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
asyncore.loop()
|
||||||
|
|
||||||
|
def collect_incoming_data(self, data):
|
||||||
|
self._ibuffer.append(data)
|
||||||
|
|
||||||
|
def found_terminator(self):
|
||||||
|
self.__playerController.lineReceived("".join(self._ibuffer))
|
||||||
|
self._ibuffer = []
|
||||||
|
|
||||||
|
def sendLine(self, line):
|
||||||
|
self.__playerController._vlcready.wait()
|
||||||
|
#print "send: {}".format(line)
|
||||||
|
self.push(line + "\n")
|
||||||
Loading…
x
Reference in New Issue
Block a user