-
Notifications
You must be signed in to change notification settings - Fork 0
/
kllapp2.py
159 lines (135 loc) · 5.53 KB
/
kllapp2.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
from pathlib import Path
from textual.reactive import reactive
from textual import events, on
from textual.app import App, ComposeResult
from textual.message import Message
from textual.strip import Strip
from textual.widget import Widget
from textual.widgets import Static, Switch, Button, Label
from textual.geometry import Size
from textual.containers import Vertical, Horizontal
from textual.scroll_view import ScrollView
from textual.events import Key
from rich.segment import Segment
from mediaplayer import MediaPlayer
class BookScroll(ScrollView):
def __init__(
self,
source: Path,
*children: Widget,
name: str | None = None,
id: str | None = None,
classes: str | None = None,
disabled: bool = False,
) -> None:
self.text = source.read_text().splitlines()
super().__init__(
*children, name=name, id=id, classes=classes, disabled=disabled
)
self.virtual_size = Size(78, len(self.text))
class ScrolledBook(Message):
def __init__(self, origin: str, offset: int) -> None:
self.origin = origin
self.offset = offset
super().__init__()
def _handle_scroll(self, down: bool):
current = self.scroll_offset[1]
target = current + self.app.scroll_sensitivity_y * (down * 2 - 1)
self.post_message(self.ScrolledBook(self.id, target - current))
self.log(current, target)
def on_mouse_scroll_down(self, event: events.MouseScrollDown) -> None:
event.stop()
self._handle_scroll(True)
def on_mouse_scroll_up(self, event: events.MouseScrollUp) -> None:
event.stop()
self._handle_scroll(False)
def render_line(self, y: int) -> Strip:
scroll_y = self.scroll_offset[1]
y += scroll_y
segments = [Segment(self.text[y])]
strip = Strip(segments, len(self.text[y]))
return strip
class MediaPlayerControl(Static):
media_player = MediaPlayer.from_playlist((list(Path("media/piknik").glob("*.mp4"))))
curr_time = reactive(media_player.get_str_time())
def on_mount(self) -> None:
self.update_timer = self.set_interval(1 / 10, self.update_time, pause=True)
def compose(self) -> ComposeResult:
yield Button(label="⏴", id="seek_b", classes="media_button")
yield Button(label="⏯", id="play_pause", classes="media_button")
yield Button(label="⏵︎", id="seek_f", classes="media_button")
yield Button(label="⏮", id="chap_b", classes="media_button")
yield Button(label="⏭︎", id="chap_f", classes="media_button")
yield Label(
f"{self.media_player.chapter_idx + 1}/{len(self.media_player.playlist)}",
id="chapter",
)
yield Label(id="time")
def update_time(self):
if not self.media_player.is_playing:
return
self.curr_time = self.media_player.get_str_time()
def watch_curr_time(self, new_time):
self.query_one("#time", Label).update(new_time)
def update_chapter(self):
new_label = (
f"{self.media_player.chapter_idx + 1}/{len(self.media_player.playlist)}"
)
self.query_one("#chapter", Label).update(new_label)
def play_pause(self):
if self.media_player.is_playing():
self.media_player.pause()
self.update_timer.pause()
else:
self.media_player.play()
self.update_timer.resume()
def on_button_pressed(self, event: Button.Pressed) -> None:
self.curr_time = self.media_player.get_str_time()
ts = self.media_player.get_time()
seek_time = 5000
match event.button.id:
case "seek_b":
self.media_player.set_time(ts - seek_time)
case "seek_f":
self.media_player.set_time(ts + seek_time)
case "chap_b":
self.media_player.prev_item()
self.update_chapter()
case "chap_f":
self.media_player.next_item()
self.update_chapter()
case "play_pause":
self.play_pause()
class KLlApp(App):
CSS_PATH = "kllapp2.tcss"
def compose(self) -> ComposeResult:
with Horizontal():
with Vertical(id="sidebar", classes="sidebar"):
yield Static("De/Couple scroll (C)")
yield Switch(False, id="scroll_couple")
yield MediaPlayerControl(id="mediacontrol")
yield BookScroll(Path("media/piknik/picnic_utf8.txt"), id="ru")
yield BookScroll(Path("media/piknik/picnic_eng.txt"), id="en")
@on(BookScroll.ScrolledBook)
def scroll_sibling(self, event: BookScroll.ScrolledBook) -> None:
if not self.query_one("#scroll_couple", Switch).value:
return
a, b = self.query(BookScroll)
other = {a.id: b, b.id: a}
other[event.origin].scroll_relative(y=event.offset)
self.log("parent", event.origin, event.offset)
def on_key(self, event: Key) -> None:
match event.key:
case "h":
self.query_one("#seek_b", Button).action_press()
case "space":
self.query_one("#play_pause", Button).action_press()
case "l":
self.query_one("#seek_f", Button).action_press()
case "j":
self.query_one("#ru", BookScroll)._handle_scroll(down=True)
case "k":
self.query_one("#ru", BookScroll)._handle_scroll(down=False)
if __name__ == "__main__":
app = KLlApp()
app.run()