18 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
d7edaab2d1 bump obsws-python version
minor bump
2025-07-01 12:15:50 +01:00
09270683d9 implement record split
- CLI command added
- GUI button mouse/keyboard events implemented.

closes #1
2025-07-01 12:13:19 +01:00
b7c25525a1 catch ConnectionRefusedError as well as TimeoutError
patch bump
2025-06-30 07:44:23 +01:00
c39b909d11 fix gui --theme example 2025-06-30 07:04:33 +01:00
856ab6cbec patch bump 2025-06-30 07:02:10 +01:00
c7baa9ffd6 add separate entry point for the GUI. This allows the GUI to run with pythonw (no console) 2025-06-30 07:01:47 +01:00
d0401d0457 add keyboard bindings for current+update buttons 2025-06-30 06:28:08 +01:00
8813c25819 set timeout for directory command
initialise current_directory to empty value if the request fails.

patch bump
2025-06-30 06:17:05 +01:00
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
cb892d66d8 run ruff format as well 2025-06-27 12:38:35 +01:00
fccef1e2bf rename build workflow to release
add ruff workflow
2025-06-27 12:31:35 +01:00
17 changed files with 387 additions and 148 deletions

View File

@@ -1,62 +0,0 @@
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, '-') }}

26
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
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,26 +1,62 @@
name: Release
name: Release PYZ
on:
release:
types: [published]
push:
tags:
- 'v*.*.*'
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: write
jobs:
pypi-publish:
name: upload release to PyPI
build:
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
- name: Checkout code
uses: actions/checkout@v4
- uses: pdm-project/setup-pdm@v4
- name: Set up PDM
uses: pdm-project/setup-pdm@v4
with:
python-version: '3.11'
- name: Publish package distributions to PyPI
run: pdm publish
- 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, '-') }}

19
.github/workflows/ruff.yml vendored Normal file
View File

@@ -0,0 +1,19 @@
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'

View File

@@ -28,7 +28,18 @@ pipx install simple-recorder
*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.
- Download the pyz file in [Releases](https://github.com/onyx-and-iris/simple-recorder/releases)
- 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
@@ -49,28 +60,18 @@ OBS_THEME=Reds
## 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*.
#### Themes
You can change the colour theme with the --theme option:
```console
simple-recorder --theme="Light Purple"
```
### CLI
To launch the CLI:
```console
simple-recorder start "File Name"
simple-recorder stop
```
#### Commands:
```shell
Usage: simple-recorder [OPTIONS] COMMAND
@@ -79,6 +80,9 @@ Usage: simple-recorder [OPTIONS] COMMAND
┃ stop Stop recording ┃
┃ pause Pause 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 ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
┏━ Options ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
@@ -90,15 +94,24 @@ Usage: simple-recorder [OPTIONS] COMMAND
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
```
To launch the CLI pass any subcommand (start/stop etc...), for example:
### GUI
To launch the GUI:
```console
simple-recorder start "File Name"
simple-recorder stop
simple-recorder-gui
```
- 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.
![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/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 78 KiB

8
pdm.lock generated
View File

@@ -5,7 +5,7 @@
groups = ["default", "build"]
strategy = ["inherit_metadata"]
lock_version = "4.5.0"
content_hash = "sha256:292c5ea319597e3539895c1ac50004c884c5d46edd5f7b195ede79156558feab"
content_hash = "sha256:e744505d4be91a30830dd1d57d956a6c8775ea75385351b3f4c0d29faddd9ce3"
[[metadata.targets]]
requires_python = ">=3.11"
@@ -63,7 +63,7 @@ files = [
[[package]]
name = "obsws-python"
version = "1.7.2"
version = "1.8.0"
requires_python = ">=3.9"
summary = "A Python SDK for OBS Studio WebSocket v5.0"
groups = ["default"]
@@ -72,8 +72,8 @@ dependencies = [
"websocket-client",
]
files = [
{file = "obsws_python-1.7.2-py3-none-any.whl", hash = "sha256:acda31852ad9d7165de915b0603c13f6df527d3f61619970bf5fb562e300bc85"},
{file = "obsws_python-1.7.2.tar.gz", hash = "sha256:b5cdaad30fbe1f6d4787b6530048b9882f070c3ee7830abb6dad4a47f84d7fa0"},
{file = "obsws_python-1.8.0-py3-none-any.whl", hash = "sha256:537bde416e149b6f59e0b2f31761d4a40329feafec171bde6fc1346ab8516e28"},
{file = "obsws_python-1.8.0.tar.gz", hash = "sha256:e082894f80deb0836861fdc3c222e497308c8f66328da6075baba5b456a20971"},
]
[[package]]

View File

@@ -1,12 +1,12 @@
[project]
name = "simple-recorder"
version = "0.2.0"
version = "0.5.0"
description = "A simple OBS recorder"
authors = [{ name = "onyx-and-iris", email = "code@onyxandiris.online" }]
dependencies = [
"clypi>=1.8.1",
"FreeSimpleGUI>=5.2.0.post1",
"obsws-python>=1.7.2",
"obsws-python>=1.8.0",
]
requires-python = ">=3.11"
readme = "README.md"
@@ -15,6 +15,9 @@ license = { text = "MIT" }
[project.scripts]
simple-recorder = "simple_recorder:run"
[project.gui-scripts]
simple-recorder-gui = "simple_recorder:run"
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
@@ -28,6 +31,4 @@ compile = "shiv -c simple-recorder -o bin/simple-recorder.pyz ."
[dependency-groups]
build = [
"shiv>=1.0.8",
]
build = ["shiv>=1.0.8"]

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,15 +3,16 @@ import logging
from clypi import ClypiConfig, ClypiException, Command, arg, configure
from typing_extensions import override
from .chapter import Chapter
from .directory import Directory
from .errors import SimpleRecorderError
from .gui import SimpleRecorderWindow
from .pause import Pause
from .resume import Resume
from .split import Split
from .start import Start
from .stop import Stop
logger = logging.getLogger(__name__)
config = ClypiConfig(
nice_errors=(SimpleRecorderError,),
)
@@ -36,8 +37,11 @@ def theme_parser(value: str) -> str:
return value
SUBCOMMANDS = Start | Stop | Pause | Resume | Split | Chapter | Directory
class SimpleRecorder(Command):
subcommand: Start | Stop | Pause | Resume | None = None
subcommand: SUBCOMMANDS | 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(
@@ -51,17 +55,20 @@ class SimpleRecorder(Command):
)
debug: bool = arg(
default=False,
env="DEBUG",
help="Enable debug logging",
hidden=True,
)
@override
async def run(self):
"""Run the Simple Recorder GUI."""
async def pre_run_hook(self):
if self.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)
await window.run()

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, 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

@@ -3,9 +3,12 @@ import logging
import FreeSimpleGUI as fsg
import obsws_python as obsws
from .chapter import Chapter
from .directory import Directory
from .errors import SimpleRecorderError
from .pause import Pause
from .resume import Resume
from .split import Split
from .start import Start
from .stop import Stop
@@ -26,8 +29,11 @@ class SimpleRecorderWindow(fsg.Window):
) 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-")],
@@ -59,7 +65,7 @@ class SimpleRecorderWindow(fsg.Window):
[
fsg.Text(
f"Status: {status_message}",
key="-OUTPUT-",
key="-OUTPUT-RECORDER-",
text_color="white"
if status_message.startswith("Connected")
else "red",
@@ -70,7 +76,12 @@ class SimpleRecorderWindow(fsg.Window):
settings_layout = [
[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)
@@ -85,8 +96,10 @@ class SimpleRecorderWindow(fsg.Window):
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["Add Chapter"].bind("<Return>", " || RETURN")
self["Add Chapter"].bind("<Shift-Return>", " || SHIFT-RETURN")
self["-FILENAME-"].bind("<KeyPress>", " || KEYPRESS")
self["-FILENAME-"].update(select=True)
self["Add Chapter"].bind("<FocusIn>", " || FOCUS")
self["Add Chapter"].bind("<Enter>", " || FOCUS")
@@ -94,7 +107,12 @@ class SimpleRecorderWindow(fsg.Window):
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):
chapter_name = "unnamed"
while True:
event, values = self.read()
self.logger.debug(f"Event: {event}, Values: {values}")
@@ -110,11 +128,11 @@ class SimpleRecorderWindow(fsg.Window):
port=self.port,
password=self.password,
).run()
self["-OUTPUT-"].update(
self["-OUTPUT-RECORDER-"].update(
"Recording started successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-"].update(
self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red"
)
@@ -123,11 +141,11 @@ class SimpleRecorderWindow(fsg.Window):
await Stop(
host=self.host, port=self.port, password=self.password
).run()
self["-OUTPUT-"].update(
self["-OUTPUT-RECORDER-"].update(
"Recording stopped successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-"].update(
self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red"
)
@@ -136,11 +154,11 @@ class SimpleRecorderWindow(fsg.Window):
await Pause(
host=self.host, port=self.port, password=self.password
).run()
self["-OUTPUT-"].update(
self["-OUTPUT-RECORDER-"].update(
"Recording paused successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-"].update(
self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red"
)
@@ -149,32 +167,94 @@ class SimpleRecorderWindow(fsg.Window):
await Resume(
host=self.host, port=self.port, password=self.password
).run()
self["-OUTPUT-"].update(
self["-OUTPUT-RECORDER-"].update(
"Recording resumed successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-"].update(
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-"].update(
self["-OUTPUT-RECORDER-"].update(
"Right-click to set a chapter name", text_color="white"
)
else:
self["-OUTPUT-"].update("", text_color="white")
self["-OUTPUT-RECORDER-"].update("", text_color="white")
case ["Add Chapter", "RIGHT_CLICK"]:
_ = fsg.popup_get_text(
case ["Split Recording"] | ["Split Recording", "RETURN"]:
try:
await Split(
host=self.host, port=self.port, password=self.password
).run()
self["-OUTPUT-RECORDER-"].update(
"Recording split successfully", text_color="green"
)
except SimpleRecorderError as e:
self["-OUTPUT-RECORDER-"].update(
f"Error: {e.raw_message}", text_color="red"
)
case ["Add Chapter", "RIGHT_CLICK" | "SHIFT-RETURN"]:
chapter_name = 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 ["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(
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"]:
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"
)
case _:

View File

@@ -26,5 +26,5 @@ class Pause(Command):
client.pause_record()
print("Recording paused successfully.")
except TimeoutError:
except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -26,5 +26,5 @@ class Resume(Command):
client.resume_record()
print("Recording resumed successfully.")
except TimeoutError:
except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -0,0 +1,36 @@
import obsws_python as obsws
from clypi import Command, arg
from typing_extensions import override
from .errors import SimpleRecorderError
class Split(Command):
"""Split the current recording into a new file."""
host: str = arg(inherited=True)
port: int = arg(inherited=True)
password: str = arg(inherited=True)
@override
async def run(self):
"""Run the split 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 split.")
client.split_record_file()
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

@@ -45,5 +45,5 @@ class Start(Command):
)
client.start_record()
print(f"Recording started with filename: {highlight(filename)}")
except TimeoutError:
except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")

View File

@@ -3,7 +3,6 @@ from clypi import Command, arg
from typing_extensions import override
from .errors import SimpleRecorderError
from .styler import highlight
class Stop(Command):
@@ -24,6 +23,6 @@ class Stop(Command):
raise SimpleRecorderError("Recording is not active.")
client.stop_record()
print(highlight("Recording stopped successfully."))
except TimeoutError:
print("Recording stopped successfully.")
except (ConnectionRefusedError, TimeoutError):
raise SimpleRecorderError("Failed to connect to OBS. Is it running?")