4 Commits

Author SHA1 Message Date
d7edaab2d1 bump obsws-python version
minor bump
2025-07-01 12:15:50 +01:00
09270683d9 implement record split
- CLI command added
- GUI button mouse/keyboard events implemented.

closes #1
2025-07-01 12:13:19 +01:00
b7c25525a1 catch ConnectionRefusedError as well as TimeoutError
patch bump
2025-06-30 07:44:23 +01:00
c39b909d11 fix gui --theme example 2025-06-30 07:04:33 +01:00
11 changed files with 74 additions and 17 deletions

View File

@@ -109,7 +109,7 @@ Just enter the filename and click *Start*.
You can change the colour theme with the --theme option: You can change the colour theme with the --theme option:
```console ```console
simple-recorder --theme="Light Purple" simple-recorder-gui --theme="Light Purple"
``` ```
[obs-studio]: https://obsproject.com/ [obs-studio]: https://obsproject.com/

8
pdm.lock generated
View File

@@ -5,7 +5,7 @@
groups = ["default", "build"] groups = ["default", "build"]
strategy = ["inherit_metadata"] strategy = ["inherit_metadata"]
lock_version = "4.5.0" lock_version = "4.5.0"
content_hash = "sha256:292c5ea319597e3539895c1ac50004c884c5d46edd5f7b195ede79156558feab" content_hash = "sha256:e744505d4be91a30830dd1d57d956a6c8775ea75385351b3f4c0d29faddd9ce3"
[[metadata.targets]] [[metadata.targets]]
requires_python = ">=3.11" requires_python = ">=3.11"
@@ -63,7 +63,7 @@ files = [
[[package]] [[package]]
name = "obsws-python" name = "obsws-python"
version = "1.7.2" version = "1.8.0"
requires_python = ">=3.9" requires_python = ">=3.9"
summary = "A Python SDK for OBS Studio WebSocket v5.0" summary = "A Python SDK for OBS Studio WebSocket v5.0"
groups = ["default"] groups = ["default"]
@@ -72,8 +72,8 @@ dependencies = [
"websocket-client", "websocket-client",
] ]
files = [ files = [
{file = "obsws_python-1.7.2-py3-none-any.whl", hash = "sha256:acda31852ad9d7165de915b0603c13f6df527d3f61619970bf5fb562e300bc85"}, {file = "obsws_python-1.8.0-py3-none-any.whl", hash = "sha256:537bde416e149b6f59e0b2f31761d4a40329feafec171bde6fc1346ab8516e28"},
{file = "obsws_python-1.7.2.tar.gz", hash = "sha256:b5cdaad30fbe1f6d4787b6530048b9882f070c3ee7830abb6dad4a47f84d7fa0"}, {file = "obsws_python-1.8.0.tar.gz", hash = "sha256:e082894f80deb0836861fdc3c222e497308c8f66328da6075baba5b456a20971"},
] ]
[[package]] [[package]]

View File

@@ -1,12 +1,12 @@
[project] [project]
name = "simple-recorder" name = "simple-recorder"
version = "0.3.4" version = "0.4.0"
description = "A simple OBS recorder" description = "A simple OBS recorder"
authors = [{ name = "onyx-and-iris", email = "code@onyxandiris.online" }] authors = [{ name = "onyx-and-iris", email = "code@onyxandiris.online" }]
dependencies = [ dependencies = [
"clypi>=1.8.1", "clypi>=1.8.1",
"FreeSimpleGUI>=5.2.0.post1", "FreeSimpleGUI>=5.2.0.post1",
"obsws-python>=1.7.2", "obsws-python>=1.8.0",
] ]
requires-python = ">=3.11" requires-python = ">=3.11"
readme = "README.md" readme = "README.md"

View File

@@ -8,6 +8,7 @@ from .errors import SimpleRecorderError
from .gui import SimpleRecorderWindow from .gui import SimpleRecorderWindow
from .pause import Pause from .pause import Pause
from .resume import Resume from .resume import Resume
from .split import Split
from .start import Start from .start import Start
from .stop import Stop from .stop import Stop
@@ -37,8 +38,11 @@ def theme_parser(value: str) -> str:
return value return value
SUBCOMMANDS = Start | Stop | Pause | Resume | Split | Directory
class SimpleRecorder(Command): class SimpleRecorder(Command):
subcommand: Start | Stop | Pause | Resume | Directory | None = None subcommand: SUBCOMMANDS | None = None
host: str = arg(default="localhost", env="OBS_HOST", help="OBS WebSocket host") host: str = arg(default="localhost", env="OBS_HOST", help="OBS WebSocket host")
port: int = arg(default=4455, env="OBS_PORT", help="OBS WebSocket port") port: int = arg(default=4455, env="OBS_PORT", help="OBS WebSocket port")
password: str | None = arg( password: str | None = arg(
@@ -52,7 +56,6 @@ class SimpleRecorder(Command):
) )
debug: bool = arg( debug: bool = arg(
default=False, default=False,
env="DEBUG",
help="Enable debug logging", help="Enable debug logging",
hidden=True, hidden=True,
) )

View File

@@ -32,5 +32,5 @@ class Directory(Command):
f"Current recording directory: {highlight(resp.record_directory)}" f"Current recording directory: {highlight(resp.record_directory)}"
) )
return resp.record_directory return resp.record_directory
except TimeoutError: except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?") raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -7,6 +7,7 @@ from .directory import Directory
from .errors import SimpleRecorderError from .errors import SimpleRecorderError
from .pause import Pause from .pause import Pause
from .resume import Resume from .resume import Resume
from .split import Split
from .start import Start from .start import Start
from .stop import Stop from .stop import Stop
@@ -94,6 +95,7 @@ class SimpleRecorderWindow(fsg.Window):
self["Stop Recording"].bind("<Return>", " || RETURN") self["Stop Recording"].bind("<Return>", " || RETURN")
self["Pause Recording"].bind("<Return>", " || RETURN") self["Pause Recording"].bind("<Return>", " || RETURN")
self["Resume Recording"].bind("<Return>", " || RETURN") self["Resume Recording"].bind("<Return>", " || RETURN")
self["Split Recording"].bind("<Return>", " || RETURN")
self["-FILENAME-"].bind("<KeyPress>", " || KEYPRESS") self["-FILENAME-"].bind("<KeyPress>", " || KEYPRESS")
self["-FILENAME-"].update(select=True) self["-FILENAME-"].update(select=True)
@@ -177,6 +179,19 @@ class SimpleRecorderWindow(fsg.Window):
else: else:
self["-OUTPUT-RECORDER-"].update("", text_color="white") self["-OUTPUT-RECORDER-"].update("", text_color="white")
case ["Split Recording"] | ["Split Recording", "RETURN"]:
try:
await Split(
host=self.host, port=self.port, password=self.password
).run()
self["-OUTPUT-RECORDER-"].update(
"Recording split successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red"
)
case ["Add Chapter", "RIGHT_CLICK"]: case ["Add Chapter", "RIGHT_CLICK"]:
_ = fsg.popup_get_text( _ = fsg.popup_get_text(
"Enter chapter name:", "Enter chapter name:",
@@ -184,7 +199,7 @@ class SimpleRecorderWindow(fsg.Window):
default_text="unnamed", default_text="unnamed",
) )
case ["Split Recording" | "Add Chapter"]: case ["Add Chapter"]:
self["-OUTPUT-RECORDER-"].update( self["-OUTPUT-RECORDER-"].update(
"This feature is not implemented yet", text_color="orange" "This feature is not implemented yet", text_color="orange"
) )

View File

@@ -26,5 +26,5 @@ class Pause(Command):
client.pause_record() client.pause_record()
print("Recording paused successfully.") print("Recording paused successfully.")
except TimeoutError: except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?") raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -26,5 +26,5 @@ class Resume(Command):
client.resume_record() client.resume_record()
print("Recording resumed successfully.") print("Recording resumed successfully.")
except TimeoutError: except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?") raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -0,0 +1,40 @@
import logging
import obsws_python as obsws
from clypi import Command, arg
from typing_extensions import override
from .errors import SimpleRecorderError
logging.basicConfig(level=logging.disable())
class Split(Command):
"""Split the current recording into a new file."""
host: str = arg(inherited=True)
port: int = arg(inherited=True)
password: str = arg(inherited=True)
@override
async def run(self):
"""Run the split command."""
try:
with obsws.ReqClient(
host=self.host, port=self.port, password=self.password, timeout=3
) as client:
resp = client.get_record_status()
if not resp.output_active:
raise SimpleRecorderError("No active recording to split.")
client.split_record_file()
print("Recording split successfully.")
except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")
except obsws.error.OBSSDKRequestError as e:
if e.code == 702:
raise SimpleRecorderError(
"Unable to split file, please check your OBS settings."
)
else:
raise SimpleRecorderError(f"Error: {e.code} - {e.message}")

View File

@@ -45,5 +45,5 @@ class Start(Command):
) )
client.start_record() client.start_record()
print(f"Recording started with filename: {highlight(filename)}") print(f"Recording started with filename: {highlight(filename)}")
except TimeoutError: except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?") raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -3,7 +3,6 @@ from clypi import Command, arg
from typing_extensions import override from typing_extensions import override
from .errors import SimpleRecorderError from .errors import SimpleRecorderError
from .styler import highlight
class Stop(Command): class Stop(Command):
@@ -24,6 +23,6 @@ class Stop(Command):
raise SimpleRecorderError("Recording is not active.") raise SimpleRecorderError("Recording is not active.")
client.stop_record() client.stop_record()
print(highlight("Recording stopped successfully.")) print("Recording stopped successfully.")
except TimeoutError: except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?") raise SimpleRecorderError("Failed to connect to OBS. Is it running?")