Compare commits
10 Commits
0352afae50
...
4d1a2693da
Author | SHA1 | Date | |
---|---|---|---|
4d1a2693da | |||
d345dcf6d1 | |||
858e29a4da | |||
86c52e35b6 | |||
62228bb061 | |||
2ed799d9e9 | |||
dfdc92a98b | |||
a5c1a49248 | |||
3e69c99523 | |||
bae5ebda48 |
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -18,3 +18,8 @@
|
|||
__pycache__
|
||||
**.ipynb_checkpoints
|
||||
|
||||
|
||||
|
||||
# Added by cargo
|
||||
|
||||
/target
|
||||
|
|
12
install
Executable file
12
install
Executable file
|
@ -0,0 +1,12 @@
|
|||
#! /bin/sh
|
||||
#
|
||||
# install.sh
|
||||
# Copyright (C) 2023 gum <gum@gumvy>
|
||||
#
|
||||
# Distributed under terms of the MIT license.
|
||||
#
|
||||
|
||||
python -m venv --prompt break-timer .venv
|
||||
. .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
20
makefile
Normal file
20
makefile
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
pwd = $(shell pwd)
|
||||
name = $(notdir $(pwd))
|
||||
symlink = ${HOME}/.local/bin/break-timer
|
||||
venv = $(pwd)/.venv/bin/python
|
||||
|
||||
$(symlink): $(venv)
|
||||
echo "#!/bin/sh\n$(venv) $(pwd)/src/main.py \"\$$@\"" > $(symlink)
|
||||
chmod +x $(symlink)
|
||||
|
||||
$(venv): requirements.txt
|
||||
python -m venv --prompt $(name) .venv
|
||||
. .venv/bin/activate; \
|
||||
pip install --upgrade pip; \
|
||||
pip install -r requirements.txt
|
||||
|
||||
clean:
|
||||
rm -r .venv
|
||||
rm $(symlink)
|
||||
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
|||
pyright
|
189
src/main.py
Executable file
189
src/main.py
Executable file
|
@ -0,0 +1,189 @@
|
|||
#! /home/gum/p/break/.venv/bin/python
|
||||
# vim:fenc=utf-8
|
||||
#
|
||||
# Copyright © 2023 gum <gum@gums-lappy>
|
||||
#
|
||||
# Distributed under terms of the MIT license.
|
||||
|
||||
"""
|
||||
Play my playlist every few minutes.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
from argparse import ArgumentParser
|
||||
from datetime import datetime
|
||||
from select import select
|
||||
from subprocess import DEVNULL, Popen, run
|
||||
from termios import TCIOFLUSH, tcflush
|
||||
from time import sleep, time
|
||||
|
||||
WINDOW_TITLE = ""
|
||||
|
||||
|
||||
def generate_interval(work_time: int, break_time: int):
|
||||
"""
|
||||
Generate the interval between plays in seconds.
|
||||
"""
|
||||
if work_time and break_time:
|
||||
while True:
|
||||
yield work_time
|
||||
yield break_time
|
||||
else:
|
||||
# pomodoro scheme
|
||||
while True:
|
||||
yield 60 * 25
|
||||
for _ in range(3):
|
||||
yield 60 * 5
|
||||
yield 60 * 25
|
||||
yield 60 * 30
|
||||
|
||||
def play_playlist(playlist):
|
||||
"""
|
||||
Play the playlist.
|
||||
"""
|
||||
return Popen(
|
||||
["mpv", "--shuffle", "--no-video", playlist], stdout=DEVNULL, stderr=DEVNULL
|
||||
)
|
||||
|
||||
def test_playlist(playlist, duration=None):
|
||||
print(
|
||||
"testing playlist, press enter to start if you hear the playlist",
|
||||
end="",
|
||||
flush=True,
|
||||
)
|
||||
if duration is None:
|
||||
duration = float("inf")
|
||||
play = play_playlist(playlist)
|
||||
focus=bool(WINDOW_TITLE) and duration is None
|
||||
wait_or_enter(duration, i3_focus=focus)
|
||||
play.terminate()
|
||||
|
||||
def wait_or_enter(duration, i3_focus=False):
|
||||
"""
|
||||
A timer that will stop if the user presses enter.
|
||||
The i3_focus flag will focus the i3 window with the title "break" to prevent the user from doing other things while the timer lasts.
|
||||
"""
|
||||
stop = ""
|
||||
start = time()
|
||||
if i3_focus:
|
||||
run(
|
||||
["i3-msg", f'[title="{WINDOW_TITLE}"] focus, fullscreen'],
|
||||
stdout=DEVNULL,
|
||||
stderr=DEVNULL,
|
||||
)
|
||||
while not stop and (time() - start < duration): # Run until flag is True
|
||||
if i3_focus:
|
||||
run(
|
||||
["i3-msg", f'[title="{WINDOW_TITLE}"] focus'],
|
||||
stdout=DEVNULL,
|
||||
stderr=DEVNULL,
|
||||
)
|
||||
stop, _, _ = select([sys.stdin], [], [], 1)
|
||||
|
||||
if i3_focus:
|
||||
run(
|
||||
["i3-msg", f'[title="{WINDOW_TITLE}"] move to scratchpad'],
|
||||
stdout=DEVNULL,
|
||||
stderr=DEVNULL,
|
||||
)
|
||||
|
||||
if time() - start < duration:
|
||||
return sys.stdin.readline().strip()
|
||||
else:
|
||||
tcflush(sys.stdin, TCIOFLUSH)
|
||||
print(flush=True)
|
||||
return
|
||||
|
||||
def write_break_message(msg):
|
||||
today = datetime.now().strftime("%Y-%m-%d")
|
||||
minute = datetime.now().strftime("%H:%M")
|
||||
msg = f"{minute} BREAK {msg}"
|
||||
with open(f"/home/gum/z/break/{today}.txt", "a") as f:
|
||||
print(msg)
|
||||
f.write(msg + "\n")
|
||||
|
||||
def main(playlist, work_time, break_time):
|
||||
interval = generate_interval(work_time, break_time)
|
||||
while True:
|
||||
# work
|
||||
i = next(interval)
|
||||
print("work for", i // 60, "minutes", end="", flush=True)
|
||||
wait_or_enter(i)
|
||||
|
||||
# break
|
||||
i = next(interval)
|
||||
print("break for", i // 60, "minutes", end=" ", flush=True)
|
||||
msg = wait_or_enter(i, i3_focus=bool(WINDOW_TITLE))
|
||||
|
||||
# start music to signal the end of a break.
|
||||
if not msg:
|
||||
print("> ", end="", flush=True)
|
||||
play = play_playlist(playlist)
|
||||
msg = wait_or_enter(float("inf"), i3_focus=bool(WINDOW_TITLE))
|
||||
play.terminate()
|
||||
|
||||
write_break_message(msg)
|
||||
|
||||
def timer(playlist, duration):
|
||||
second = datetime.now().strftime("%H:%M:%S")
|
||||
print(second, "wait for", duration, "seconds")
|
||||
msg = wait_or_enter(duration, i3_focus=bool(WINDOW_TITLE))
|
||||
|
||||
# start music to signal the end of a break.
|
||||
if msg is None:
|
||||
print("timer is over, press enter to quit", end="", flush=True)
|
||||
play = play_playlist(playlist)
|
||||
wait_or_enter(float("inf"), i3_focus=bool(WINDOW_TITLE))
|
||||
play.terminate()
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--playlist",
|
||||
default=os.path.expanduser('~') + "/m",
|
||||
help="path to the folder with the playlist to play",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--work-time",
|
||||
default=None,
|
||||
type=int,
|
||||
help="Interval between plays in minutes, if empty I use the pomodoro scheme.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--break-time",
|
||||
default=None,
|
||||
type=int,
|
||||
help="Interval between plays in minutes, if empty I use the pomodoro scheme.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--window-title",
|
||||
default=None,
|
||||
type=str,
|
||||
help="window title of this program, to prevent the user from doing other things during a break.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--once",
|
||||
default=None,
|
||||
type=int,
|
||||
help="set true for this to be a single timer",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--test-sound",
|
||||
action="store_true",
|
||||
help="test the sound before starting the timer",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
WINDOW_TITLE = args.window_title
|
||||
|
||||
if args.test_sound:
|
||||
test_playlist(args.playlist)
|
||||
elif args.once is None:
|
||||
# For some reason the first time I play the playlist it doesn't work.
|
||||
# This is a workaround.
|
||||
test_playlist(args.playlist, 10)
|
||||
if args.once:
|
||||
timer(args.playlist, args.once)
|
||||
else:
|
||||
main(args.playlist, args.work_time, args.break_time)
|
Loading…
Reference in New Issue
Block a user