Skip to content

Commit

Permalink
Merge pull request #41 from Jugran/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
Jugran authored Aug 20, 2024
2 parents 8231559 + 80536d2 commit d3b6a4e
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 7 deletions.
2 changes: 1 addition & 1 deletion lyrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

CONFIG_PATH = Path.home().joinpath('.config', 'lyrics-in-terminal','lyrics.cfg')

__version__ = '1.5.1-dev'
__version__ = '1.6.0-dev'

if not CONFIG_PATH.exists():
from shutil import copy
Expand Down
4 changes: 4 additions & 0 deletions lyrics/lyrics.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mpd_port=6600
mpd_pass=
#colors
#offset=1
statusbar=on

[BINDINGS]
up=arrow_up
Expand All @@ -31,6 +32,9 @@ autoswitchtoggle=a

delete=d
edit=e
find=/
find-next=n
find-prev=p

help=h

Expand Down
158 changes: 153 additions & 5 deletions lyrics/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def input(self, window, key):

elif key == self.binds['delete']:
if window.player.track.delete_lyrics():
window.stdscr.addstr(window.height - 1, window.width - 10,
window.stdscr.addstr(window.height - 1, 1,
' Deleted ', curses.A_REVERSE)
elif key == self.binds['help']:
window.stdscr.erase()
Expand All @@ -64,11 +64,13 @@ def input(self, window, key):
window.current_pos = 0
window.player.refresh(cache=True)
window.update_track()
elif key == self.binds['find']:
window.find()

# autoswitch toggle
elif key == self.binds['autoswitchtoggle']:
window.player.autoswitch = not window.player.autoswitch
window.stdscr.addstr(window.height - 1, window.width - 18,
window.stdscr.addstr(window.height - 1, 1,
f" Autoswitch: {'on' if window.player.autoswitch else 'off'} ", curses.A_REVERSE)

class HelpPage:
Expand Down Expand Up @@ -134,6 +136,7 @@ def main(self):

class Window:
def __init__(self, stdscr, player, timeout):
self.options = Config('OPTIONS')
self.stdscr = stdscr
self.height, self.width = stdscr.getmaxyx()
self.player = player
Expand All @@ -143,9 +146,11 @@ def __init__(self, stdscr, player, timeout):
self.pad_offset = 1
self.text_padding = 5
self.keys = Key()
self.find_position = 0
self.timeout = timeout

curses.use_default_colors()
self.stdscr.timeout(timeout)
self.stdscr.timeout(self.timeout)
self.set_up()

def set_up(self):
Expand All @@ -170,7 +175,15 @@ def set_titlebar(self):
self.stdscr.addstr(1, 1, track_info[1],
curses.A_REVERSE | curses.A_BOLD | curses.A_DIM)
self.stdscr.addstr(2, 1, track_info[2], curses.A_REVERSE)


def set_statusbar(self):
if self.options['statusbar'] == 'on':
lines = self.player.track.length
if self.current_pos < 0:
self.current_pos = 0
pct_progress = f' {round(self.current_pos * 100 / lines) + 1}% '
self.stdscr.insstr(self.height - 1, self.width - len(pct_progress), pct_progress, curses.A_DIM)

def set_offset(self):
if self.player.track.alignment == 0:
# center align
Expand All @@ -181,9 +194,10 @@ def set_offset(self):
self.pad_offset = (self.width - self.player.track.width)

def scroll_down(self, step=1):
if self.current_pos < self.player.track.length - (self.height * 0.5):
if self.current_pos < self.player.track.length - step:
self.current_pos += step
else:
self.current_pos = self.player.track.length - 1
self.stdscr.addstr(self.height - 1, 1, 'END', curses.A_REVERSE)

def scroll_up(self, step=1):
Expand All @@ -194,6 +208,139 @@ def scroll_up(self, step=1):
self.stdscr.clrtoeol()
self.current_pos -= step

def find_check_keys(self, key=None, lines_map=[]):
if key == self.keys.binds['find-next']:
self.stdscr.addstr(self.height - 1, self.width - 3, 'n ')
self.stdscr.clrtoeol()
# reached end of matches, loop back to start
if self.find_position + 1 >= len(lines_map):
self.find_position = 0
else:
self.find_position += 1
return True
elif key == self.keys.binds['find-prev']:
self.stdscr.addstr(self.height - 1, self.width - 3, 'p ')
self.stdscr.clrtoeol()
if self.find_position - 1 < 0:
self.find_position = len(lines_map) - 1
else:
self.find_position -= 1
return True
# other keys for more accessibility
elif key == self.keys.binds['down']:
self.scroll_down()
elif key == self.keys.binds['up']:
self.scroll_up()
elif key == self.keys.binds['step-down']:
self.scroll_down(self.keys.binds['step-size'])
elif key == self.keys.binds['step-up']:
self.scroll_up(self.keys.binds['step-size'])
elif key == self.keys.binds['find']:
self.find()
return False

def find(self):
# wait for input
self.stdscr.timeout(-1)
prompt = ':'
self.stdscr.move(self.height - 1, 0)
self.stdscr.clrtoeol()
self.stdscr.addstr(self.height - 1, self.pad_offset, prompt)
self.set_statusbar()
# show cursor and key presses during find
curses.echo()
curses.curs_set(1)

# (y, x, input max length), case-insensitive
find_string = self.stdscr.getstr(self.height - 1, len(prompt) + self.pad_offset, 100)
find_string = find_string.decode(encoding="utf-8").strip()

# hide cursor and key presses
curses.curs_set(0)
curses.noecho()

if find_string:
# use word wrap which covers both wrap/nowrap and ensures line count is accurate
text = self.player.track.get_text(wrap=True, width=self.width - self.text_padding)
lines = text.split('\n')

# [0,9,10,14] list of lines that contain a match
lines_map = []
for line_num, line in enumerate(lines):
# case-insensitive match
if find_string.lower() in line.lower():
lines_map.append(line_num)

if len(lines_map) > 0:

# continue search from current position
for line in lines_map:
# >= causes us to stay on the current line for a new search
if line >= self.current_pos:
self.find_position = lines_map.index(line)
break
# otherwise loop back to the start
else:
self.find_position = 0

while True:
# update current position based on where we are at in the find
self.current_pos = lines_map[self.find_position]

# duplicated from main() to manually refresh on find
self.stdscr.clear()
self.set_titlebar()
self.stdscr.refresh()
self.scroll_pad.refresh(self.current_pos, 0, 4, self.pad_offset, self.height - 2, self.width - 1)

# find & status bar output
find_string_output = f' {find_string} '
find_count_output = f" {self.find_position + 1}/{len(lines_map)} "
self.stdscr.addstr(self.height - 1, self.pad_offset, find_string_output, curses.A_REVERSE)
self.stdscr.insstr(self.height - 1, self.pad_offset + len(find_string_output) + 1, find_count_output)
# multiple matches, show next/prev
if len(lines_map) > 1:
help_output = f"[{chr(self.keys.binds['find-next'])}]=next, [{chr(self.keys.binds['find-prev'])}]=prev"
self.stdscr.addstr(self.height - 1, self.pad_offset + len(find_string_output) + len(find_count_output) + 2, help_output)
self.set_statusbar()

# highlight found text
line_text = lines[self.current_pos]
# case-insensitive, [4, 5, 21]
found_index_list = [i for i in range(len(line_text)) if line_text.lower().startswith(find_string.lower(), i)]
# loop over each character in the line, highlight found strings
highlight_end = -1
for cpos, char in enumerate(line_text):
attr = curses.A_NORMAL
# start of found string
if cpos in found_index_list:
highlight_end = cpos + len(find_string)
attr = curses.A_REVERSE
# inside string
elif highlight_end > cpos:
attr = curses.A_REVERSE
self.stdscr.addch(4, self.pad_offset + cpos, char, attr)

# after finding a match in a line, stop, wait for input
self.stdscr.timeout(10000)
key = self.stdscr.getch()
result = self.find_check_keys(key, lines_map)
if not result:
break

else:
output = ' not found '
self.stdscr.insstr(self.height - 1, self.pad_offset + len(prompt) + len(find_string) + 2, output, curses.A_REVERSE)
self.set_statusbar()
# timeout or key press
self.stdscr.timeout(5000)
key = self.stdscr.getch()
self.find_check_keys(key, lines_map)

# clear search line
self.stdscr.clear()
self.stdscr.timeout(self.timeout)

def update_track(self):
self.stdscr.clear()
self.scroll_pad.clear()
Expand Down Expand Up @@ -228,6 +375,7 @@ def main(self):
self.keys.input(self, key)

self.set_titlebar()
self.set_statusbar()
self.stdscr.refresh()
self.scroll_pad.refresh(self.current_pos, 0, 4,
self.pad_offset, self.height - 2, self.width - 1)
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ def updateConfigFile(self):
packages=find_packages(),
entry_points={
'console_scripts': [
'lyrics = lyrics.lyrics_in_terminal:main'
'lyrics = lyrics.lyrics_in_terminal:main',
'lyt = lyrics.lyrics_in_terminal:main'
]
},
classifiers=[
Expand Down

0 comments on commit d3b6a4e

Please sign in to comment.