How to add to VLC playlist queue using Python subprocess - python

I am trying to use the Python 2.7 subprocess library to programmatically add songs to the VLC player queue.
From here and here, I am able to launch VLC Player and play a song (or queue songs from the outset);
from subprocess import Popen
vlcpath = r'C:\Program Files (x86)\VideoLAN\VLC\vlc.exe'
musicpath1 = r'path\to\song1.mp3'
musicpath2 = r'path\to\song2.mp3'
p = Popen([vlcpath,musicpath1]) # launch VLC and play song
p = Popen([vlcpath,musicpath1,musicpath2]) # launch VLC and play/queue songs
The problem is that I do not know the entire queue playlist at launch. I want to be able to add songs to the queue of the VLC process already running. How do I accomplish this, please?
From here, I think the appropriate command line entry is:
vlc.exe --started-from-file --playlist-enqueue "2.wmv"
But I do not know the syntax to execute this in subprocess. I tried a couple of things, but couldn't get either to work:
calling Popen again (opens a new process)
calling p.communicate (I thought this is how to enter stdin commands)

To run the command: vlc.exe --started-from-file --playlist-enqueue "2.wmv"
using subprocess module on Windows:
from subprocess import Popen
cmd = 'vlc.exe --started-from-file --playlist-enqueue "2.wmv"'
p = Popen(cmd) # start and forget
assert not p.poll() # assert that it is started successfully
To wait for the command to finish:
from subprocess import check_call
check_call(cmd) # start, wait until it is done, raise on non-zero exit status
But how do I run that command a second time on the same p process?
Your code starts a new instance of VLC, rather than running that on
top of the p that was already open. I found that if I run the vlc.exe --started-from-file --playlist-enqueue "2.wmv" command multiple times manually (in a command prompt window), it correctly launches vlc (the
first time) and then adds to queue (on subsequent calls). So I think I
just need to be able to run the code you suggested multiple times "on
top of itself"
Each Popen() starts a new process. Each time you run the command manually in the command-line it starts a new process. It might be upto the current vlc configuration on your system whether it keeps multiple vlc instances or you are running a different command (different command-line arguments).

Related

Subprocess for python loop of commands [duplicate]

I have created a simple method that executes a command like you do in the terminal
from subprocess import PIPE, run
class Command_Line():
#staticmethod
def execute(command):
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True)
print(result)
return result.stdout
My problem with the code above is it does not wait until the task/process is done. Lets say i use ffmpeg to change the frame rate of a video via the following code
import Command_Line as cmd
cmd.execute('ffmpeg -i "000000004.avi" -c copy -y -r 30 "000000004.avi"')
The problem is the output video because it does not complete the process. I've search how to wait like Is there a way to check if a subprocess is still running?
but could not incorporate it with my code. Can you share your experience with this.
Thanks
According to the python documentation subprocess.run waits for the process to end.
The problem is that ffmpeg overwrites the input file if the input and output files are the same and therefore the output video becomes unusable.
subprocess.run() is synchronous which means that the system will wait till it finishes before moving on to the next command. subprocess.Popen() does the same thing but it is asynchronous (the system will not wait for it to finish). You can try reloading your file using importlib.reload command. It may find your generated file then.

Python: Keep processes started by subprocess.Popen alive after exiting

I am making a virtual assistant that can start several programs using subprocess.Popen("path/to/app.exe"). But when I exit the python program, all of processes are killed. I want the processes (the applications started with Popen) to be independent and remain alive after main process is killed.
I have tried adding start_new_session=True as argument in subprocess.Popen() as some posts have suggested, but it's still not working.
I don't think showing the code is necessary, but still, here you go.
app_path = r'C:\Users\myusername\AppData\Local\Discord\app-1.0.9001\discord.exe'
subprocess.Popen(app_path) # also tried adding start_new_session=True as argument
Since you're on Windows, you can call the start command, which exists for this very purpose: to run another program independently of the one that starts it.
The start command is provided by the command-line interpreter cmd.exe. It is not an executable: there is no start.exe. It is a "shell command" (in Linux terminology), which is why shell=True must be passed when creating the subprocess.
You won't be able to communicate with the subprocess started in this way, that is, not via the pipe mechanism provided by the subprocess module. So instead of Popen, you may just use the convenience function run:
from subprocess import run
app = 'notepad'
run(['start', app], shell=True)
The example starts the Notepad editor (instead of Discord in the question) in order to make it easier to reproduce.
In cases where the full path to the app contains spaces, we can either call start like so
app = r'C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe'
run(f'start "" "{app}"', shell=True)
using the Edge browser in this example, or pass the directory separately:
folder = r'C:\Program Files (x86)\Microsoft\Edge\Application'
app = 'msedge.exe'
run(['start', '/d', folder, app], shell=True)
This is needed because start treats a single argument as the window title if that argument is in quotes. And only if not does it treat it as the command. See "Can I use the start command with spaces in the path?" (on SuperUser) for more details.
Answered here: https://stackoverflow.com/a/34718600/4355695
subprocess.Popen(full_command, shell=True, close_fds=True)
(In my linux system I have to put shell=True if I'm passing a full command as string instead of a split-up array of arguments. In windows it may differ, idk)

Python Mplayer Script Doesn't Run While Playing

I am very much a noob with Python and Linux commands but I'm trying to wrap my head around it.
My application is bigger than this sample but I am trying to get this working before expanding.
I am using mplayer in slave mode to play a video clip but I want to automatically kill it when the clip is done. So my idea is to start mplayer in slave mode then command 'get_time_length', store that value, sleep for that value, kill it and return to the main routine.
import os
import commands
import glob
#Global Commands
glcmd = 'get_time_length'
print("Preparing for kiosk mode! Press CTRL+C to exit")
try:
while 1:
path = '/media/*/button1/*'
os.system('mplayer -fs -quiet -slave path')
line = os.popen(glcmd).readline().strip() #Get the duration from mplayer
duration = line.split('=')[1] #Get just the length of video in Seconds
print(duration)
sleep(duration) #Wait until the video is done
os.system('quit')
The problem that I am having is that once the video clip starts playing, it never runs any commands during play. I'm also not sure if my syntax is right in order to retrieve the command line but I at least want to figure out how to be able to send commands with to mplayer with my script while a video is playing.
Thanks!
You can use the subprocess lib as recommended in the docs which say about os.popen Deprecated since version 2.6: This function is obsolete, all you need to do is use check_call the program will end when the video does.
from subprocess import check_call
path = '/media/*/button1/*'
check_call(['mplayer',"-fs", '-quiet' ,'-slave',path])
If you wanted to communicate somehow you would use subprocess.Popen, this will print all the output in a loop and terminate the process if the user enter ctrl-c:
from time import sleep
from subprocess import Popen, PIPE
path = '/media/*/button1/*'
try:
p = Popen(['mplayer', '-quiet' ,'-slave', path],stdout=PIPE, stdin=PIPE)
for line in iter(p.stdout.readline,""):
print(line)
except KeyboardInterrupt:
p.terminate()
If you wanted to send commands you would write to p.stdin

Sending commans on sub process in python

I just want to build a little python music client on my raspberry pi. I installed "mpg321" and it works great but now my problem. After sending the command
os.system("mpg321 -R testPlayer")
python waits for user input like play, pause or quit. If I write this in my terminal the player pause the music oder quits. Perfect but I want python to do that so I send the command
os.system("LOAD test.mp3")
where LOAD is the command for loading this mp3. But nothing happens. When I quit the player via terminal I get the error:
sh: 1: LOAD: not found
I think this means that
os.system("mpg321 -R testPlayer")
takes the whole process and after I quit it python tries to execute the comman LOAD. So how do I get these things work together?
My code:
import os
class PyMusic:
def __init__(self):
print "initial stuff later"
def playFile(self, fileName, directory = ""):
os.system("mpg321 -R testPlayer")
os.system("LOAD test.mp3")
if __name__ == "__main__":
pymusic = PyMusic()
pymusic.playFile("test.mp3")
Thanks for your help!
First, you should almost never be using os.system. See the subprocess module.
One major advantage of using subprocess is that you can choose whatever behavior you want—run it in the background, start it and wait for it to finish (and throw an exception if it returns non-zero), interact with its stdin and stdout explicitly, whatever makes sense.
Here, you're not trying to run another command "LOAD test.mp3", you're trying to pass that as input to the existing process. So:
p = subprocess.Popen(['mpg321', '-R', 'testPlayer'], stdin=PIPE)
Then you can do this:
p.stdin.write('LOAD test.mp3\n')
This is roughly equivalent to doing this from the shell:
echo -e 'LOAD test.mp3\n' | mpg321 -R testPlayer
However, you should probably read about communicate, because whenever it's possible to figure out how to make your code work with communicate, it's a lot simpler than trying to deal with generic I/O (especially if you've never coded with pipes, sockets, etc. before).
Or, if you're trying to interact with a command-line UI (e.g., you can't send the command until you get the right prompt), you may want to look at an "expect" library. There are a few of these to choose from, so you should search at PyPI to find the right one for you (although I can say that I've used pexpect successfully in the past, and the documentation is full of samples that get the ideas across a lot more quickly than most expect documentation does).
You are looking for a way to send data to stdin. Here is an example of this using Popen:
from subprocess import Popen, PIPE, STDOUT
p = Popen(['mpg321', '-R testPlayer'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)
mpg123_stdout = p.communicate(input='LOAD test.mp3\n')[0]
print(mpg123_stdout)
You establish pointers to stdin and stdout, then after you start your process, you communicate with stdin and read from stdout. Be sure to send new lines (carriage returns)

waiting for multiple Python processes to finish in Windows 7 CMD

I am trying to run multiple Python scripts in parallel in Windows 7 (and 10). I am running them all from another Python script, which performs more functions on the files the scripts are editing. I want the external script to wait until the other scripts are done running. I have tried start /w, but that made each script wait before closing the console window.
Essentially what I want to do is for Python to wait until the 3 processes are done. The last script is just a print("done"), and is meaningless for all I care. This is important for me to solve with 3 processes because I need to do the same thing with 30. (On a server, there are enough available threads.)
This is the CMD command I am trying to run.
os.system("start python node1.py & start python node2.py & start python node3.py && start /w printstatement.py")
Any suggestions?
Use subprocess.Popen instead of os.system. You'll get 3 Popen instances that you can wait on. For example:
import subprocess
procs = [subprocess.Popen(['python', 'node{}.py'.format(n)])
for n in range(1, 4)]
retcodes = [p.wait() for p in procs]
If you want separate console windows, like how CMD's start command works, then add the option creationflags=subprocess.CREATE_NEW_CONSOLE to the Popen call (Windows only). If you instead want separate consoles that don't create windows, use creationflags=CREATE_NO_WINDOW (0x08000000). In this case they still have console standard I/O; it's just not rendered to a window.
Solution using asyncio:
import asyncio
commands = [
'python node1.py',
'python node2.py',
]
async def run_command(command):
task = await asyncio.create_subprocess_exec(*command.split())
await task.wait()
combined_task = asyncio.gather(*(run_command(command) for command in commands))
asyncio.get_event_loop().run_until_complete(combined_task)

Categories

Resources