cplay-ng

simple curses audio player
git clone https://git.ce9e.org/cplay-ng.git

commit
337eab74fbe0c0ed4d5612051eacfe817145bc38
parent
b8f3b422d3a4a16e3c335b3b87bae0509836f017
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2023-11-05 09:17
lint

Diffstat

M cplay.py 77 +++++++++++++++++++++++++++++++------------------------------

1 files changed, 39 insertions, 38 deletions


diff --git a/cplay.py b/cplay.py

@@ -78,7 +78,7 @@ def str_match(query, s):
   78    78     return all(q in s.lower() for q in query.lower().split())
   79    79 
   80    80 
   81    -1 def resize(*args):
   -1    81 def resize(*_args):
   82    82     os.write(app.resize_out, b'.')
   83    83 
   84    84 
@@ -87,9 +87,10 @@ def get_socket(path):
   87    87         try:
   88    88             sock = socket.socket(family=socket.AF_UNIX)
   89    89             sock.connect(path)
   90    -1             return sock
   91    90         except (FileNotFoundError, ConnectionRefusedError):
   92    91             time.sleep(0.1)
   -1    92         else:
   -1    93             return sock
   93    94 
   94    95 
   95    96 @functools.lru_cache
@@ -148,7 +149,7 @@ class Player:
  148   149         self._proc = subprocess.Popen(
  149   150             [
  150   151                 'mpv',
  151    -1                 '--input-ipc-server=%s' % self.socket_path,
   -1   152                 f'--input-ipc-server={self.socket_path}',
  152   153                 '--idle',
  153   154                 '--audio-display=no',
  154   155                 '--replaygain=track',
@@ -334,13 +335,13 @@ class List:
  334   335                 attr |= curses.A_REVERSE
  335   336             if self.position + i == self.active:
  336   337                 attr |= curses.A_BOLD
  337    -1             item = self.format_item(item)
  338    -1             item = space_between('  ' + item, '', app.cols)
  339    -1             yield (item, attr)
  340    -1         for i in range(max(0, self.rows - len(items))):
   -1   338             s_item = self.format_item(item)
   -1   339             s_item = space_between('  ' + s_item, '', app.cols)
   -1   340             yield (s_item, attr)
   -1   341         for _i in range(max(0, self.rows - len(items))):
  341   342             yield ''
  342   343 
  343    -1     def process_key(self, key):
   -1   344     def process_key(self, key):  # noqa: C901
  344   345         if key in [curses.KEY_DOWN, 'j']:
  345   346             self.move_cursor(1)
  346   347         elif key in [curses.KEY_UP, 'k']:
@@ -460,28 +461,27 @@ class Filelist(List):
  460   461         self.rsearch_str = query
  461   462         self.set_cursor(self.cursor)
  462   463 
   -1   464     def activate(self, item):
   -1   465         ext = item.rsplit('.', 1)[-1]
   -1   466         if os.path.isdir(item):
   -1   467             self.set_path(item)
   -1   468         elif ext in AUDIO_EXTENSIONS:
   -1   469             playlist.active = -1
   -1   470             player.play(item)
   -1   471         elif ext == 'm3u':
   -1   472             playlist.load(item)
   -1   473             app.toggle_tabs()
   -1   474 
  463   475     def process_key(self, key):
  464   476         if key == 'a':
  465    -1             if not self.items:
  466    -1                 return True
  467    -1             if playlist.add(self.items[self.cursor]):
   -1   477             if self.items and playlist.add(self.items[self.cursor]):
  468   478                 self.move_cursor(1)
  469   479         elif key == 's':
  470   480             app.input.start('search: ', on_input=self.filter)
  471   481             self.filter(self.rsearch_str)
  472   482         elif key == '\n':
  473    -1             if not self.items:
  474    -1                 return True
  475    -1             item = self.items[self.cursor]
  476    -1             ext = item.rsplit('.', 1)[-1]
  477    -1             if os.path.isdir(item):
  478    -1                 self.set_path(item)
  479    -1             elif ext in AUDIO_EXTENSIONS:
  480    -1                 playlist.active = -1
  481    -1                 player.play(item)
  482    -1             elif ext == 'm3u':
  483    -1                 playlist.load(item)
  484    -1                 app.toggle_tabs()
   -1   483             if self.items:
   -1   484                 self.activate(self.items[self.cursor])
  485   485         elif key == curses.KEY_BACKSPACE:
  486   486             if self.rsearch_str:
  487   487                 self.set_path(self.path)
@@ -590,7 +590,7 @@ class Playlist(List):
  590   590 
  591   591     def add_dir(self, path):
  592   592         count = 0
  593    -1         for p, ext, is_dir in listdir(path):
   -1   593         for p, _ext, _is_dir in listdir(path):
  594   594             count += self.add(p, recursive=True)
  595   595         return count
  596   596 
@@ -598,8 +598,8 @@ class Playlist(List):
  598   598         count = 0
  599   599         dirname = os.path.dirname(path)
  600   600         with open(path, errors='replace') as fh:
  601    -1             for line in fh:
  602    -1                 line = line.strip()
   -1   601             for _line in fh:
   -1   602                 line = _line.strip()
  603   603                 if not line or line[0] == '#':
  604   604                     continue
  605   605                 if not re.match(r'^(/|https?://)', line):
@@ -608,7 +608,7 @@ class Playlist(List):
  608   608                 count += 1
  609   609         return count
  610   610 
  611    -1     def add(self, path, recursive=False):
   -1   611     def add(self, path, *, recursive=False):
  612   612         ext = path.rsplit('.', 1)[-1]
  613   613         if os.path.isdir(path):
  614   614             return self.add_dir(path)
@@ -633,10 +633,10 @@ class Playlist(List):
  633   633                     fh.write('%s\n' % item)
  634   634                 self.path = path
  635   635                 self.items_written = self.items.copy()
  636    -1         except IOError:
   -1   636         except OSError:
  637   637             pass
  638   638 
  639    -1     def process_key(self, key):
   -1   639     def process_key(self, key):  # noqa: C901
  640   640         if key == 'm':
  641   641             self.move_item(1)
  642   642         elif key == 'M':
@@ -724,13 +724,13 @@ class Application:
  724   724         else:
  725   725             status = ''
  726   726 
  727    -1         counter = '%s / %s' % (
   -1   727         counter = ' / '.join([
  728   728             format_time(player.position),
  729   729             format_time(player.length),
  730    -1         )
   -1   730         ])
  731   731         yield space_between(status, counter, self.cols)
  732   732 
  733    -1     def render(self, force=False):
   -1   733     def render(self, *, force=False):
  734   734         lines = list(self._render())
  735   735         try:
  736   736             for i, line in enumerate(lines):
@@ -743,8 +743,9 @@ class Application:
  743   743                 screen.move(i, 0)
  744   744                 screen.clrtoeol()
  745   745                 if isinstance(line, str):
  746    -1                     line = (line, 0)
  747    -1                 screen.insstr(i, 0, *line)
   -1   746                     screen.insstr(i, 0, line, 0)
   -1   747                 else:
   -1   748                     screen.insstr(i, 0, *line)
  748   749             # make sure cursor is in a meaningful position for a11y
  749   750             screen.move(self.tab.cursor - self.tab.position + 2, 0)
  750   751             screen.refresh()
@@ -752,7 +753,7 @@ class Application:
  752   753             pass
  753   754         self.old_lines = lines
  754   755 
  755    -1     def process_key(self, key):
   -1   756     def process_key(self, key):  # noqa: C901
  756   757         if self.input.process_key(key):
  757   758             pass
  758   759         elif self.tab.process_key(key):
@@ -790,7 +791,7 @@ class Application:
  790   791                 player.finish_seek()
  791   792 
  792   793                 timeout = 0.5 if player.is_playing else None
  793    -1                 for key, mask in sel.select(timeout):
   -1   794                 for key, _mask in sel.select(timeout):
  794   795                     if key.fileobj is self.resize_in:
  795   796                         os.read(self.resize_in, 8)
  796   797                         self.on_resize()
@@ -816,10 +817,10 @@ screen = curses.initscr()
  816   817 
  817   818 
  818   819 def main():
  819    -1     screen.keypad(True)
   -1   820     screen.keypad(flag=True)
  820   821     curses.cbreak()
  821   822     curses.noecho()
  822    -1     curses.meta(True)
   -1   823     curses.meta(flag=True)
  823   824     curses.curs_set(0)
  824   825 
  825   826     signal.signal(signal.SIGWINCH, resize)