Compare commits

..

5 Commits

Author SHA1 Message Date
6035e09e43 upd screenshot 2025-06-29 16:56:49 +01:00
0fe22debb6 handle TimeoutError
patch bump
2025-06-29 16:47:24 +01:00
d8d7fce5cc reorganise readme 2025-06-29 16:42:08 +01:00
ca7604c279 update --help in README 2025-06-29 16:37:26 +01:00
2eb48556ed implement get/set recording directory
closes #3

minor bump
2025-06-29 16:35:00 +01:00
6 changed files with 126 additions and 47 deletions

View File

@ -49,6 +49,38 @@ OBS_THEME=Reds
## Use ## Use
### CLI
To launch the CLI pass any subcommand, for example:
```console
simple-recorder start "File Name"
simple-recorder stop
```
#### Commands:
```shell
Usage: simple-recorder [OPTIONS] COMMAND
┏━ Subcommands ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ start Start recording ┃
┃ stop Stop recording ┃
┃ pause Pause recording ┃
┃ resume Resume recording ┃
┃ directory Get or set the recording directory ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Options ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ --host <HOST> OBS WebSocket host ┃
┃ --port <PORT> OBS WebSocket port ┃
┃ --password <PASSWORD> OBS WebSocket password ┃
┃ --theme <THEME> GUI theme (Light Purple, Neutral Blue, Reds, Sandy Beach, ┃
┃ Kayak, Light Blue 2) ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
```
### GUI ### GUI
To launch the GUI run the root command without any subcommands: To launch the GUI run the root command without any subcommands:
@ -69,36 +101,4 @@ You can change the colour theme with the --theme option:
simple-recorder --theme="Light Purple" simple-recorder --theme="Light Purple"
``` ```
### CLI
```shell
Usage: simple-recorder [OPTIONS] COMMAND
┏━ Subcommands ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ start Start recording ┃
┃ stop Stop recording ┃
┃ pause Pause recording ┃
┃ resume Resume recording ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Options ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ --host <HOST> OBS WebSocket host ┃
┃ --port <PORT> OBS WebSocket port ┃
┃ --password <PASSWORD> OBS WebSocket password ┃
┃ --theme <THEME> GUI theme (Light Purple, Neutral Blue, Reds, Sandy Beach, ┃
┃ Kayak, Light Blue 2) ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
```
To launch the CLI pass any subcommand (start/stop etc...), for example:
```console
simple-recorder start "File Name"
simple-recorder stop
```
- If no filename is passed to start then you will be prompted for one.
- A default_name will be used if none is supplied to the prompt.
[obs-studio]: https://obsproject.com/ [obs-studio]: https://obsproject.com/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 78 KiB

View File

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

@ -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 .directory import Directory
from .errors import SimpleRecorderError from .errors import SimpleRecorderError
from .gui import SimpleRecorderWindow from .gui import SimpleRecorderWindow
from .pause import Pause from .pause import Pause
@ -37,7 +38,7 @@ def theme_parser(value: str) -> str:
class SimpleRecorder(Command): class SimpleRecorder(Command):
subcommand: Start | Stop | Pause | Resume | None = None subcommand: Start | Stop | Pause | Resume | Directory | 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(

View File

@ -0,0 +1,36 @@
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 Directory(Command):
"""Get or set the recording directory."""
directory: Positional[str] = arg(
default=None,
help="Directory to set for recordings. If not provided, the current directory will be displayed.",
)
host: str = arg(inherited=True)
port: int = arg(inherited=True)
password: str = arg(inherited=True)
@override
async def run(self):
try:
with obsws.ReqClient(
host=self.host, port=self.port, password=self.password
) as client:
if self.directory:
client.set_record_directory(self.directory)
print(f"Recording directory set to: {highlight(self.directory)}")
else:
resp = client.get_record_directory()
print(
f"Current recording directory: {highlight(resp.record_directory)}"
)
return resp.record_directory
except TimeoutError:
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

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 .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
@ -26,6 +27,8 @@ class SimpleRecorderWindow(fsg.Window):
) as client: ) as client:
resp = client.get_version() resp = client.get_version()
status_message = f"Connected to OBS {resp.obs_version}" status_message = f"Connected to OBS {resp.obs_version}"
resp = client.get_record_directory()
current_directory = resp.record_directory
except (ConnectionRefusedError, TimeoutError): except (ConnectionRefusedError, TimeoutError):
status_message = "Failed to connect to OBS. Is it running?" status_message = "Failed to connect to OBS. Is it running?"
@ -59,7 +62,7 @@ class SimpleRecorderWindow(fsg.Window):
[ [
fsg.Text( fsg.Text(
f"Status: {status_message}", f"Status: {status_message}",
key="-OUTPUT-", key="-OUTPUT-RECORDER-",
text_color="white" text_color="white"
if status_message.startswith("Connected") if status_message.startswith("Connected")
else "red", else "red",
@ -70,7 +73,12 @@ class SimpleRecorderWindow(fsg.Window):
settings_layout = [ settings_layout = [
[fsg.Text("Enter the filepath for the recording:")], [fsg.Text("Enter the filepath for the recording:")],
[fsg.InputText("", key="-FILEPATH-", size=(45, 1))], [fsg.InputText(current_directory, key="-FILEPATH-", size=(45, 1))],
[
fsg.Button("Get Current", key="-GET-CURRENT-", size=(10, 1)),
fsg.Button("Update", key="-UPDATE-", size=(10, 1)),
],
[fsg.Text("", key="-OUTPUT-SETTINGS-", text_color="white")],
] ]
settings_tab = fsg.Tab("Settings", settings_layout) settings_tab = fsg.Tab("Settings", settings_layout)
@ -110,11 +118,11 @@ class SimpleRecorderWindow(fsg.Window):
port=self.port, port=self.port,
password=self.password, password=self.password,
).run() ).run()
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
"Recording started successfully", text_color="green" "Recording started successfully", text_color="green"
) )
except SimpleRecorderError as e: except SimpleRecorderError as e:
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red" f"Error: {e.raw_message}", text_color="red"
) )
@ -123,11 +131,11 @@ class SimpleRecorderWindow(fsg.Window):
await Stop( await Stop(
host=self.host, port=self.port, password=self.password host=self.host, port=self.port, password=self.password
).run() ).run()
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
"Recording stopped successfully", text_color="green" "Recording stopped successfully", text_color="green"
) )
except SimpleRecorderError as e: except SimpleRecorderError as e:
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red" f"Error: {e.raw_message}", text_color="red"
) )
@ -136,11 +144,11 @@ class SimpleRecorderWindow(fsg.Window):
await Pause( await Pause(
host=self.host, port=self.port, password=self.password host=self.host, port=self.port, password=self.password
).run() ).run()
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
"Recording paused successfully", text_color="green" "Recording paused successfully", text_color="green"
) )
except SimpleRecorderError as e: except SimpleRecorderError as e:
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red" f"Error: {e.raw_message}", text_color="red"
) )
@ -149,21 +157,21 @@ class SimpleRecorderWindow(fsg.Window):
await Resume( await Resume(
host=self.host, port=self.port, password=self.password host=self.host, port=self.port, password=self.password
).run() ).run()
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
"Recording resumed successfully", text_color="green" "Recording resumed successfully", text_color="green"
) )
except SimpleRecorderError as e: except SimpleRecorderError as e:
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red" f"Error: {e.raw_message}", text_color="red"
) )
case ["Add Chapter", "FOCUS" | "LEAVE" as focus_event]: case ["Add Chapter", "FOCUS" | "LEAVE" as focus_event]:
if focus_event == "FOCUS": if focus_event == "FOCUS":
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
"Right-click to set a chapter name", text_color="white" "Right-click to set a chapter name", text_color="white"
) )
else: else:
self["-OUTPUT-"].update("", text_color="white") self["-OUTPUT-RECORDER-"].update("", text_color="white")
case ["Add Chapter", "RIGHT_CLICK"]: case ["Add Chapter", "RIGHT_CLICK"]:
_ = fsg.popup_get_text( _ = fsg.popup_get_text(
@ -173,10 +181,44 @@ class SimpleRecorderWindow(fsg.Window):
) )
case ["Split Recording" | "Add Chapter"]: case ["Split Recording" | "Add Chapter"]:
self["-OUTPUT-"].update( self["-OUTPUT-RECORDER-"].update(
"This feature is not implemented yet", text_color="orange" "This feature is not implemented yet", text_color="orange"
) )
case ["-GET-CURRENT-"]:
try:
current_directory = await Directory(
host=self.host, port=self.port, password=self.password
).run()
self["-FILEPATH-"].update(current_directory)
except SimpleRecorderError as e:
self["-OUTPUT-SETTINGS-"].update(
f"Error: {e.raw_message}", text_color="red"
)
case ["-UPDATE-"]:
filepath = values["-FILEPATH-"]
if not filepath:
self["-OUTPUT-SETTINGS-"].update(
"Filepath cannot be empty", text_color="red"
)
else:
try:
await Directory(
directory=filepath,
host=self.host,
port=self.port,
password=self.password,
).run()
self["-OUTPUT-SETTINGS-"].update(
"Recording directory updated successfully.",
text_color="green",
)
except SimpleRecorderError as e:
self["-OUTPUT-SETTINGS-"].update(
f"Error: {e.raw_message}", text_color="red"
)
case _: case _:
self.logger.debug(f"Unhandled event: {e}") self.logger.debug(f"Unhandled event: {e}")