python-clipboard-speaker

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit d637a1cf0cad7e64140bfaaef0c2f071c672b88b
parent 1ff7225be540f6ae95db481660a1c3e2dc7f9fc2
Author: Yuval Langer <yuvallangerontheroad@gmail.com>
Date:   Sat, 27 Nov 2021 03:35:56 +0200

Turn it a proper Python package.

Diffstat:
MCHANGELOG.md | 14+++++++++++---
MREADME.md | 44++++++++++++++++++++++++++++++++++++++------
Dclipboard-speaker | 63---------------------------------------------------------------
Dclipboard-speaker-kill | 14--------------
Aclipboard_speaker/__init__.py | 0
Aclipboard_speaker/clipboard_speaker.py | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aclipboard_speaker/clipboard_speaker_kill.py | 19+++++++++++++++++++
Asetup.py | 25+++++++++++++++++++++++++
8 files changed, 160 insertions(+), 86 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -1,15 +1,23 @@ +## 2021-11-27 + +Turn it into a Python package by adding a setup.py and sticking the files into +a module. + ## 2021-11-26 More Python script experimentations. I've started with a shell threeliner using ready made packages and I ended up reimplementing them badly. Something something UNIX philosophy. -There is now a `~/.clipboard-speaker/words-per-minute` setting file for the Python version. +There is now a `~/.clipboard-speaker/words-per-minute` setting file for the +Python version. Switched (again) to FIFO so you can: - 10. Marking text and press the keybinding that runs clipboard-speaker as usual. While clipboard-speaker is running and speaking, you can - 20. Mark more text and press the keybindings for it to feed more text into clipboard-speaker's text buffer, and if you wish + 10. Marking text and press the keybinding that runs clipboard-speaker as + usual. While clipboard-speaker is running and speaking, you can + 20. Mark more text and press the keybindings for it to feed more text into + clipboard-speaker's text buffer, and if you wish 30. GOTO 20. ## 2021-11-25 diff --git a/README.md b/README.md @@ -18,13 +18,45 @@ Have a look at the <a href="CHANGELOG.md">CHANGELOG.md</a> file. ### Installation: -1. Install the infrastructure: - `apt install espeak-ng xsel python3` -2. Copy the two executable scripts into your home bin directory: - `cp clipboard-speaker clipboard-speaker-kill ~/bin/` -3. Add the key bindings in (if you use Gnome. If you don't, look up how to +#### Install the infrastructure: + +##### On Debian based systems: + +`apt install espeak-ng xsel python3` + +#### Install the script files: + +##### Manually: + +Copy the two executable scripts into your home bin directory: + +``` +cp clipboard_speaker/clipboard_speaker.py ~/bin/clipboard-speaker +cp clipboard_speaker/clipboard_speaker_kill.py ~/bin/clipboard-speaker-kill +``` + +##### pip: + +``` +pip install --user . +``` + +After which the script files are located, at least here in my system, under +`~/.local/bin/` as `~/.local/bin/clipboard-speak` and +`~/.local/bin/clipboard-speak-kill`. + +#### Map your keybindings: + +##### On Gnome: + +Add the key bindings in (if you use Gnome. If you don't, look up how to assign scripts to keybindings in your own window manager): - `Gnome Settings → Keyboard Shortcuts → All the way down and press the + button` + +``` +Gnome Settings + → Keyboard Shortcuts + → All the way down and press the + button +``` ### Usage: diff --git a/clipboard-speaker b/clipboard-speaker @@ -1,63 +0,0 @@ -#!/usr/bin/env python3 - -import os -from pathlib import Path -from subprocess import Popen, PIPE - -DEFAULT_WORDS_PER_MINUTE = "175" - -CLIPBOARD_SPEAKER_PATH = Path.home() / ".clipboard-speaker" -PID_FILE_PATH = CLIPBOARD_SPEAKER_PATH / "pid" -FIFO_FILE_PATH = CLIPBOARD_SPEAKER_PATH / "fifo" - -WORDS_PER_MINUTE_PATH = CLIPBOARD_SPEAKER_PATH / "words-per-minute" - - -def get_words_per_minute() -> str: - if not WORDS_PER_MINUTE_PATH.exists(): - with WORDS_PER_MINUTE_PATH.open("w") as words_per_minute_file: - words_per_minute_file.write(DEFAULT_WORDS_PER_MINUTE) - return DEFAULT_WORDS_PER_MINUTE - - with WORDS_PER_MINUTE_PATH.open("r") as words_per_minute_file: - words_per_minute = words_per_minute_file.read().strip() - - return words_per_minute - - -if __name__ == "__main__": - words_per_minute = get_words_per_minute() - try: - os.mkfifo(FIFO_FILE_PATH, mode=0o600) - except FileExistsError: - pass - - # https://stackoverflow.com/questions/63132778/how-to-use-fifo-named-pipe-as-stdin-in-popen-python - fifo_read_file = os.open(FIFO_FILE_PATH, os.O_RDONLY | os.O_NONBLOCK) - fifo_write_file = os.open(FIFO_FILE_PATH, os.O_WRONLY) - - Popen( - ["xsel", "-p"], - stdout=fifo_write_file, - ) - os.close(fifo_write_file) - - if not PID_FILE_PATH.exists(): - speak_ng_process = Popen( - [ - "speak-ng", - f"-s {words_per_minute}", - ], - stdin=fifo_read_file, - ) - os.close(fifo_read_file) - - with PID_FILE_PATH.open("w") as pid_file: - pid_file.write(str(speak_ng_process.pid)) - - try: - speak_ng_process.wait() - except KeyboardInterrupt as e: - os.remove(PID_FILE_PATH) - - os.remove(PID_FILE_PATH) diff --git a/clipboard-speaker-kill b/clipboard-speaker-kill @@ -1,14 +0,0 @@ -#!/usr/bin/env python3 - -import os -import pathlib -import signal - -CLIPBOARD_SPEAKER_PATH = pathlib.Path.home() / ".clipboard-speaker" -PID_FILE_PATH = CLIPBOARD_SPEAKER_PATH / "pid" - -if __name__ == "__main__": - with PID_FILE_PATH.open("r") as pid_file: - pid = int(pid_file.read()) - os.remove(PID_FILE_PATH) - os.kill(pid, signal.SIGTERM) diff --git a/clipboard_speaker/__init__.py b/clipboard_speaker/__init__.py diff --git a/clipboard_speaker/clipboard_speaker.py b/clipboard_speaker/clipboard_speaker.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +import os +from pathlib import Path +from subprocess import Popen, PIPE + +DEFAULT_WORDS_PER_MINUTE = "175" + +CLIPBOARD_SPEAKER_PATH = Path.home() / ".clipboard-speaker" +PID_FILE_PATH = CLIPBOARD_SPEAKER_PATH / "pid" +FIFO_FILE_PATH = CLIPBOARD_SPEAKER_PATH / "fifo" + +WORDS_PER_MINUTE_PATH = CLIPBOARD_SPEAKER_PATH / "words-per-minute" + + +def get_words_per_minute() -> str: + if not WORDS_PER_MINUTE_PATH.exists(): + with WORDS_PER_MINUTE_PATH.open("w") as words_per_minute_file: + words_per_minute_file.write(DEFAULT_WORDS_PER_MINUTE) + return DEFAULT_WORDS_PER_MINUTE + + with WORDS_PER_MINUTE_PATH.open("r") as words_per_minute_file: + words_per_minute = words_per_minute_file.read().strip() + + return words_per_minute + + +def main() -> None: + words_per_minute = get_words_per_minute() + try: + os.mkfifo(FIFO_FILE_PATH, mode=0o600) + except FileExistsError: + pass + + # https://stackoverflow.com/questions/63132778/how-to-use-fifo-named-pipe-as-stdin-in-popen-python + fifo_read_file = os.open(FIFO_FILE_PATH, os.O_RDONLY | os.O_NONBLOCK) + fifo_write_file = os.open(FIFO_FILE_PATH, os.O_WRONLY) + + Popen( + ["xsel", "-p"], + stdout=fifo_write_file, + ) + os.close(fifo_write_file) + + if not PID_FILE_PATH.exists(): + speak_ng_process = Popen( + [ + "speak-ng", + f"-s {words_per_minute}", + ], + stdin=fifo_read_file, + ) + os.close(fifo_read_file) + + with PID_FILE_PATH.open("w") as pid_file: + pid_file.write(str(speak_ng_process.pid)) + + try: + speak_ng_process.wait() + except KeyboardInterrupt as e: + os.remove(PID_FILE_PATH) + + os.remove(PID_FILE_PATH) + + +if __name__ == "__main__": + main() diff --git a/clipboard_speaker/clipboard_speaker_kill.py b/clipboard_speaker/clipboard_speaker_kill.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +import os +import pathlib +import signal + +CLIPBOARD_SPEAKER_PATH = pathlib.Path.home() / ".clipboard-speaker" +PID_FILE_PATH = CLIPBOARD_SPEAKER_PATH / "pid" + + +def main() -> None: + with PID_FILE_PATH.open("r") as pid_file: + pid = int(pid_file.read()) + os.remove(PID_FILE_PATH) + os.kill(pid, signal.SIGTERM) + + +if __name__ == "__main__": + main() diff --git a/setup.py b/setup.py @@ -0,0 +1,25 @@ +from setuptools import setup, find_packages + +with open("README.md") as f: + readme = f.read() + +with open("LICENSE") as f: + license = f.read() + +setup( + name="clipboard-speaker", + version="1.0.0", + description="Read aloud selected text.", + long_description=readme, + author="Yuval Langer", + author_email="yuval.langer@gmail.com", + url="https://gitlab.com/yuvallangerontheroad/clipboard-speaker", + license=license, + entry_points={ + "console_scripts": [ + "clipboard-speaker=clipboard_speaker.clipboard_speaker:main", + "clipboard-speaker-kill=clipboard_speaker.clipboard_speaker_kill:main", + ], + }, + packages=["clipboard_speaker"], +)