1 Commits

16 changed files with 200 additions and 516 deletions

62
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,62 @@
name: Build PYZ
on:
push:
tags:
- 'v*.*.*'
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up PDM
uses: pdm-project/setup-pdm@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pdm sync -d -G build
- name: Build addon
run: pdm run compile
- name: Upload build artifacts
if: success()
uses: actions/upload-artifact@v4
with:
name: pyz_build
path: ./bin/simple-recorder.pyz
upload_release:
runs-on: ubuntu-latest
if: ${{ startsWith(github.ref, 'refs/tags/') }}
needs: build
steps:
- name: Download releases files
uses: actions/download-artifact@v4.1.7
with:
name: pyz_build
- name: Display structure of downloaded files
run: tree
- name: Release
uses: softprops/action-gh-release@v1
with:
files: simple-recorder.pyz
fail_on_unmatched_files: true
prerelease: ${{ contains(github.ref, '-') }}

View File

@@ -1,26 +0,0 @@
name: Publish to PyPI
on:
release:
types: [published]
push:
tags:
- 'v*.*.*'
jobs:
pypi-publish:
name: upload release to PyPI
runs-on: ubuntu-latest
environment: pypi
permissions:
# This permission is needed for private repositories.
contents: read
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
steps:
- uses: actions/checkout@v4
- uses: pdm-project/setup-pdm@v4
- name: Publish package distributions to PyPI
run: pdm publish

View File

@@ -1,62 +1,26 @@
name: Release PYZ name: Release
on: on:
release:
types: [published]
push: push:
tags: tags:
- 'v*.*.*' - 'v*.*.*'
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: write
jobs: jobs:
build: pypi-publish:
name: upload release to PyPI
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: pypi
permissions:
# This permission is needed for private repositories.
contents: read
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
steps: steps:
- name: Checkout code - uses: actions/checkout@v4
uses: actions/checkout@v4
- name: Set up PDM - uses: pdm-project/setup-pdm@v4
uses: pdm-project/setup-pdm@v4
with:
python-version: '3.11'
- name: Install dependencies - name: Publish package distributions to PyPI
run: | run: pdm publish
pdm sync -d -G build
- name: Build addon
run: pdm run compile
- name: Upload build artifacts
if: success()
uses: actions/upload-artifact@v4
with:
name: pyz_build
path: ./bin/simple-recorder.pyz
upload_release:
runs-on: ubuntu-latest
if: ${{ startsWith(github.ref, 'refs/tags/') }}
needs: build
steps:
- name: Download releases files
uses: actions/download-artifact@v4.1.7
with:
name: pyz_build
- name: Display structure of downloaded files
run: tree
- name: Release
uses: softprops/action-gh-release@v1
with:
files: simple-recorder.pyz
fail_on_unmatched_files: true
prerelease: ${{ contains(github.ref, '-') }}

View File

@@ -1,19 +0,0 @@
name: Ruff
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/ruff-action@v3
with:
args: 'format --check --diff'

100
README.md
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) [![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) [![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.
--- ---
@@ -28,18 +30,7 @@ pipx install simple-recorder
*with pyz* *with pyz*
- Download the pyz file in [Releases](https://github.com/onyx-and-iris/simple-recorder/releases) 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.
- Optional step: for automatic discovery of the pyz file follow this guide on [Setting Up Windows for Zippapps](https://jhermann.github.io/blog/python/deployment/2020/02/29/python_zippapps_on_windows.html#Setting-Up-Windows-10-for-Zipapps)
Finally run the pyz with python (CLI)/pythonw (GUI):
```console
python simple-recorder.pyz <subcommand>
pythonw simple-recorder.pyz
```
note, the pyz extension won't be required if you followed the optional step and made it discoverable.
## Configuration ## Configuration
@@ -60,9 +51,47 @@ OBS_THEME=Reds
## Use ## Use
### GUI
To launch the GUI run the root command without any subcommands:
```console
simple-recorder
```
![simple-recorder](./img/simple-recorder.png)
Just enter the filename and click *Start Recording*.
#### Themes
Passing flags is fine, however, for example to set the theme:
```console
simple-recorder --theme="Light Purple"
```
Available themes: Light Purple, Neutral Blue, Reds, Sandy Beach, Kayak, Light Blue 2, Dark Teal1
### CLI ### CLI
To launch the CLI: ```shell
Usage: simple-recorder [OPTIONS] COMMAND
┏━ Subcommands ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ start Start recording ┃
┃ stop Stop recording ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Options ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ --host <HOST> OBS WebSocket host ┃
┃ --port <PORT> OBS WebSocket port ┃
┃ --password <PASSWORD> OBS WebSocket password ┃
┃ --theme <THEME> OBS WebSocket theme ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
```
For example:
```console ```console
simple-recorder start "File Name" simple-recorder start "File Name"
@@ -70,46 +99,7 @@ simple-recorder start "File Name"
simple-recorder stop simple-recorder stop
``` ```
#### Commands: - 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.
```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
To launch the GUI:
```console
simple-recorder-gui
```
![simple-recorder](./img/simple-recorder.png)
Just enter the filename and click *Start*.
#### Themes
You can change the colour theme with the --theme option:
```console
simple-recorder-gui --theme="Light Purple"
```
[obs-studio]: https://obsproject.com/ [obs-studio]: https://obsproject.com/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 58 KiB

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:e744505d4be91a30830dd1d57d956a6c8775ea75385351b3f4c0d29faddd9ce3" content_hash = "sha256:292c5ea319597e3539895c1ac50004c884c5d46edd5f7b195ede79156558feab"
[[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.8.0" version = "1.7.2"
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.8.0-py3-none-any.whl", hash = "sha256:537bde416e149b6f59e0b2f31761d4a40329feafec171bde6fc1346ab8516e28"}, {file = "obsws_python-1.7.2-py3-none-any.whl", hash = "sha256:acda31852ad9d7165de915b0603c13f6df527d3f61619970bf5fb562e300bc85"},
{file = "obsws_python-1.8.0.tar.gz", hash = "sha256:e082894f80deb0836861fdc3c222e497308c8f66328da6075baba5b456a20971"}, {file = "obsws_python-1.7.2.tar.gz", hash = "sha256:b5cdaad30fbe1f6d4787b6530048b9882f070c3ee7830abb6dad4a47f84d7fa0"},
] ]
[[package]] [[package]]

View File

@@ -1,12 +1,12 @@
[project] [project]
name = "simple-recorder" name = "simple-recorder"
version = "0.4.0" 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 = [
"clypi>=1.8.1", "clypi>=1.8.1",
"FreeSimpleGUI>=5.2.0.post1", "FreeSimpleGUI>=5.2.0.post1",
"obsws-python>=1.8.0", "obsws-python>=1.7.2",
] ]
requires-python = ">=3.11" requires-python = ">=3.11"
readme = "README.md" readme = "README.md"
@@ -15,9 +15,6 @@ license = { text = "MIT" }
[project.scripts] [project.scripts]
simple-recorder = "simple_recorder:run" simple-recorder = "simple_recorder:run"
[project.gui-scripts]
simple-recorder-gui = "simple_recorder:run"
[build-system] [build-system]
requires = ["pdm-backend"] requires = ["pdm-backend"]
build-backend = "pdm.backend" build-backend = "pdm.backend"
@@ -31,4 +28,6 @@ compile = "shiv -c simple-recorder -o bin/simple-recorder.pyz ."
[dependency-groups] [dependency-groups]
build = ["shiv>=1.0.8"] build = [
"shiv>=1.0.8",
]

View File

@@ -3,11 +3,8 @@ 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 .resume import Resume
from .split import Split from .split import Split
from .start import Start from .start import Start
from .stop import Stop from .stop import Stop
@@ -19,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)}"
@@ -38,34 +35,20 @@ def theme_parser(value: str) -> str:
return value return value
SUBCOMMANDS = Start | Stop | Pause | Resume | Split | Directory
class SimpleRecorder(Command): class SimpleRecorder(Command):
subcommand: SUBCOMMANDS | 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,
help="Enable debug logging",
hidden=True,
) )
@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,36 +0,0 @@
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, timeout=3
) 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 (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -1,12 +1,8 @@
import logging import logging
import FreeSimpleGUI as fsg import FreeSimpleGUI as fsg
import obsws_python as obsws from clypi import ClypiException
from .directory import Directory
from .errors import SimpleRecorderError
from .pause import Pause
from .resume import Resume
from .split import Split from .split import Split
from .start import Start from .start import Start
from .stop import Stop from .stop import Stop
@@ -22,101 +18,30 @@ class SimpleRecorderWindow(fsg.Window):
self.password = password self.password = password
fsg.theme(theme) fsg.theme(theme)
try: layout = [
with obsws.ReqClient( [fsg.Text("Enter recording filename:")],
host=self.host, port=self.port, password=self.password, timeout=3 [fsg.InputText("", key="-FILENAME-")],
) as client:
resp = client.get_version()
status_message = f"Connected to OBS {resp.obs_version}"
resp = client.get_record_directory()
current_directory = resp.record_directory
except (ConnectionRefusedError, TimeoutError):
status_message = "Failed to connect to OBS. Is it running?"
current_directory = ""
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("Start Recording"),
fsg.Button("Stop", key="Stop Recording", size=(20, 1)), fsg.Button("Stop Recording"),
], fsg.Button("Split Recording"),
[
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)),
], ],
[fsg.Text("Status: Not started", key="-OUTPUT-")],
] ]
super().__init__("Simple Recorder", layout, finalize=True)
frame = fsg.Frame(
"",
recorder_layout,
relief=fsg.RELIEF_SUNKEN,
)
recorder_tab = fsg.Tab(
"Recorder",
[
[frame],
[
fsg.Text(
f"Status: {status_message}",
key="-OUTPUT-RECORDER-",
text_color="white"
if status_message.startswith("Connected")
else "red",
)
],
],
)
settings_layout = [
[fsg.Text("Enter the filepath for the recording:")],
[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)
mainframe = [
[fsg.TabGroup([[recorder_tab, settings_tab]])],
]
super().__init__("Simple Recorder", mainframe, 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["Pause Recording"].bind("<Return>", " || RETURN")
self["Resume Recording"].bind("<Return>", " || RETURN")
self["Split Recording"].bind("<Return>", " || RETURN") self["Split 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")
self["-GET-CURRENT-"].bind("<Return>", " || RETURN")
self["-UPDATE-"].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-"],
@@ -124,121 +49,41 @@ class SimpleRecorderWindow(fsg.Window):
port=self.port, port=self.port,
password=self.password, password=self.password,
).run() ).run()
self["-OUTPUT-RECORDER-"].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-RECORDER-"].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
).run() ).run()
self["-OUTPUT-RECORDER-"].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-RECORDER-"].update( self["-OUTPUT-"].update(
f"Error: {e.raw_message}", text_color="red" f"Error: {e.raw_message}", text_color="red"
) )
case ["Pause Recording"] | ["Pause Recording", "RETURN"]: case ["Split Recording", "RETURN" | None]:
try:
await Pause(
host=self.host, port=self.port, password=self.password
).run()
self["-OUTPUT-RECORDER-"].update(
"Recording paused successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-RECORDER-"].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-RECORDER-"].update(
"Recording resumed successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red"
)
case ["Add Chapter", "FOCUS" | "LEAVE" as focus_event]:
if focus_event == "FOCUS":
self["-OUTPUT-RECORDER-"].update(
"Right-click to set a chapter name", text_color="white"
)
else:
self["-OUTPUT-RECORDER-"].update("", text_color="white")
case ["Split Recording"] | ["Split Recording", "RETURN"]:
try: try:
await Split( await Split(
host=self.host, port=self.port, password=self.password host=self.host, port=self.port, password=self.password
).run() ).run()
self["-OUTPUT-RECORDER-"].update( self["-OUTPUT-"].update(
"Recording split successfully", text_color="green" "Recording split successfully", text_color="green"
) )
except SimpleRecorderError as e: except ClypiException as e:
self["-OUTPUT-RECORDER-"].update( self["-OUTPUT-"].update(
f"Error: {e.raw_message}", text_color="red"
)
case ["Add Chapter", "RIGHT_CLICK"]:
_ = fsg.popup_get_text(
"Enter chapter name:",
"Add Chapter",
default_text="unnamed",
)
case ["Add Chapter"]:
self["-OUTPUT-RECORDER-"].update(
"This feature is not implemented yet", text_color="orange"
)
case ["-GET-CURRENT-"] | ["-GET-CURRENT-", "RETURN"]:
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-"] | ["-UPDATE-", "RETURN"]:
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" f"Error: {e.raw_message}", text_color="red"
) )
case _: case _:
self.logger.debug(f"Unhandled event: {e}") self.logger.warning(f"Unhandled event: {event}")
self.close() 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 (ConnectionRefusedError, 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 (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -1,40 +1,27 @@
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 .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 recording."""
host: str = arg(inherited=True) host: str = arg(inherited=True)
port: int = arg(inherited=True) port: int = arg(inherited=True)
password: str = arg(inherited=True) password: str = arg(inherited=True)
@override
async def run(self): async def run(self):
"""Run the split command."""
try:
with obsws.ReqClient( with obsws.ReqClient(
host=self.host, port=self.port, password=self.password, timeout=3 host=self.host, port=self.port, password=self.password
) as client: ) as client:
resp = client.get_record_status() resp = client.get_record_status()
if not resp.output_active: if not resp.output_active:
raise SimpleRecorderError("No active recording to split.") raise SimpleRecorderError("Recording is not active.")
if resp.output_paused:
raise SimpleRecorderError(
"Recording is paused. Please resume before splitting."
)
client.split_record_file() client.split_record_file()
print("Recording split successfully.") 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

@@ -29,9 +29,8 @@ class Start(Command):
if not self.filename: if not self.filename:
raise SimpleRecorderError("Recording name cannot be empty.") raise SimpleRecorderError("Recording name cannot be empty.")
try:
with obsws.ReqClient( with obsws.ReqClient(
host=self.host, port=self.port, password=self.password, timeout=3 host=self.host, port=self.port, password=self.password
) as client: ) as client:
resp = client.get_record_status() resp = client.get_record_status()
if resp.output_active: if resp.output_active:
@@ -45,5 +44,3 @@ 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 (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -3,6 +3,7 @@ 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):
@@ -14,15 +15,12 @@ class Stop(Command):
@override @override
async def run(self): async def run(self):
try:
with obsws.ReqClient( with obsws.ReqClient(
host=self.host, port=self.port, password=self.password, timeout=3 host=self.host, port=self.port, password=self.password
) as client: ) as client:
resp = client.get_record_status() resp = client.get_record_status()
if not resp.output_active: if not resp.output_active:
raise SimpleRecorderError("Recording is not active.") raise SimpleRecorderError("Recording is not active.")
client.stop_record() client.stop_record()
print("Recording stopped successfully.") print(highlight("Recording stopped successfully."))
except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")