Compare commits

...

27 Commits

Author SHA1 Message Date
79f4156ffa chore(version): bump to 0.6.0
All checks were successful
Build and Publish / build-and-publish (push) Successful in 1m23s
2025-10-05 20:33:46 +02:00
7f4b559644 feat(module): add support for llama-swap API integration 2025-10-05 20:33:27 +02:00
98b5ab1f1c chore(version): bump to 0.5.0
All checks were successful
Build and Publish / build-and-publish (push) Successful in 1m27s
2025-10-05 20:14:16 +02:00
e628816ea8 feat(config): enhance Ollama configuration with dynamic path support 2025-10-05 20:13:42 +02:00
4e4389a03f chore(version): bump to 0.4.5
All checks were successful
Build and Publish / build-and-publish (push) Successful in 1m40s
- Update project version from 0.4.4 to 0.4.5
- Prepares for the next release with minor updates
2025-05-18 20:41:38 +02:00
2c0cffe16a fix(worker): adjust timestamp format in journal entries
- Updates the timestamp format from HH:MM:SS to HH:MM for journal logs
- Simplifies the time representation in generated journal paths
2025-05-18 20:41:14 +02:00
98fc0596d4 chore: bump version to 0.4.4
All checks were successful
Build and Publish / build-and-publish (push) Successful in 19s
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-05-05 12:02:55 +02:00
7059ebda4c refactor(send_cmd): rename config variable for clarity
- Rename `CONFIGURATION` to `CONFIG` for consistency and brevity.
- Update method call to use dot notation for accessing `socket_path`.

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-05-05 12:02:51 +02:00
46215a8aef feat(dependencies): add pydantic and typing-related packages to poetry.lock
All checks were successful
Build and Publish / build-and-publish (push) Successful in 26s
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-05-05 12:00:49 +02:00
809145f53a feat(project): update version and add pydantic dependency
- Bump project version from 0.4.2 to 0.4.3.
- Add `pydantic` as a dependency for improved data validation.

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-05-05 12:00:42 +02:00
58b9cb586c feat(libs): integrate Ollama and Whisper clients with config models
- Add `AppConfig` and `PresetConfig` models using Pydantic for config validation
- Refactor `read_configurations` to return an `AppConfig` instance
- Implement `OllamaClient` for chat-based server interaction
- Implement `WhisperClient` for transcription via Whisper CLI
- Migrate notification utilities to `libs` directory
- Update tray application to use new clients and config structure
- Simplify Whisper and Ollama integration logic in `WhisperWorker`

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-05-05 12:00:33 +02:00
5688769437 chore: bump version to 0.4.2
All checks were successful
Build and Publish / build-and-publish (push) Successful in 13s
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 17:52:45 +02:00
f4604d4485 refactor(voice_to_text_tray): improve configuration handling
- Replace direct usage of global configuration dictionaries with local
  variables for better readability and maintainability.
- Update all references to use the newly introduced local variables.

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 17:52:40 +02:00
741a337278 fix(build-and-deploy): revert cache action to v1 and simplify cache key
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 17:34:04 +02:00
7e4d6524ed chore: bump version to 0.4.1
All checks were successful
Build and Publish / build-and-publish (push) Successful in 16s
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:55:35 +02:00
4aa5466597 chore: bump version to 0.4.0
All checks were successful
Build and Publish / build-and-publish (push) Successful in 24s
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:44:48 +02:00
495452f7bd feat(pyvtt): add support for optional preset in commands
- Add `preset` argument to `send_cmd` for passing optional presets.
- Modify command formatting to include preset if provided.
- Update CLI to accept an optional `preset` argument.
- Extend socket listener to parse and validate preset values.
- Enhance functionality to allow preset-based dynamic behavior.

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:44:35 +02:00
7719cf429d chore: bump version to 0.3.3
All checks were successful
Build and Publish / build-and-publish (push) Successful in 16s
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:30:54 +02:00
d7f783834f fix(voice_to_text_tray): use get() method for safer access to CURRENT_PRESET mode
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:30:48 +02:00
d18a9915f9 chore: bump version to 0.3.2
All checks were successful
Build and Publish / build-and-publish (push) Successful in 16s
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:24:15 +02:00
da76f9420b fix(voice_to_text_tray): update journal timestamp format to include seconds
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:24:10 +02:00
b4a728d0c6 chore: bump version to 0.3.1
All checks were successful
Build and Publish / build-and-publish (push) Successful in 18s
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:21:30 +02:00
f33b583aec feat(voice_to_text_tray): enhance journal creation with error handling and timestamp formatting
Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:21:24 +02:00
78881a572d chore: bump version to 0.3.0
All checks were successful
Build and Publish / build-and-publish (push) Successful in 17s
- Update project version from 0.2.1 to 0.3.0 in pyproject.toml.

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:18:35 +02:00
0a808a8471 feat(voice_to_text_tray): add journal saving for transcription results
- Add functionality to save transcription results to a journal file.
- Use current date and time to organize entries in the journal.
- Retain clipboard copy behavior for non-journal modes.
- Enhance error handling for journal writing operations.

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:18:22 +02:00
776d95f8cd chore: bump version to 0.2.1
All checks were successful
Build and Publish / build-and-publish (push) Successful in 17s
- Update project version from 0.2.0 to 0.2.1 in pyproject.toml.
- Indicates a minor update, likely for small improvements or fixes.

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:07:36 +02:00
b5281b393c refactor(configuration): simplify import paths
- Update import statements to remove redundant `src` prefix.
- Improve clarity and maintainability of module references.

Signed-off-by: Max P. <Mail@MPassarello.de>
2025-04-30 15:07:10 +02:00
11 changed files with 461 additions and 105 deletions

View File

@@ -20,11 +20,9 @@ jobs:
python-version: "3.12" python-version: "3.12"
- name: 🔄 Restore cache - name: 🔄 Restore cache
uses: https://git.0xmax42.io/actions/cache@v2 uses: https://git.0xmax42.io/actions/cache@v1
with: with:
keys: | key: poetry-v1-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
poetry-v1-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
poetry-v1-${{ runner.os }}
paths: | paths: |
~/.cache/pypoetry ~/.cache/pypoetry
~/.cache/pip ~/.cache/pip

175
poetry.lock generated
View File

@@ -1,5 +1,17 @@
# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. # This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand.
[[package]]
name = "annotated-types"
version = "0.7.0"
description = "Reusable constraint types to use with typing.Annotated"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"},
{file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"},
]
[[package]] [[package]]
name = "certifi" name = "certifi"
version = "2025.4.26" version = "2025.4.26"
@@ -518,6 +530,140 @@ files = [
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
] ]
[[package]]
name = "pydantic"
version = "2.11.4"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"},
{file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"},
]
[package.dependencies]
annotated-types = ">=0.6.0"
pydantic-core = "2.33.2"
typing-extensions = ">=4.12.2"
typing-inspection = ">=0.4.0"
[package.extras]
email = ["email-validator (>=2.0.0)"]
timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
[[package]]
name = "pydantic-core"
version = "2.33.2"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"},
{file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"},
{file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"},
{file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"},
{file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"},
{file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"},
{file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"},
{file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"},
{file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"},
{file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"},
{file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"},
{file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"},
{file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"},
{file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"},
{file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"},
{file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"},
{file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"},
{file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"},
{file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"},
{file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"},
{file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"},
{file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"},
{file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"},
{file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"},
{file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"},
{file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"},
{file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"},
{file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"},
{file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"},
{file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"},
{file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"},
{file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"},
{file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"},
{file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"},
{file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"},
{file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"},
{file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"},
{file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"},
{file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"},
{file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"},
{file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"},
{file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"},
{file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"},
{file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"},
{file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"},
{file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"},
{file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"},
{file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"},
{file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"},
{file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"},
]
[package.dependencies]
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[[package]] [[package]]
name = "pygments" name = "pygments"
version = "2.19.1" version = "2.19.1"
@@ -745,6 +891,33 @@ urllib3 = ">=1.26.0"
[package.extras] [package.extras]
keyring = ["keyring (>=15.1)"] keyring = ["keyring (>=15.1)"]
[[package]]
name = "typing-extensions"
version = "4.13.2"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
groups = ["main"]
files = [
{file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"},
{file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"},
]
[[package]]
name = "typing-inspection"
version = "0.4.0"
description = "Runtime typing introspection tools"
optional = false
python-versions = ">=3.9"
groups = ["main"]
files = [
{file = "typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f"},
{file = "typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122"},
]
[package.dependencies]
typing-extensions = ">=4.12.0"
[[package]] [[package]]
name = "urllib3" name = "urllib3"
version = "2.4.0" version = "2.4.0"
@@ -766,4 +939,4 @@ zstd = ["zstandard (>=0.18.0)"]
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.12" python-versions = ">=3.12"
content-hash = "33d6dddddf6b4c9ff020272b1e39c87ac4b85a53683a2929b7e0673ef4a09e0b" content-hash = "095dc8efb8ce84ac67e84e537fe81f42eca53375ddcc2bd267be592efb9232cf"

View File

@@ -1,12 +1,16 @@
[project] [project]
name = "pyvtt" name = "pyvtt"
version = "0.2.0" version = "0.6.0"
description = "Python Voice to Text + LLMA" description = "Python Voice to Text + LLMA"
authors = [{ name = "Max P.", email = "Mail@MPassarello.de" }] authors = [{ name = "Max P.", email = "Mail@MPassarello.de" }]
license = { text = "MIT" } license = { text = "MIT" }
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["pyqt5 (>=5.15.11,<6.0.0)", "requests (>=2.32.3,<3.0.0)"] dependencies = [
"pyqt5 (>=5.15.11,<6.0.0)",
"requests (>=2.32.3,<3.0.0)",
"pydantic (>=2.11.4,<3.0.0)",
]
[tool.poetry] [tool.poetry]
packages = [{ include = "pyvtt", from = "src" }] packages = [{ include = "pyvtt", from = "src" }]

View File

@@ -2,23 +2,26 @@
"audio_file": "/tmp/pyvtt_recording.wav", "audio_file": "/tmp/pyvtt_recording.wav",
"output_file": "/tmp/pyvtt_transcript.txt", "output_file": "/tmp/pyvtt_transcript.txt",
"whisper_path": "/path/to/whisper-cli", "whisper_path": "/path/to/whisper-cli",
"language": "en",
"socket_path": "/tmp/pyvtt.sock", "socket_path": "/tmp/pyvtt.sock",
"ollama_url": "http://localhost", "ollama_url": "http://localhost",
"ollama_path": "/api/chat",
"ollama_port": 12345, "ollama_port": 12345,
"presets": [ "presets": [
{ {
"name": "Default", "name": "Default",
"language": "en", "language": "en",
"whisper_model": "/path/to/default-whisper-model.bin", "whisper_model": "/path/to/default-whisper-model.bin",
"ollama_model": "default-model", "ollama": "disable"
"ollama_prompt": "Provide a detailed response to the following text:\n\n"
}, },
{ {
"name": "Quick English", "name": "Quick English",
"whisper_model": "/path/to/quick-whisper-model.bin", "whisper_model": "/path/to/quick-whisper-model.bin",
"ollama_model": "quick-model", "ollama_model": "gemma3:4b",
"ollama_prompt": "Quickly correct the following English text for grammar and punctuation:\n\n" "ollama_context": 131072,
"ollama_prompt": [
"Quickly correct the following English text for grammar and punctuation:\n",
"\n"
]
}, },
{ {
"name": "German Correction", "name": "German Correction",

View File

@@ -2,9 +2,11 @@ import json
import os import os
from pathlib import Path from pathlib import Path
from pyvtt.models.config import AppConfig
DEFAULT_CONFIG_PATH = Path.home() / ".pyvtt.json" DEFAULT_CONFIG_PATH = Path.home() / ".pyvtt.json"
def read_configurations(): def read_configurations() -> AppConfig:
""" """
Reads the configuration settings from a JSON file named 'pyvtt.settings.json' Reads the configuration settings from a JSON file named 'pyvtt.settings.json'
located in the same directory as the script. located in the same directory as the script.
@@ -18,7 +20,8 @@ def read_configurations():
""" """
try: try:
with open(DEFAULT_CONFIG_PATH) as f: with open(DEFAULT_CONFIG_PATH) as f:
return json.load(f) raw_config = json.load(f)
return AppConfig(**raw_config)
except Exception as e: except Exception as e:
print(f"Error reading configurations: {e}") print(f"Error reading configurations: {e}")
raise Exception(f"Error reading configurations: {e}") raise Exception(f"Error reading configurations: {e}")

103
src/pyvtt/libs/ollama.py Normal file
View File

@@ -0,0 +1,103 @@
import json
import requests
from typing import Union, List, Optional
from pyvtt.libs.notify import notify
from pyvtt.models.config import AppConfig, PresetConfig
class OllamaClient:
def __init__(self, config: AppConfig):
"""
Initialisiert den API-Client (Ollama oder llama-swap) mit Basis-Konfiguration.
"""
self.base_url = config.ollama_url.rstrip("/")
self.port = config.ollama_port
self.path = config.ollama_path or "/api/chat"
# Falls llama-swap (OpenAI-API-Kompatibel), verwende den OpenAI-Pfad
if "v1" in self.path or "completions" in self.path:
self.is_llama_swap = True
else:
self.is_llama_swap = False
def send_chat(self, user_message: str, config: PresetConfig) -> str:
"""
Sendet eine Chat-Anfrage an Ollama oder llama-swap.
"""
if config.ollama and config.ollama.lower() == "disable":
print("[OllamaClient] Ollama ist im Preset deaktiviert.")
return user_message
# Prompt aufbereiten
prompt_str = (
"\n".join(config.ollama_prompt)
if isinstance(config.ollama_prompt, list)
else str(config.ollama_prompt)
)
# === Payload vorbereiten ===
if self.is_llama_swap:
# OpenAI-/llama-swap-kompatibles Format
payload = {
"model": config.ollama_model,
"messages": [
{"role": "system", "content": prompt_str},
{"role": "user", "content": user_message},
],
"stream": False,
}
# Kontextgröße optional hinzufügen
if config.ollama_context:
payload["num_ctx"] = config.ollama_context
else:
# Klassisches Ollama-Format
payload = {
"model": config.ollama_model,
"messages": [
{"role": "system", "content": prompt_str},
{"role": "user", "content": user_message},
],
"options": (
{"num_ctx": config.ollama_context}
if config.ollama_context
else {}
),
"stream": False,
}
endpoint = f"{self.base_url}:{self.port}{self.path}"
# === Anfrage senden ===
try:
headers = {"Content-Type": "application/json"}
if self.is_llama_swap:
headers["Authorization"] = "Bearer no-key"
response = requests.post(endpoint, headers=headers, data=json.dumps(payload))
response.raise_for_status()
json_response = response.json()
# === Antwort extrahieren ===
if self.is_llama_swap:
# OpenAI-kompatible Struktur
content = (
json_response.get("choices", [{}])[0]
.get("message", {})
.get("content", "")
.strip()
)
else:
# Ollama-eigene Struktur
content = (
json_response.get("message", {})
.get("content", "")
.strip()
)
return "\n".join(line.strip() for line in content.splitlines())
except requests.exceptions.RequestException as e:
print(f"[OllamaClient] HTTP-Fehler: {e}")
notify("Fehler", "Kommunikationsfehler mit Ollama / llama-swap!")
return ""

50
src/pyvtt/libs/whisper.py Normal file
View File

@@ -0,0 +1,50 @@
import subprocess
from typing import Optional
from pathlib import Path
from pyvtt.libs.notify import notify
from pyvtt.models.config import AppConfig, PresetConfig
class WhisperClient:
def __init__(self, config: AppConfig):
"""
Initialisiert den Whisper-Client mit der globalen Anwendungskonfiguration.
:param config: AppConfig-Instanz mit Pfaden zur Whisper-Binary, Audio- und Ausgabedatei.
"""
self.whisper_path = config.whisper_path
self.audio_file = config.audio_file
self.output_file = config.output_file
def transcribe(self, config: PresetConfig) -> str:
"""
Führt Whisper (CLI) zur Transkription der Audiodatei aus und gibt das Transkript zurück.
:param config: PresetConfig-Instanz mit Whisper-Modell und Spracheinstellungen.
:return: Das rohe Transkript als String – oder None bei Fehlern.
"""
output_base = self.output_file.replace(".txt", "")
whisper_cmd = [
self.whisper_path,
"-m", config.whisper_model,
"-f", self.audio_file,
"-l", config.language,
"-otxt",
"-of", output_base
]
try:
subprocess.run(whisper_cmd, check=True)
except subprocess.CalledProcessError as e:
print(f"[WhisperClient] Whisper-Ausführungsfehler: {e}")
notify("Fehler", "Ein Fehler mit 'Whisper' ist aufgetreten!")
return ""
try:
with open(self.output_file, "r", encoding="utf-8") as f:
return "\n".join(line.strip() for line in f.readlines())
except Exception as e:
print(f"[WhisperClient] Fehler beim Einlesen der Ausgabedatei: {e}")
notify("Fehler", "Ein Fehler beim Lesen der Whisper-Ausgabe ist aufgetreten!")
return ""

View File

@@ -0,0 +1,26 @@
from typing import List, Optional, Union
from pydantic import BaseModel, HttpUrl, Field
class PresetConfig(BaseModel):
name: str
language: str
whisper_model: str
ollama: Optional[str] = None
ollama_model: Optional[str] = None
ollama_context: Optional[int] = None
ollama_prompt: Optional[Union[str, List[str]]] = None
mode: Optional[str] = None
journal_name: Optional[str] = None
class AppConfig(BaseModel):
audio_file: str
output_file: str
whisper_path: str
socket_path: str
ollama_url: str
ollama_path: str
ollama_port: int
journal_path: str
presets: List[PresetConfig]

View File

@@ -1,11 +1,13 @@
import socket import socket
import sys import sys
import argparse import argparse
from src.pyvtt.configuration import read_configurations from pyvtt.configuration import read_configurations
from typing import Optional
CONFIGURATION = read_configurations() CONFIG = read_configurations()
def send_cmd(cmd: str, socket_path: str):
def send_cmd(cmd: str, socket_path: str, preset: Optional[str] = None) -> None:
""" """
Sends a command to a Unix domain socket server. Sends a command to a Unix domain socket server.
@@ -26,6 +28,8 @@ def send_cmd(cmd: str, socket_path: str):
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client: with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
client.settimeout(3) client.settimeout(3)
client.connect(socket_path) client.connect(socket_path)
if preset:
cmd = f"{cmd}::::{preset}"
client.sendall(cmd.encode()) client.sendall(cmd.encode())
except FileNotFoundError: except FileNotFoundError:
print(f"Error: The socket file '{socket_path}' does not exist.", file=sys.stderr, flush=True) print(f"Error: The socket file '{socket_path}' does not exist.", file=sys.stderr, flush=True)
@@ -47,6 +51,15 @@ def main():
default="toggle", default="toggle",
help="The command to send to the server (default: toggle).", help="The command to send to the server (default: toggle).",
) )
parser.add_argument(
"preset",
nargs="?",
default=None,
help="The preset to use (optional)."
)
args = parser.parse_args() args = parser.parse_args()
send_cmd(args.command, CONFIGURATION["socket_path"]) send_cmd(args.command, CONFIG.socket_path, args.preset)

View File

@@ -1,3 +1,4 @@
import datetime
import sys import sys
import subprocess import subprocess
import os import os
@@ -8,11 +9,13 @@ import requests
from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QAction from PyQt5.QtWidgets import QApplication, QSystemTrayIcon, QMenu, QAction
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtCore import QThread, pyqtSignal
from src.pyvtt.configuration import read_configurations from pyvtt.configuration import read_configurations
from pyvtt.notify import notify, play_sound from pyvtt.libs.notify import notify, play_sound
from pyvtt.libs.ollama import OllamaClient
from pyvtt.libs.whisper import WhisperClient
CONFIGURATION = read_configurations() CONFIG = read_configurations()
CURRENT_PRESET = CONFIGURATION["presets"][0] # Default to first preset CURRENT_PRESET = CONFIG.presets[0] # Default to first preset
class WhisperWorker(QThread): class WhisperWorker(QThread):
""" """
@@ -30,71 +33,41 @@ class WhisperWorker(QThread):
""" """
finished = pyqtSignal(str) finished = pyqtSignal(str)
def __init__(self):
super().__init__()
self.whisper = WhisperClient(CONFIG)
self.ollama = OllamaClient(CONFIG)
def run(self): def run(self):
try: CURENT_CONFIG_LOCALE = CONFIG
# Whisper ausführen CURRENT_PRESET_LOCALE = CURRENT_PRESET
whisper_cmd = [
CONFIGURATION["whisper_path"],
"-m", CURRENT_PRESET["whisper_model"],
"-f", CONFIGURATION["audio_file"],
"-l", CURRENT_PRESET["language"],
"-otxt",
"-of", CONFIGURATION["output_file"].replace(".txt", "")
]
try:
subprocess.run(whisper_cmd, check=True)
except subprocess.CalledProcessError as e:
print(f"Whisper Fehler: {e}")
notify("Fehler", "Ein Fehler mit 'Whisper' ist aufgetreten!")
return
try: try:
with open(CONFIGURATION["output_file"], "r") as f: raw_result = self.whisper.transcribe(CURRENT_PRESET_LOCALE)
raw_result = "\n".join(line.strip() for line in f.readlines()) formatted_result = self.ollama.send_chat(raw_result, CURRENT_PRESET_LOCALE)
except Exception as e:
print(f"Datei Fehler: {e}")
notify("Fehler", "Ein Fehler beim Lesen der Whisper-Ausgabe ist aufgetreten!")
return
print("Whisper Transkript erhalten.")
# --- An Ollama schicken ---
if CURRENT_PRESET["ollama"] != "disable":
if isinstance(CURRENT_PRESET["ollama_prompt"], list):
prompt = "\n".join(CURRENT_PRESET["ollama_prompt"])
else:
prompt = CURRENT_PRESET["ollama_prompt"]
payload = {
"model": CURRENT_PRESET["ollama_model"],
"messages": [
{"role": "system", "content": prompt},
{"role": "user", "content": raw_result}
],
"options": {
"num_ctx": CURRENT_PRESET["ollama_context"]
},
"stream": False
}
ollama_endpoint = f"{CONFIGURATION['ollama_url']}:{CONFIGURATION['ollama_port']}/api/chat"
response = requests.post(ollama_endpoint, json=payload)
try:
response.raise_for_status()
except requests.exceptions.HTTPError as e:
print(f"HTTP Fehler: {e}")
notify("Fehler", "Ein Fehler bei der Kommunikation mit 'Ollama' ist aufgetreten!")
return
json_response = response.json()
formatted_result = json_response.get("message", {}).get("content", "").strip()
formatted_result = "\n".join(line.strip() for line in formatted_result.splitlines())
print("Ollama Antwort erhalten.")
else:
formatted_result = raw_result
print("Kein Ollama Prompt angegeben, nur Whisper Ergebnis verwendet.")
# Ergebnis ins Clipboard kopieren # Ergebnis ins Clipboard kopieren
if CURRENT_PRESET_LOCALE.mode == "journal":
today = datetime.date.today().strftime("%Y.%m.%d")
journal_path = os.path.join(CURENT_CONFIG_LOCALE.journal_path, f"{today} - {CURRENT_PRESET_LOCALE.journal_name}.md")
now = datetime.datetime.now().strftime("%H:%M")
if not os.path.exists(journal_path):
try:
with open(journal_path, "w") as f:
f.write(f"# {CURRENT_PRESET_LOCALE.journal_name} - {today}\n\n")
except Exception as e:
print(f"Journal Erstellungsfehler: {e}")
notify("Fehler", "Ein Fehler beim Erstellen des Journals ist aufgetreten!")
return
try:
with open(journal_path, "a") as f:
f.write(f"## {now} Uhr\n")
f.write(f"{formatted_result}\n\n")
except Exception as e:
print(f"Journal Fehler: {e}")
notify("Fehler", "Ein Fehler beim Schreiben ins Journal ist aufgetreten!")
return
else:
try: try:
subprocess.run(["wl-copy"], input=formatted_result.encode(), check=True) subprocess.run(["wl-copy"], input=formatted_result.encode(), check=True)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
@@ -132,11 +105,11 @@ class SocketListener(threading.Thread):
def __init__(self, tray_app): def __init__(self, tray_app):
super().__init__(daemon=True) super().__init__(daemon=True)
self.tray_app = tray_app self.tray_app = tray_app
if os.path.exists(CONFIGURATION["socket_path"]): if os.path.exists(CONFIG.socket_path):
os.remove(CONFIGURATION["socket_path"]) os.remove(CONFIG.socket_path)
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sock.bind(CONFIGURATION["socket_path"]) self.sock.bind(CONFIG.socket_path)
os.chmod(CONFIGURATION["socket_path"], 0o666) os.chmod(CONFIG.socket_path, 0o666)
self.sock.listen(1) self.sock.listen(1)
def run(self): def run(self):
@@ -144,6 +117,15 @@ class SocketListener(threading.Thread):
conn, _ = self.sock.accept() conn, _ = self.sock.accept()
with conn: with conn:
data = conn.recv(1024).decode().strip() data = conn.recv(1024).decode().strip()
if data:
cmd = data.split("::::")
if len(cmd) > 1:
data = cmd[0]
preset = cmd[1]
if preset in [p.name for p in CONFIG.presets]:
self.tray_app.set_preset([p.name for p in CONFIG.presets].index(preset))
else:
data = cmd[0]
if data == "toggle": if data == "toggle":
self.tray_app.toggle_recording() self.tray_app.toggle_recording()
elif data == "start": elif data == "start":
@@ -190,8 +172,8 @@ class TrayApp:
# Preset Menü # Preset Menü
self.preset_actions = [] self.preset_actions = []
self.preset_group = QMenu("Presets") self.preset_group = QMenu("Presets")
for i, preset in enumerate(CONFIGURATION["presets"]): for i, preset in enumerate(CONFIG.presets):
action = QAction(preset["name"], self.menu) action = QAction(preset.name, self.menu)
action.setCheckable(True) action.setCheckable(True)
if i == 0: if i == 0:
action.setChecked(True) action.setChecked(True)
@@ -221,8 +203,9 @@ class TrayApp:
def set_preset(self, index): def set_preset(self, index):
global CURRENT_PRESET global CURRENT_PRESET
print(f"Preset gewechselt: {CONFIGURATION['presets'][index]['name']}") selected_preset = CONFIG.presets[index]
CURRENT_PRESET = CONFIGURATION["presets"][index] print(f"Preset gewechselt: {selected_preset.name}")
CURRENT_PRESET = selected_preset
# Nur einer darf gecheckt sein # Nur einer darf gecheckt sein
for i, action in enumerate(self.preset_actions): for i, action in enumerate(self.preset_actions):
action.setChecked(i == index) action.setChecked(i == index)
@@ -232,7 +215,7 @@ class TrayApp:
print("Starte Aufnahme...") print("Starte Aufnahme...")
self.recording_process = subprocess.Popen([ self.recording_process = subprocess.Popen([
"ffmpeg", "-f", "pulse", "-i", "default", "-ar", "16000", "ffmpeg", "-f", "pulse", "-i", "default", "-ar", "16000",
"-ac", "1", CONFIGURATION["audio_file"], "-y", "-loglevel", "quiet" "-ac", "1", CONFIG.audio_file, "-y", "-loglevel", "quiet"
]) ])
notify("Aufnahme", "Aufnahme gestartet!") notify("Aufnahme", "Aufnahme gestartet!")
@@ -260,15 +243,15 @@ class TrayApp:
print(f"Fertig:\n{text}") print(f"Fertig:\n{text}")
def reload_configurations(self): def reload_configurations(self):
global CONFIGURATION, CURRENT_PRESET global CONFIG, CURRENT_PRESET
print("Lade Einstellungen neu...") print("Lade Einstellungen neu...")
CONFIGURATION = read_configurations() CONFIG = read_configurations()
CURRENT_PRESET = CONFIGURATION["presets"][0] # Default to first preset CURRENT_PRESET = CONFIG.presets[0] # Default to first preset
# Update preset menu # Update preset menu
self.preset_group.clear() self.preset_group.clear()
self.preset_actions = [] self.preset_actions = []
for i, preset in enumerate(CONFIGURATION["presets"]): for i, preset in enumerate(CONFIG.presets):
action = QAction(preset["name"], self.menu) action = QAction(preset.name, self.menu)
action.setCheckable(True) action.setCheckable(True)
if i == 0: if i == 0:
action.setChecked(True) action.setChecked(True)
@@ -278,8 +261,8 @@ class TrayApp:
print("Einstellungen erfolgreich neu geladen.") print("Einstellungen erfolgreich neu geladen.")
def cleanup(self): def cleanup(self):
if os.path.exists(CONFIGURATION["socket_path"]): if os.path.exists(CONFIG.socket_path):
os.remove(CONFIGURATION["socket_path"]) os.remove(CONFIG.socket_path)
print("Socket sauber entfernt.") print("Socket sauber entfernt.")
def run(self): def run(self):