Python Forum

Full Version: Echo call to VLC bash using subprocess.Popen on Linux
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
I am using this both on my macosx and linux and it works well.
On linux the shell commands are slightly different:

vlc -I oldrc --rc-unix=/home/user/Documents/Sockets/socket1.sock
vlc -I oldrc --rc-unix=/home/user/Documents/Sockets/socket2.sock

and

echo play | nc -U /home/user/Documents/Sockets/socket1.sock
echo enqueue /home/user/Documents/DualscreenVLC/videos/MVI_2534.MP4 | nc -U /home/user/Documents/Sockets/socket1.sock

I am attempting to use subprocess.Popen in Python for further control but here I run into difficulties on Linux (Ubuntu). It either hangs on the stdout.read() call or when I take this away, all the shell commands are only executed after the script finishes and I get a broken pipe error. The issue seems to be specifically with the 'echo' commands. Did I migrate wrong?
This is the python script:

import subprocess
import time

Socket1Location = "/Users/user/socket1.sock"
Socket2Location = "/Users/user/socket2.sock"
video1 = "/Users/user/Documents/Scripts/DualscreenVLC/videos/video01.mp4"
video2 = "/Users/user/Documents/Scripts/DualscreenVLC/videos/video02.mp4"

def RunVLCCommand1(cmd):
    p = subprocess.Popen("echo " + cmd + " | nc -U " + Socket1Location, shell = True, stdout = subprocess.PIPE)
    errcode = p.wait()
    retval = p.stdout.read()
    print "returning: " + retval
    return retval
    
def RunVLCCommand2(cmd):
    p = subprocess.Popen("echo " + cmd + " | nc -U " + Socket2Location, shell = True, stdout = subprocess.PIPE)
    errcode = p.wait()
    retval = p.stdout.read()
    print "returning: " + retval
    return retval

subprocess.Popen(["/Applications/VLC.app/Contents/MacOS/VLC","--rc-unix=/Users/user/socket1.sock"])
subprocess.Popen(["/Applications/VLC.app/Contents/MacOS/VLC","--rc-unix=/Users/user/socket2.sock"])

#RunVLCCommand1 and 2 can now be used to send commands to VLC, for example RunVLCCommand1('play')
Hi, try this perhaps
p = subprocess.run("echo " + cmd + " | nc -U " + Socket1Location, shell = True, capture_output=True)
print(p.returncode)
print(p.stdout)
Thank you, I tried it but I get an "attribute error: 'module' object has no attribute 'run'.
Should I call another library?
subprocess.run() is new in python 3.5. Can you upgrade to this version? Hmm. Actually, the capture_output parameter is new in 3.7. You could perhaps simply use the communicate() method.i
p = subprocess.Popen("echo " + cmd + " | nc -U " + Socket1Location, shell = True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
When I use communicate() the script hangs, just like when I use stdout.read()...
This is the script:

p = subprocess.Popen(['vlc -I oldrc --rc-unix=/home/user/Documents/Sockets/socket1.sock'], shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
out, err = p.communicate()

p = subprocess.Popen(['echo enqueue /home/user/Documents/DualscreenVLC/videos/MVI_2534.MP4 } nc -U /home/user/Documents/Sockets/socket1.sock'], shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
out, err = p.communicate()
I can try upgrading to python 3.5 tomorrow but it maybe it is related to the operating system as I am not finding this issue anywhere else...
There is an old code that I used in Linux for certain cases when stdout blocks with subprocess. It uses socketpair() and select(). Here is an example involving two scripts a.py and b.py (launched by a.py which reads its output), but it also works when the second process is not a python program. I already posted this in other forums but I think it's a nice trick to know
##################################
# a.py

import subprocess
import socket
import select

esock, echildsock = socket.socketpair()
osock, ochildsock = socket.socketpair()
p = subprocess.Popen(['python3','b.py'], 
    stderr=echildsock.fileno(),
    stdout=ochildsock.fileno())

while p.poll() is None:
    r, w, x = select.select([esock, osock],[],[], 1.0)
    if not r:
        continue # timed out
    for s in r:
        print('stdout ready' if s is osock else 'stderr ready')
        data = s.recv(1024)
        print('received', data.decode('utf8'))
osock.shutdown(socket.SHUT_RDWR)
osock.close()
esock.shutdown(socket.SHUT_RDWR)
esock.close()
##################################
# b.py

res = input('???')
print('in b:', res)
Thank you, that looks great.

If I understand it well, I can replace asock and osock with the vlc socket locations.
And I can add the echo commands to the input in b.py so it will be send to both sockets?
Does for s in r: mean that the p.poll() command iterates through b.py?
Quote:If I understand it well, I can replace asock and osock with the vlc socket locations.

No, only replace the call to Popen() at line 10-12 with your command:
p = subprocess.Popen("echo " + cmd + " | nc -U " + Socket1Location,
    shell = True, 
    stderr=echildsock.fileno(),
    stdout=ochildsock.fileno())
Quote:Does for s in r: mean that the p.poll() command iterates through b.py?
No while p.poll() is None iterates while the process p is running. Perhaps you don't know the select command. The call to select() blocks until there is something to read in the sockets esock or osock which are connected to p's error and output streams. It returns a list of sockets that are ready, so r can be [esock] or [osock] or [esock, osock]. The for s in r reads the data from the sockets that have something to receive. I added a timeout of 1 second in select() so that every one second, if there is nothing to read, a.py checks if process p is still alive.
Thank you very much, that makes sense.
I have been trying to implement it but without the shell=true flag I get this error:
OSError: [Errno 2] No such file or directory
When I split the command into:
(['vlc','-I oldrc --rc-unix=/home/user/Documents/Sockets/socket1.sock'],stderr=echildsock.fileno(), stdout=ochildsock.fileno())
The error is mitigated but no socket is opened. (I tried every combination of splitting the command).

When I do add the shell=True command the code hangs at the stdout again except when I add a 'break' to the while loop. An instance of VLC loads but the echo calls don't seem to communicate with vlc.
You could perhaps simply remove the echo command and write directly to the nc command stdin
p = subprocess.Popen(["nc", "-U ", Socket1Location], stdin = subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out, err = p.communicate(input=b'play')
Pages: 1 2