-
-
Save ramast/c47bd5e57586e9c2deb74975e27089f0 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3 | |
# Based on code from these stackoverflow answers: | |
# https://askubuntu.com/questions/60837/record-a-programs-output-with-pulseaudio/910879#910879 | |
import re | |
import subprocess | |
import sys | |
import os | |
import signal | |
from time import sleep | |
INDEX_RE = re.compile(r'[0-9]+$') | |
APP_NAME_RE = re.compile(r'"([^"]+)"') | |
SINK_RE=re.compile("\s*sink: ([0-9]+) <.*>") | |
DEFAULT_OUTPUT_RE = re.compile(r'^\s*name: <([^ >]+)>') | |
record_module_id = None | |
def get_default_output(): | |
#pacmd list-sinks | grep -A1 "* index" | grep -oP "<\K[^ >]+" | |
output = subprocess.run(["pacmd", "list-sinks"], stdout=subprocess.PIPE, check=True).stdout | |
for line in output.decode('utf-8').split('\n'): | |
match = DEFAULT_OUTPUT_RE.match(line) | |
if match: | |
return match[1] | |
print("Can't seem to find proper input sink, are you using pulseaudio?") | |
sys.exit(3) | |
def load_record_module(): | |
default_output = get_default_output() | |
output = subprocess.run( | |
["pactl", "load-module", "module-combine-sink", "sink_name=record-n-play", f"slaves={default_output}", | |
"sink_properties=device.description=Record-and-Play"], | |
stdout=subprocess.PIPE, check=True).stdout | |
return int(output.strip()) | |
def load_apps(): | |
output = subprocess.run(["pacmd", "list-sink-inputs"], stdout=subprocess.PIPE, check=True).stdout | |
output = output.decode('utf-8').split('\n') | |
indexes = [] | |
app_names = [] | |
sinks = [] | |
for line in output: | |
if "index" in line: | |
index = INDEX_RE.findall(line)[0] | |
indexes.append(index) | |
elif "application.name" in line: | |
app_name = APP_NAME_RE.findall(line)[0] | |
app_names.append(app_name) | |
elif len(sinks) < len(indexes) and "sink: " in line: | |
sink = SINK_RE.match(line)[1] | |
sinks.append(sink) | |
if len(indexes) == 0: | |
print("Sorry, couldn't find any input audio channels") | |
sys.exit(1) | |
return indexes, app_names, sinks | |
def cleanup(*args, **kwargs): | |
if record_module_id is None: | |
sys.exit(0) | |
return | |
os.system(f"pactl move-sink-input {indexes[user_selection]} {sinks[user_selection]}") | |
os.system(f"pactl unload-module {record_module_id}") | |
print("Terminated") | |
sys.exit(0) | |
signal.signal(signal.SIGTERM, cleanup) | |
signal.signal(signal.SIGINT, cleanup) | |
if os.path.exists("temp.mp3"): | |
print("temp.mp3 already exist, aborting") | |
sys.exit(2) | |
_, app_names, _ = load_apps() | |
print("") | |
for idx, app_name in enumerate(app_names): | |
print(f"{idx + 1} - {app_name}") | |
print("") | |
while True: | |
try: | |
user_selection = int(input("Please enter a number: ")) | |
except ValueError: | |
print("Only numbers are allowed") | |
continue | |
if user_selection > len(app_names) or user_selection <= 0: | |
print("Number out of range") | |
continue | |
user_selection = int(user_selection) - 1 | |
break | |
app_name = app_names[user_selection] | |
print(f"Your selection was: {app_name}") | |
input("Please press enter when you are ready to start") | |
while True: | |
indexes, app_names, sinks = load_apps() | |
if app_name not in app_names: | |
print("Couldn't find selected audio channel, retrying") | |
sleep(0.2) | |
continue | |
user_selection = app_names.index(app_name) | |
record_module_id=load_record_module() | |
os.system(f"pactl move-sink-input {indexes[user_selection]} record-n-play") | |
os.system(f"parec --format=s16le -d record-n-play.monitor | lame -r -q 3 --lowpass 17 --abr 192 - 'temp.mp3'") | |
cleanup() |
cleaned up this code (with the black formatter), switched to subprocess for calls and made the encoder configurable in:
https://gitlab.com/anarcat/scripts/-/blob/master/pulse-recorder.py
let me know what the license of this is so i can give proper credit! :)
Thanks Anarcat, This work is all based on code written on stackoverflow
https://askubuntu.com/questions/60837/record-a-programs-output-with-pulseaudio/910879#910879
By users Waschtl and KrisWebDev
If you want to give credit it, I guess can link to that stackoverflow link ?
As far as I know there are no license, you can do whatever you want with it.
If you want to give credit it, I guess can link to that stackoverflow link ?
I had that already in the commitlogs, but made it explicit in the comments at the top of file.
As far as I know there are no license, you can do whatever you want with it.
Actually, contents on Stackoverflow is covered by the CC-BY-SA-4.0 license, so that kind of matters (for example, I have to give attribution, and so do you!) :)
Actually, contents on Stackoverflow is covered by the CC-BY-SA-4.0
Thanks, This is really good to know. I've already gave attributions first comment after the code.
Hopefully that'd be enough
yeah i guess that's alright :)
oh, and by the way, the script has evolved quite a bit. it now properly handles multiple outputs and has an "automatic" mode that doesn't prompt the user. i hope you like it!
Your scripts looks a lot more sophisticated and judging by the code I think it has also more features.
I've tried to run it but ran into a problem
First I ran the script like this python3 ~/pulse-recorder.py --raw
and it just hangs with no output.
I realized that it's because I didn't pass the -i
parameter but what is happening in this case?
I've tried again with -i
and I liked how I could identify the process by it's unique ID. Really helpful but after choosing the process I wanted to record it didn't record anything.
I've ran it again with --debug
option and I guess that was the issue
WARNING:root:Couldn't find selected audio channel, retrying
I think this output should be visible without the need for --debug
. I am not sure why it couldn't find "selected audio channel` though? I've tried same experiment with my old script and seemed to record fine.
Steps to reproduce:
- Open youtube video (i've used firefox if that make any difference)
- pause the video
- run the script and choose Firefox process id
- run the video
- go back to the script and press enter to record.
Thanks but still doesn't seem to be working :(
running clients:
6966 - Firefox
Please enter a number: 6966
Press enter to record from Firefox...
INFO: Recording from client 6966 (Firefox)
Traceback (most recent call last):
File "/home/ramast/pulse-recorder.py", line 250, in <module>
main(args)
File "/home/ramast/pulse-recorder.py", line 145, in main
record_module_id = load_record_module(sinks[client_index])
KeyError: 6966
Please ignore that, I don't think it was a mistake form the script.
Seems to be working fine now
I've updated my stackoverflow answer to give mention to your script.
Thanks for sharing!!
Hey @ramast, do you think this script can adapt to Win(10)+?
Hi @ati-ince, Windows doesn't use PulseAudio and this solution is based on PulseAudio so no it's impossible :(
Sorry to hear that @ramast :"-( I didn't find yet any operating system independent solution. So, thank you for solving the Linux side problem.
Explanation on how to use can be found here
https://askubuntu.com/questions/60837/record-a-programs-output-with-pulseaudio/910879#910879