diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d9c297a..5116e729 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -84,4 +84,4 @@ repos: hooks: - id: codespell args: - - --skip=logs/**,data/**,*.ipynb + - --skip=logs/**,data/**,*.ipynb,obs_settings/** diff --git a/obs_settings/AMIProfile/basic.ini b/obs_settings/AMIProfile/basic.ini new file mode 100644 index 00000000..028e9db3 --- /dev/null +++ b/obs_settings/AMIProfile/basic.ini @@ -0,0 +1,103 @@ +[General] +Name=AMIProfile + +[Output] +Mode=Simple +FilenameFormatting=%CCYY-%MM-%DD %hh-%mm-%ss +DelayEnable=false +DelaySec=20 +DelayPreserve=true +Reconnect=true +RetryDelay=2 +MaxRetries=25 +BindIP=default +IPFamily=IPv4+IPv6 +NewSocketLoopEnable=false +LowLatencyEnable=false + +[Stream1] +IgnoreRecommended=false + +[SimpleOutput] +FilePath=/home/gop-geson +RecFormat2=mkv +VBitrate=2500 +ABitrate=160 +UseAdvanced=false +Preset=veryfast +NVENCPreset2=p5 +RecQuality=Stream +RecRB=false +RecRBTime=20 +RecRBSize=512 +RecRBPrefix=Replay +StreamAudioEncoder=aac +RecAudioEncoder=aac +RecTracks=1 +StreamEncoder=nvenc +RecEncoder=nvenc + +[AdvOut] +ApplyServiceSettings=true +UseRescale=false +TrackIndex=1 +VodTrackIndex=2 +Encoder=obs_x264 +RecType=Standard +RecFilePath=/home/gop-geson +RecFormat2=mkv +RecUseRescale=false +RecTracks=1 +RecEncoder=none +FLVTrack=1 +StreamMultiTrackAudioMixes=1 +FFOutputToFile=true +FFFilePath=/home/gop-geson +FFExtension=mp4 +FFVBitrate=2500 +FFVGOPSize=250 +FFUseRescale=false +FFIgnoreCompat=false +FFABitrate=160 +FFAudioMixes=1 +Track1Bitrate=160 +Track2Bitrate=160 +Track3Bitrate=160 +Track4Bitrate=160 +Track5Bitrate=160 +Track6Bitrate=160 +RecSplitFileTime=15 +RecSplitFileSize=2048 +RecRB=false +RecRBTime=20 +RecRBSize=512 +AudioEncoder=libfdk_aac +RecAudioEncoder=libfdk_aac + +[Video] +BaseCX=1920 +BaseCY=1080 +OutputCX=1920 +OutputCY=1080 +FPSType=0 +FPSCommon=60 +FPSInt=30 +FPSNum=30 +FPSDen=1 +ScaleType=bicubic +ColorFormat=NV12 +ColorSpace=709 +ColorRange=Partial +SdrWhiteLevel=300 +HdrNominalPeakLevel=1000 + +[Audio] +MonitoringDeviceId=default +MonitoringDeviceName=Default +SampleRate=48000 +ChannelSetup=Stereo +MeterDecayRate=23.53 +PeakMeterType=0 + +[Panels] +CookieId=17955BC7F8D42C27 diff --git a/obs_settings/AMISceneCollection.json b/obs_settings/AMISceneCollection.json new file mode 100644 index 00000000..a052b719 --- /dev/null +++ b/obs_settings/AMISceneCollection.json @@ -0,0 +1,181 @@ +{ + "DesktopAudioDevice1": { + "prev_ver": 503382018, + "name": "Desktop Audio", + "uuid": "2895ff9b-8f7a-42aa-b63e-6b34f3b8bd86", + "id": "pulse_output_capture", + "versioned_id": "pulse_output_capture", + "settings": { + "device_id": "default" + }, + "mixers": 255, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "libobs.mute": [], + "libobs.unmute": [], + "libobs.push-to-mute": [], + "libobs.push-to-talk": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + "current_scene": "VRChat", + "current_program_scene": "VRChat", + "scene_order": [ + { + "name": "VRChat" + } + ], + "name": "VRChat", + "groups": [], + "quick_transitions": [], + "transitions": [], + "saved_projectors": [], + "current_transition": "フェード", + "transition_duration": 300, + "preview_locked": false, + "scaling_enabled": false, + "scaling_level": 0, + "scaling_off_x": 0.0, + "scaling_off_y": 0.0, + "virtual-camera": { + "type2": 3 + }, + "modules": { + "scripts-tool": [], + "output-timer": { + "streamTimerHours": 0, + "streamTimerMinutes": 0, + "streamTimerSeconds": 30, + "recordTimerHours": 0, + "recordTimerMinutes": 0, + "recordTimerSeconds": 30, + "autoStartStreamTimer": false, + "autoStartRecordTimer": false, + "pauseRecordTimer": true + }, + "auto-scene-switcher": { + "interval": 300, + "non_matching_scene": "", + "switch_if_not_matching": false, + "active": false, + "switches": [] + } + }, + "resolution": { + "x": 1920, + "y": 1080 + }, + "sources": [ + { + "prev_ver": 503382018, + "name": "VRChat", + "uuid": "ab81d61c-361b-49ae-8335-f561cfcfc7eb", + "id": "scene", + "versioned_id": "scene", + "settings": { + "id_counter": 1, + "custom_size": false, + "items": [ + { + "name": "VRChatWindow", + "source_uuid": "8978c5f4-f71c-45f4-9582-f7bc80d25a76", + "visible": true, + "locked": false, + "rot": 0.0, + "pos": { + "x": 0.0, + "y": 0.0 + }, + "scale": { + "x": 1.0, + "y": 1.0 + }, + "align": 5, + "bounds_type": 2, + "bounds_align": 0, + "bounds_crop": false, + "bounds": { + "x": 1920.0, + "y": 1080.0 + }, + "crop_left": 0, + "crop_top": 0, + "crop_right": 0, + "crop_bottom": 0, + "id": 1, + "group_item_backup": false, + "scale_filter": "disable", + "blend_method": "default", + "blend_type": "normal", + "show_transition": { + "duration": 0 + }, + "hide_transition": { + "duration": 0 + }, + "private_settings": {} + } + ] + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": { + "OBSBasic.SelectScene": [], + "libobs.show_scene_item.1": [], + "libobs.hide_scene_item.1": [] + }, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + }, + { + "prev_ver": 503382018, + "name": "VRChatWindow", + "uuid": "8978c5f4-f71c-45f4-9582-f7bc80d25a76", + "id": "xcomposite_input", + "versioned_id": "xcomposite_input", + "settings": { + "capture_window": "121634819\r\nVRChat\r\nsteam_app_438100", + "show_cursor": false + }, + "mixers": 0, + "sync": 0, + "flags": 0, + "volume": 1.0, + "balance": 0.5, + "enabled": true, + "muted": false, + "push-to-mute": false, + "push-to-mute-delay": 0, + "push-to-talk": false, + "push-to-talk-delay": 0, + "hotkeys": {}, + "deinterlace_mode": 0, + "deinterlace_field_order": 0, + "monitoring_type": 0, + "private_settings": {} + } + ] +} diff --git a/scripts/auto-cycle-restart-vrchat.sh b/scripts/auto-cycle-restart-vrchat.sh new file mode 100755 index 00000000..93cb2965 --- /dev/null +++ b/scripts/auto-cycle-restart-vrchat.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# This script automatically restarts VRChat and sets up OBS at specified intervals. +# It requires restart-vrchat.sh and obs_vrchat_setup.py to be in the same directory. + +# Get the directory of the current script +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + +# Function to check if a command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +# Check if required commands exist +if ! command_exists pgrep || ! command_exists kill || ! command_exists steam || ! command_exists python3; then + echo "Error: Required commands (pgrep, kill, steam, or python3) not found. Please install them." + exit 1 +fi + +# Check if the Python script exists +if [ ! -f "${SCRIPT_DIR}/obs_vrchat_setup.py" ]; then + echo "Error: obs_vrchat_setup.py not found in the script directory." + exit 1 +fi + +# Check if the restart script exists +if [ ! -f "${SCRIPT_DIR}/restart-vrchat.sh" ]; then + echo "Error: restart-vrchat.sh not found in the script directory." + exit 1 +fi + +# Function to restart VRChat and setup OBS +restart_and_setup() { + echo "Restarting VRChat and setting up OBS..." + + # Run the restart-vrchat.sh script + bash "${SCRIPT_DIR}/restart-vrchat.sh" + + # Wait for VRChat to start (adjust this time if needed) + echo "Waiting for VRChat to start..." + sleep 30 + + # Run the Python script to setup OBS + python3 "${SCRIPT_DIR}/obs_vrchat_setup.py" + + echo "Cycle completed." +} + +# Main loop +while true; do + # Ask user for the cycle time in minutes + read -p "Enter the cycle time in minutes (or 'q' to quit): " cycle_time + + # Check if user wants to quit + if [ "$cycle_time" = "q" ]; then + echo "Exiting script." + exit 0 + fi + + # Validate input + if ! [[ "$cycle_time" =~ ^[0-9]+$ ]]; then + echo "Invalid input. Please enter a positive integer." + continue + fi + + # Convert minutes to seconds + seconds=$((cycle_time * 60)) + + echo "Starting cycle. Will restart VRChat and setup OBS every $cycle_time minutes." + + # Initial run + restart_and_setup + + # Loop until interrupted + while true; do + echo "Sleeping for $cycle_time minutes..." + sleep $seconds + restart_and_setup + done +done diff --git a/scripts/obs_vrchat_setup.py b/scripts/obs_vrchat_setup.py new file mode 100755 index 00000000..9a03e8c6 --- /dev/null +++ b/scripts/obs_vrchat_setup.py @@ -0,0 +1,112 @@ +#!/usr/bin/python3 + +"""OBS VRChat Window Selector and Virtual Camera Starter. + +Dependencies: + - Python 3.7+ + - obs-websocket-py + +Installation: + pip install obs-websocket-py + +Usage: + 1. Ensure OBS is installed and running. + 2. Enable WebSocket server in OBS (Tools -> WebSocket Server Settings). + 3. Import OBS settings: + - Open OBS + - Go to Scene Collection -> Import + - Select the scene collection file from the 'obs_settings' directory + - Go to Profile -> Import + - Select the profile file from the 'obs_settings' directory + 4. Run this script: + python obs_vrchat_setup.py + +This script will: + - Check if OBS is running + - Connect to OBS via WebSocket + - Select the VRChat window in the appropriate scene + - Start the virtual camera if it's not already running +""" + +import subprocess +import sys + +from obswebsocket import exceptions, obsws, requests + + +def check_obs_running() -> None: + result = subprocess.run(["pgrep", "-x", "obs"], capture_output=True, text=True) + if result.returncode != 0: + print("OBS is not running. Please start OBS **IN HOST DISPLAY**.") + sys.exit(1) + + +def connect_obs() -> obsws: + client = obsws("localhost", 4455, "") + try: + client.connect() + return client + except exceptions.ConnectionFailure: + print("OBS WebSocket is not enabled or unreachable. Please check OBS settings.") + sys.exit(1) + + +def select_vrchat_window(client: obsws) -> bool: + scene_list = client.call(requests.GetSceneList()).datain + vrchat_scene = next((scene for scene in scene_list["scenes"] if "vrchat" in scene["sceneName"].lower()), None) + + if not vrchat_scene: + print("VRChat scene not found.") + return False + + items = client.call(requests.GetSceneItemList(sceneName=vrchat_scene["sceneName"])).datain + + for item in items["sceneItems"]: + source_name = item["sourceName"] + source = client.call(requests.GetInputSettings(inputName=source_name)).datain + + if source["inputKind"] == "xcomposite_input": + windows_list = client.call( + requests.GetInputPropertiesListPropertyItems(inputName=source_name, propertyName="capture_window") + ).datain + + print("Available window list:", [d["itemName"] for d in windows_list["propertyItems"]]) + + vrchat_window = next((w for w in windows_list["propertyItems"] if "VRChat" == w["itemName"]), None) + + if vrchat_window: + client.call( + requests.SetInputSettings( + inputName=source_name, inputSettings={"capture_window": vrchat_window["itemValue"]} + ) + ) + print(f"VRChat window selected: {vrchat_window['itemName']}") + return True + else: + print("VRChat window not found in the list of available windows.") + + print("Appropriate window capture source not found.") + return False + + +def start_virtual_camera(client: obsws) -> None: + virtual_cam_status = client.call(requests.GetVirtualCamStatus()).datain + if not virtual_cam_status["outputActive"]: + client.call(requests.StartVirtualCam()) + print("Virtual camera started.") + else: + print("Virtual camera is already running.") + + +def main() -> None: + check_obs_running() + client = connect_obs() + try: + if select_vrchat_window(client): + start_virtual_camera(client) + finally: + client.disconnect() + + +if __name__ == "__main__": + main() diff --git a/scripts/setup-computer-ubuntu22.04.sh b/scripts/setup-computer-ubuntu22.04.sh index 1fd709b4..2c4fc145 100755 --- a/scripts/setup-computer-ubuntu22.04.sh +++ b/scripts/setup-computer-ubuntu22.04.sh @@ -18,7 +18,8 @@ sudo perl -p -i.bak -e 's%(deb(?:-src|)\s+)https?://(?!archive\.canonical\.com|s sudo apt update sudo apt upgrade -y -sudo apt install -y curl openssh-server git tmux nvidia-driver-550 btrfs-progs htop nvtop +sudo apt install -y curl openssh-server git tmux nvidia-driver-550 btrfs-progs htop nvtop python3 python3-pip +pip3 install poetry obs-websocket-py # --- Docker https://docs.docker.com/engine/install/ubuntu/ --- for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done