Skip to content

Instantly share code, notes, and snippets.

@galister
Last active January 31, 2026 03:36
Show Gist options
  • Select an option

  • Save galister/1a971254af72bb2a5bc27740e984bce2 to your computer and use it in GitHub Desktop.

Select an option

Save galister/1a971254af72bb2a5bc27740e984bce2 to your computer and use it in GitHub Desktop.
VRC AVPro Playback Script for Linux
#!/usr/bin/env python3
# This script was made to be used with WlxOverlay. When in an AVPro
# world, this will spawn a media player window on your desktop, allowing
# you to enjoy content via an overlay without the usual AVPro concerns.
#
# Usage: Start VRChat with AVPro DISABLED and run this script.
# Optional: In case of a non-default install location, change below.
# Requirements: ffplay (for rtsp streams), mpv (for everything else)
import glob
import os
import re
import subprocess
import time
from datetime import datetime,timedelta,tzinfo
from dateutil.tz import tzlocal
from zoneinfo import ZoneInfo
home = os.getenv("HOME")
# Change me
vrc_appdata : str = f"{home}/.steam/steam/steamapps/compatdata/438100/pfx/drive_c/users/steamuser/AppData/LocalLow/VRChat/VRChat"
# Change me
tz : tzinfo = tzlocal()
# Change me
default_volume_percent : str = "50"
ts_fmt : str = "%Y.%m.%d %H:%M:%S"
log_path : str = f"{vrc_appdata}/output_log_*"
playback_re = re.compile(r"^([0-9.]+ [0-9:]+)[^-]+- \s+\[Video Playback\] (?:Resolving URL|Attempting to resolve URL|URL '[^']+' resolved to) \'([^\']+)")
interrupt_re = re.compile(r"^([0-9.]+ [0-9:]+)[^-]+- \s+\[(KineL|USharpVideo|iwaSync\d|ATA .+Now Playing|VideoTXL[^]]+|Behaviour\] OnLeftRoom)")
cur_file : str = ""
cur_pos : int = 0
video_start_time : datetime = datetime.min.replace(tzinfo=tz)
player_process : subprocess.Popen | None = None
def cleanup_player() -> None:
global player_process, video_start_time
if player_process:
try:
print("Stopping player...")
player_process.terminate()
except:
pass
video_start_time = datetime.min.replace(tzinfo=tz)
player_process = None
def launch_player(url: str, elapsed: timedelta) -> None:
global player_process
cleanup_player()
url = url.replace("rtspt:", "rtsp:")
if url.startswith("rtsp:"):
args = ["ffplay", "-volume="+default_volume_percent];
else:
args = ["mpv", "--cache-secs=5", "--volume="+default_volume_percent]
if elapsed.total_seconds() > 5:
args.append(f"--start={str(elapsed)}")
args.append(url)
print("Running: " + " ".join(args))
player_process = subprocess.Popen(args);
def scan_file() -> None:
global cur_file, cur_pos, video_start_time
files = glob.glob(log_path)
latest_file = max(files, key=os.path.getmtime)
if latest_file != cur_file:
cur_pos = 0
cur_file = latest_file
with open(cur_file) as f:
f.seek(cur_pos)
old_pos = cur_pos
lines = []
while True:
line = f.readline()
if not line:
break
lines.append(line)
# debug
# print('\r' + line.strip(), end='')
cur_pos = f.tell()
for line in reversed(lines):
m = interrupt_re.match(line)
if m:
cleanup_player()
print("Stop Triggered: " + line.rstrip())
break
m = playback_re.match(line)
if m:
start_time = datetime.strptime(m.group(1), ts_fmt).replace(tzinfo=tz)
elapsed = datetime.now(tz) - start_time
if elapsed.total_seconds() < 1:
cur_pos = old_pos
break
if start_time > video_start_time:
print("Play Triggered: " + line.rstrip())
video_start_time = start_time
launch_player(m.group(2), elapsed)
break
if __name__ == "__main__":
while True:
scan_file()
time.sleep(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment