1 Commits

5 changed files with 82 additions and 46 deletions

View File

@@ -30,7 +30,7 @@ pipx install simple-recorder
*with pyz* *with pyz*
An executable pyz has been included in [Releases](https://github.com/onyx-and-iris/simple-recorder/releases) which you can run in Windows. Follow the steps in this [Setting up Windows for Zipapps](https://jhermann.github.io/blog/python/deployment/2020/02/29/python_zippapps_on_windows.html#Setting-Up-Windows-10-for-Zipapps) guide. An executable pyz has been included in [Release](https://github.com/onyx-and-iris/simple-recorder/releases) which you can run in Windows. Follow the steps in this [Setting up Windows for Zipapps](https://jhermann.github.io/blog/python/deployment/2020/02/29/python_zippapps_on_windows.html#Setting-Up-Windows-10-for-Zipapps) guide.
## Configuration ## Configuration
@@ -65,32 +65,33 @@ Just enter the filename and click *Start Recording*.
#### Themes #### Themes
However, passing flags is fine, for example to set the theme: Passing flags is fine, however, for example to set the theme:
```console ```console
simple-recorder --theme="Light Purple" simple-recorder --theme="Light Purple"
``` ```
Available themes: Light Purple, Neutral Blue, Reds, Sandy Beach, Kayak, Light Blue 2, Dark Teal1
### CLI ### CLI
```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 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ 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> OBS WebSocket theme
┃ Kayak, Light Blue 2) ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
``` ```
To launch the CLI pass any subcommand (start/stop etc...), for example: For example:
```console ```console
simple-recorder start "File Name" simple-recorder start "File Name"

View File

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

@@ -5,6 +5,7 @@ from typing_extensions import override
from .errors import SimpleRecorderError from .errors import SimpleRecorderError
from .gui import SimpleRecorderWindow from .gui import SimpleRecorderWindow
from .split import Split
from .start import Start from .start import Start
from .stop import Stop from .stop import Stop
@@ -15,18 +16,18 @@ config = ClypiConfig(
) )
configure(config) configure(config)
themes = [
def theme_parser(value: str) -> str:
"""Parse the theme argument."""
themes = [
"Light Purple", "Light Purple",
"Neutral Blue", "Neutral Blue",
"Reds", "Reds",
"Sandy Beach", "Sandy Beach",
"Kayak", "Kayak",
"Light Blue 2", "Light Blue 2",
] "Dark Teal1",
]
def theme_parser(value: str) -> str:
"""Parse the theme argument."""
if value not in themes: if value not in themes:
raise ClypiException( raise ClypiException(
f"Invalid theme: {value}. Available themes: {', '.join(themes)}" f"Invalid theme: {value}. Available themes: {', '.join(themes)}"
@@ -35,30 +36,19 @@ def theme_parser(value: str) -> str:
class SimpleRecorder(Command): class SimpleRecorder(Command):
subcommand: Start | Stop | None = None subcommand: Start | Stop | Split | 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(
default=None, env="OBS_PASSWORD", help="OBS WebSocket password" default=None, env="OBS_PASSWORD", help="OBS WebSocket password"
) )
theme: str = arg( theme: str = arg(
default="Reds", default="Reds", parser=theme_parser, env="OBS_THEME", help="OBS WebSocket theme"
parser=theme_parser,
env="OBS_THEME",
help=f"GUI theme ({', '.join(themes)})",
)
debug: bool = arg(
default=False,
env="DEBUG",
help="Enable debug logging",
) )
@override @override
async def run(self): async def run(self):
"""Run the Simple Recorder GUI.""" """Run the Simple Recorder GUI."""
if self.debug:
logging.basicConfig(level=logging.DEBUG)
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

@@ -1,8 +1,9 @@
import logging import logging
import FreeSimpleGUI as fsg import FreeSimpleGUI as fsg
from clypi import ClypiException
from .errors import SimpleRecorderError from .split import Split
from .start import Start from .start import Start
from .stop import Stop from .stop import Stop
@@ -20,23 +21,27 @@ class SimpleRecorderWindow(fsg.Window):
layout = [ layout = [
[fsg.Text("Enter recording filename:")], [fsg.Text("Enter recording filename:")],
[fsg.InputText("", key="-FILENAME-")], [fsg.InputText("", key="-FILENAME-")],
[fsg.Button("Start Recording"), fsg.Button("Stop Recording")], [
fsg.Button("Start Recording"),
fsg.Button("Stop Recording"),
fsg.Button("Split Recording"),
],
[fsg.Text("Status: Not started", key="-OUTPUT-")], [fsg.Text("Status: Not started", key="-OUTPUT-")],
] ]
super().__init__("Simple Recorder", layout, finalize=True) super().__init__("Simple Recorder", layout, finalize=True)
self["-FILENAME-"].bind("<Return>", " || RETURN") self["-FILENAME-"].bind("<Return>", " || RETURN")
self["Start Recording"].bind("<Return>", " || RETURN") self["Start Recording"].bind("<Return>", " || RETURN")
self["Stop Recording"].bind("<Return>", " || RETURN") self["Stop Recording"].bind("<Return>", " || RETURN")
self["Split Recording"].bind("<Return>", " || RETURN")
async def run(self): async def run(self):
while True: while True:
event, values = self.read() event, values = self.read()
self.logger.debug(f"Event: {event}, Values: {values}")
if event == fsg.WIN_CLOSED: if event == fsg.WIN_CLOSED:
break break
match e := event.split(" || "): match event.split(" || "):
case ["Start Recording"] | ["Start Recording" | "-FILENAME-", "RETURN"]: case ["Start Recording", "RETURN" | None] | ["-FILENAME-", "RETURN"]:
try: try:
await Start( await Start(
filename=values["-FILENAME-"], filename=values["-FILENAME-"],
@@ -47,12 +52,12 @@ class SimpleRecorderWindow(fsg.Window):
self["-OUTPUT-"].update( self["-OUTPUT-"].update(
"Recording started successfully", text_color="green" "Recording started successfully", text_color="green"
) )
except SimpleRecorderError as e: except ClypiException as e:
self["-OUTPUT-"].update( self["-OUTPUT-"].update(
f"Error: {e.raw_message}", text_color="red" f"Error: {e.raw_message}", text_color="red"
) )
case ["Stop Recording"] | ["Stop Recording", "RETURN"]: case ["Stop Recording", "RETURN" | None]:
try: try:
await Stop( await Stop(
host=self.host, port=self.port, password=self.password host=self.host, port=self.port, password=self.password
@@ -60,12 +65,25 @@ class SimpleRecorderWindow(fsg.Window):
self["-OUTPUT-"].update( self["-OUTPUT-"].update(
"Recording stopped successfully", text_color="green" "Recording stopped successfully", text_color="green"
) )
except SimpleRecorderError as e: except ClypiException as e:
self["-OUTPUT-"].update(
f"Error: {e.raw_message}", text_color="red"
)
case ["Split Recording", "RETURN" | None]:
try:
await Split(
host=self.host, port=self.port, password=self.password
).run()
self["-OUTPUT-"].update(
"Recording split successfully", text_color="green"
)
except ClypiException as e:
self["-OUTPUT-"].update( self["-OUTPUT-"].update(
f"Error: {e.raw_message}", text_color="red" f"Error: {e.raw_message}", text_color="red"
) )
case _: case _:
self.logger.warning(f"Unhandled event: {e}") self.logger.warning(f"Unhandled event: {event}")
self.close() self.close()

View File

@@ -0,0 +1,27 @@
import obsws_python as obsws
from clypi import Command, arg
from .errors import SimpleRecorderError
class Split(Command):
"""Split recording."""
host: str = arg(inherited=True)
port: int = arg(inherited=True)
password: str = arg(inherited=True)
async def run(self):
with obsws.ReqClient(
host=self.host, port=self.port, password=self.password
) as client:
resp = client.get_record_status()
if not resp.output_active:
raise SimpleRecorderError("Recording is not active.")
if resp.output_paused:
raise SimpleRecorderError(
"Recording is paused. Please resume before splitting."
)
client.split_record_file()
print("Recording split successfully.")