3 Commits

Author SHA1 Message Date
5aae021aed minor bump 2025-07-01 16:08:57 +01:00
a2e1c5280b update --help in README 2025-07-01 16:08:35 +01:00
d5b7991584 implement record chapter
- command added to CLI
- GUI button mouse/keyboard events added

pre_run_hook added to set logging level

closes #2
2025-07-01 16:08:25 +01:00
6 changed files with 99 additions and 31 deletions

View File

@@ -75,21 +75,23 @@ simple-recorder stop
```shell ```shell
Usage: simple-recorder [OPTIONS] COMMAND Usage: simple-recorder [OPTIONS] COMMAND
┏━ Subcommands ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┏━ Subcommands ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ start Start recording ┃ ┃ start Start recording ┃
┃ stop Stop recording ┃ ┃ stop Stop recording ┃
┃ pause Pause recording ┃ ┃ pause Pause recording ┃
┃ resume Resume recording ┃ ┃ resume Resume recording ┃
┃ split Split the current recording into a new file ┃
┃ chapter Create a chapter in the current recording ┃
┃ directory Get or set the recording directory ┃ ┃ directory Get or set the recording directory ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┏━ Options ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┏━ Options ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ --host <HOST> OBS WebSocket host ┃ ┃ --host <HOST> OBS WebSocket host ┃
┃ --port <PORT> OBS WebSocket port ┃ ┃ --port <PORT> OBS WebSocket port ┃
┃ --password <PASSWORD> OBS WebSocket password ┃ ┃ --password <PASSWORD> OBS WebSocket password ┃
┃ --theme <THEME> GUI theme (Light Purple, Neutral Blue, Reds, Sandy Beach, ┃ ┃ --theme <THEME> GUI theme (Light Purple, Neutral Blue, Reds, Sandy Beach, ┃
┃ Kayak, Light Blue 2) ┃ Kayak, Light Blue 2)
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
``` ```
### GUI ### GUI

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "simple-recorder" name = "simple-recorder"
version = "0.4.0" version = "0.5.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 = [

View File

@@ -0,0 +1,48 @@
import obsws_python as obsws
from clypi import Command, Positional, arg
from typing_extensions import override
from .errors import SimpleRecorderError
from .styler import highlight
class Chapter(Command):
"""Create a chapter in the current recording."""
chapter_name: Positional[str] = arg(
help="Name of the chapter to create.",
prompt="Enter the name for the chapter.",
default="unnamed",
)
host: str = arg(inherited=True)
port: int = arg(inherited=True)
password: str = arg(inherited=True)
@override
async def run(self):
"""Run the chapter 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 create a chapter."
)
# Allow OBS to set unnamed chapters (it will increment the name)
if self.chapter_name == "unnamed":
client.create_record_chapter()
else:
client.create_record_chapter(self.chapter_name)
print(f"Chapter {highlight(self.chapter_name)} created 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 create chapter, please check your OBS settings."
)
else:
raise SimpleRecorderError(f"Error: {e}")

View File

@@ -3,6 +3,7 @@ import logging
from clypi import ClypiConfig, ClypiException, Command, arg, configure from clypi import ClypiConfig, ClypiException, Command, arg, configure
from typing_extensions import override from typing_extensions import override
from .chapter import Chapter
from .directory import Directory from .directory import Directory
from .errors import SimpleRecorderError from .errors import SimpleRecorderError
from .gui import SimpleRecorderWindow from .gui import SimpleRecorderWindow
@@ -12,8 +13,6 @@ from .split import Split
from .start import Start from .start import Start
from .stop import Stop from .stop import Stop
logger = logging.getLogger(__name__)
config = ClypiConfig( config = ClypiConfig(
nice_errors=(SimpleRecorderError,), nice_errors=(SimpleRecorderError,),
) )
@@ -38,7 +37,7 @@ def theme_parser(value: str) -> str:
return value return value
SUBCOMMANDS = Start | Stop | Pause | Resume | Split | Directory SUBCOMMANDS = Start | Stop | Pause | Resume | Split | Chapter | Directory
class SimpleRecorder(Command): class SimpleRecorder(Command):
@@ -61,11 +60,15 @@ class SimpleRecorder(Command):
) )
@override @override
async def run(self): async def pre_run_hook(self):
"""Run the Simple Recorder GUI."""
if self.debug: if self.debug:
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.disable())
@override
async def run(self):
"""Run the Simple Recorder GUI."""
window = SimpleRecorderWindow(self.host, self.port, self.password, self.theme) window = SimpleRecorderWindow(self.host, self.port, self.password, self.theme)
await window.run() await window.run()

View File

@@ -3,6 +3,7 @@ import logging
import FreeSimpleGUI as fsg import FreeSimpleGUI as fsg
import obsws_python as obsws import obsws_python as obsws
from .chapter import Chapter
from .directory import Directory from .directory import Directory
from .errors import SimpleRecorderError from .errors import SimpleRecorderError
from .pause import Pause from .pause import Pause
@@ -96,8 +97,9 @@ class SimpleRecorderWindow(fsg.Window):
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["Split Recording"].bind("<Return>", " || RETURN")
self["Add Chapter"].bind("<Return>", " || RETURN")
self["Add Chapter"].bind("<Shift-Return>", " || SHIFT-RETURN")
self["-FILENAME-"].bind("<KeyPress>", " || KEYPRESS")
self["-FILENAME-"].update(select=True) self["-FILENAME-"].update(select=True)
self["Add Chapter"].bind("<FocusIn>", " || FOCUS") self["Add Chapter"].bind("<FocusIn>", " || FOCUS")
self["Add Chapter"].bind("<Enter>", " || FOCUS") self["Add Chapter"].bind("<Enter>", " || FOCUS")
@@ -109,6 +111,8 @@ class SimpleRecorderWindow(fsg.Window):
self["-UPDATE-"].bind("<Return>", " || RETURN") self["-UPDATE-"].bind("<Return>", " || RETURN")
async def run(self): async def run(self):
chapter_name = "unnamed"
while True: while True:
event, values = self.read() event, values = self.read()
self.logger.debug(f"Event: {event}, Values: {values}") self.logger.debug(f"Event: {event}, Values: {values}")
@@ -192,16 +196,31 @@ class SimpleRecorderWindow(fsg.Window):
f"Error: {e.raw_message}", text_color="red" f"Error: {e.raw_message}", text_color="red"
) )
case ["Add Chapter", "RIGHT_CLICK"]: case ["Add Chapter", "RIGHT_CLICK" | "SHIFT-RETURN"]:
_ = fsg.popup_get_text( chapter_name = fsg.popup_get_text(
"Enter chapter name:", "Enter chapter name:",
"Add Chapter", "Add Chapter",
default_text="unnamed", default_text="unnamed",
) )
case ["Add Chapter"]: case ["Add Chapter"] | ["Add Chapter", "RETURN"]:
try:
await Chapter(
chapter_name=chapter_name,
host=self.host,
port=self.port,
password=self.password,
).run()
self["-OUTPUT-RECORDER-"].update( self["-OUTPUT-RECORDER-"].update(
"This feature is not implemented yet", text_color="orange" f"Chapter {chapter_name if chapter_name else 'unnamed'} added successfully",
text_color="green",
)
except SimpleRecorderError:
fsg.popup_error(
"Unable to create chapter, please check your OBS settings.\n"
"Note, currently only Hybrid MP4 is supported for chapters.",
title="Chapter Error",
keep_on_top=True,
) )
case ["-GET-CURRENT-"] | ["-GET-CURRENT-", "RETURN"]: case ["-GET-CURRENT-"] | ["-GET-CURRENT-", "RETURN"]:

View File

@@ -1,13 +1,9 @@
import logging
import obsws_python as obsws import obsws_python as obsws
from clypi import Command, arg from clypi import Command, arg
from typing_extensions import override from typing_extensions import override
from .errors import SimpleRecorderError from .errors import SimpleRecorderError
logging.basicConfig(level=logging.disable())
class Split(Command): class Split(Command):
"""Split the current recording into a new file.""" """Split the current recording into a new file."""