Compare commits

..

No commits in common. "3c77be2ff9cc84f422ea5c3693b4745d9f3e8954" and "0814678278f2770d6548100c197baf4aa3913901" have entirely different histories.

9 changed files with 38 additions and 218 deletions

View File

@ -3,7 +3,9 @@
[![pdm-managed](https://img.shields.io/endpoint?url=https%3A%2F%2Fcdn.jsdelivr.net%2Fgh%2Fpdm-project%2F.github%2Fbadge.json)](https://pdm-project.org)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
A simple OBS recorder app. Run it as a CLI or a GUI.
A single purpose application for naming file recording in OBS.
Run it as a CLI or a GUI.
---
@ -59,11 +61,11 @@ simple-recorder
![simple-recorder](./img/simple-recorder.png)
Just enter the filename and click *Start*.
Just enter the filename and click *Start Recording*.
#### Themes
You can change the colour theme with the --theme option:
However, passing flags is fine, for example to set the theme:
```console
simple-recorder --theme="Light Purple"
@ -75,10 +77,8 @@ simple-recorder --theme="Light Purple"
Usage: simple-recorder [OPTIONS] COMMAND
┏━ Subcommands ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ start Start recording ┃
┃ stop Stop recording ┃
┃ pause Pause recording ┃
┃ resume Resume recording ┃
┃ start Start recording ┃
┃ stop Stop recording ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Options ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -1,6 +1,6 @@
[project]
name = "simple-recorder"
version = "0.2.0"
version = "0.1.7"
description = "A simple OBS recorder"
authors = [{ name = "onyx-and-iris", email = "code@onyxandiris.online" }]
dependencies = [

View File

@ -5,8 +5,6 @@ from typing_extensions import override
from .errors import SimpleRecorderError
from .gui import SimpleRecorderWindow
from .pause import Pause
from .resume import Resume
from .start import Start
from .stop import Stop
@ -37,7 +35,7 @@ def theme_parser(value: str) -> str:
class SimpleRecorder(Command):
subcommand: Start | Stop | Pause | Resume | None = None
subcommand: Start | Stop | None = None
host: str = arg(default="localhost", env="OBS_HOST", help="OBS WebSocket host")
port: int = arg(default=4455, env="OBS_PORT", help="OBS WebSocket port")
password: str | None = arg(

View File

@ -1,11 +1,8 @@
import logging
import FreeSimpleGUI as fsg
import obsws_python as obsws
from .errors import SimpleRecorderError
from .pause import Pause
from .resume import Resume
from .start import Start
from .stop import Stop
@ -20,79 +17,16 @@ class SimpleRecorderWindow(fsg.Window):
self.password = password
fsg.theme(theme)
try:
with obsws.ReqClient(
host=self.host, port=self.port, password=self.password, timeout=3
) as client:
resp = client.get_version()
status_message = f"Connected to OBS {resp.obs_version}"
except (ConnectionRefusedError, TimeoutError):
status_message = "Failed to connect to OBS. Is it running?"
recorder_layout = [
[fsg.Text("Enter recording filename:", key="-PROMPT-")],
[fsg.InputText("default_name", key="-FILENAME-", focus=True)],
[
fsg.Button("Start", key="Start Recording", size=(20, 1)),
fsg.Button("Stop", key="Stop Recording", size=(20, 1)),
],
[
fsg.Button("Pause", key="Pause Recording", size=(20, 1)),
fsg.Button("Resume", key="Resume Recording", size=(20, 1)),
],
[
fsg.Button("Split", key="Split Recording", size=(20, 1)),
fsg.Button("Add Chapter", key="Add Chapter", size=(20, 1)),
],
layout = [
[fsg.Text("Enter recording filename:")],
[fsg.InputText("", key="-FILENAME-")],
[fsg.Button("Start Recording"), fsg.Button("Stop Recording")],
[fsg.Text("Status: Not started", key="-OUTPUT-")],
]
frame = fsg.Frame(
"",
recorder_layout,
relief=fsg.RELIEF_SUNKEN,
)
recorder_tab = fsg.Tab(
"Recorder",
[
[frame],
[
fsg.Text(
f"Status: {status_message}",
key="-OUTPUT-",
text_color="white"
if status_message.startswith("Connected")
else "red",
)
],
],
)
settings_layout = [
[fsg.Text("Enter the filepath for the recording:")],
[fsg.InputText("", key="-FILEPATH-", size=(45, 1))],
]
settings_tab = fsg.Tab("Settings", settings_layout)
mainframe = [
[fsg.TabGroup([[recorder_tab, settings_tab]])],
]
super().__init__("Simple Recorder", mainframe, finalize=True)
super().__init__("Simple Recorder", layout, finalize=True)
self["-FILENAME-"].bind("<Return>", " || RETURN")
self["Start Recording"].bind("<Return>", " || RETURN")
self["Stop Recording"].bind("<Return>", " || RETURN")
self["Pause Recording"].bind("<Return>", " || RETURN")
self["Resume Recording"].bind("<Return>", " || RETURN")
self["-FILENAME-"].bind("<KeyPress>", " || KEYPRESS")
self["-FILENAME-"].update(select=True)
self["Add Chapter"].bind("<FocusIn>", " || FOCUS")
self["Add Chapter"].bind("<Enter>", " || FOCUS")
self["Add Chapter"].bind("<FocusOut>", " || LEAVE")
self["Add Chapter"].bind("<Leave>", " || LEAVE")
self["Add Chapter"].bind("<Button-3>", " || RIGHT_CLICK")
async def run(self):
while True:
@ -131,53 +65,7 @@ class SimpleRecorderWindow(fsg.Window):
f"Error: {e.raw_message}", text_color="red"
)
case ["Pause Recording"] | ["Pause Recording", "RETURN"]:
try:
await Pause(
host=self.host, port=self.port, password=self.password
).run()
self["-OUTPUT-"].update(
"Recording paused successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-"].update(
f"Error: {e.raw_message}", text_color="red"
)
case ["Resume Recording"] | ["Resume Recording", "RETURN"]:
try:
await Resume(
host=self.host, port=self.port, password=self.password
).run()
self["-OUTPUT-"].update(
"Recording resumed successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-"].update(
f"Error: {e.raw_message}", text_color="red"
)
case ["Add Chapter", "FOCUS" | "LEAVE" as focus_event]:
if focus_event == "FOCUS":
self["-OUTPUT-"].update(
"Right-click to set a chapter name", text_color="white"
)
else:
self["-OUTPUT-"].update("", text_color="white")
case ["Add Chapter", "RIGHT_CLICK"]:
_ = fsg.popup_get_text(
"Enter chapter name:",
"Add Chapter",
default_text="unnamed",
)
case ["Split Recording" | "Add Chapter"]:
self["-OUTPUT-"].update(
"This feature is not implemented yet", text_color="orange"
)
case _:
self.logger.debug(f"Unhandled event: {e}")
self.logger.warning(f"Unhandled event: {e}")
self.close()

View File

@ -1,30 +0,0 @@
import obsws_python as obsws
from clypi import Command, arg
from typing_extensions import override
from .errors import SimpleRecorderError
class Pause(Command):
"""Pause recording."""
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, timeout=3
) as client:
resp = client.get_record_status()
if not resp.output_active:
raise SimpleRecorderError("No active recording to pause.")
if resp.output_paused:
raise SimpleRecorderError("Recording is already paused.")
client.pause_record()
print("Recording paused successfully.")
except TimeoutError:
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@ -1,30 +0,0 @@
import obsws_python as obsws
from clypi import Command, arg
from typing_extensions import override
from .errors import SimpleRecorderError
class Resume(Command):
"""Resume recording."""
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, timeout=3
) as client:
resp = client.get_record_status()
if not resp.output_active:
raise SimpleRecorderError("No active recording to resume.")
if not resp.output_paused:
raise SimpleRecorderError("Recording is not paused.")
client.resume_record()
print("Recording resumed successfully.")
except TimeoutError:
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@ -29,21 +29,18 @@ class Start(Command):
if not self.filename:
raise SimpleRecorderError("Recording name cannot be empty.")
try:
with obsws.ReqClient(
host=self.host, port=self.port, password=self.password, timeout=3
) as client:
resp = client.get_record_status()
if resp.output_active:
raise SimpleRecorderError("Recording is already active.")
with obsws.ReqClient(
host=self.host, port=self.port, password=self.password
) as client:
resp = client.get_record_status()
if resp.output_active:
raise SimpleRecorderError("Recording is already active.")
filename = f"{self.filename} {self.get_timestamp()}"
client.set_profile_parameter(
"Output",
"FilenameFormatting",
filename,
)
client.start_record()
print(f"Recording started with filename: {highlight(filename)}")
except TimeoutError:
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")
filename = f"{self.filename} {self.get_timestamp()}"
client.set_profile_parameter(
"Output",
"FilenameFormatting",
filename,
)
client.start_record()
print(f"Recording started with filename: {highlight(filename)}")

View File

@ -15,15 +15,12 @@ class Stop(Command):
@override
async def run(self):
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("Recording is not active.")
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.")
client.stop_record()
print(highlight("Recording stopped successfully."))
except TimeoutError:
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")
client.stop_record()
print(highlight("Recording stopped successfully."))