Fix the bug when the playlist is shorter than the screen
This commit is contained in:
parent
c1b0652078
commit
fc6b4a0c51
|
@ -48,7 +48,7 @@ Keyboard control
|
||||||
^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
+--------------+---------------------------------------------+
|
+--------------+---------------------------------------------+
|
||||||
| Key | Action |
|
| Key | Action |
|
||||||
+==============+=============================================+
|
+==============+=============================================+
|
||||||
| Return | Start playing |
|
| Return | Start playing |
|
||||||
+--------------+---------------------------------------------+
|
+--------------+---------------------------------------------+
|
||||||
|
@ -72,9 +72,9 @@ Keyboard control
|
||||||
+--------------+---------------------------------------------+
|
+--------------+---------------------------------------------+
|
||||||
| Right, ``l`` | Seek forward 5 seconds |
|
| Right, ``l`` | Seek forward 5 seconds |
|
||||||
+--------------+---------------------------------------------+
|
+--------------+---------------------------------------------+
|
||||||
| Home | Move to the begin of the list |
|
| Home | Move to the beginning of the playlist |
|
||||||
+--------------+---------------------------------------------+
|
+--------------+---------------------------------------------+
|
||||||
| End | Move to the end of the list |
|
| End | Move to the end of the playlist |
|
||||||
+--------------+---------------------------------------------+
|
+--------------+---------------------------------------------+
|
||||||
| Page Up | Move a single page up |
|
| Page Up | Move a single page up |
|
||||||
+--------------+---------------------------------------------+
|
+--------------+---------------------------------------------+
|
||||||
|
|
105
comp
105
comp
|
@ -68,14 +68,15 @@ def getlink(entry):
|
||||||
ie_key=entry.get('ie_key')).get('webpage_url')
|
ie_key=entry.get('ie_key')).get('webpage_url')
|
||||||
|
|
||||||
|
|
||||||
|
def choose_from(mode1):
|
||||||
|
if mode1 == 'all': return entries
|
||||||
|
else: return [entry for entry in entries if entry.setdefault(mode1, False)]
|
||||||
|
|
||||||
|
|
||||||
def playlist(mode):
|
def playlist(mode):
|
||||||
"""Return a generator of entries to be played."""
|
"""Return a generator of entries to be played."""
|
||||||
action, choose_from = mode.split('-')
|
action = mode.split('-')[0]
|
||||||
if choose_from == 'all':
|
entries2play = choose_from(mode.split('-')[1])
|
||||||
entries2play = entries
|
|
||||||
else:
|
|
||||||
entries2play = [entry for entry in entries
|
|
||||||
if entry.setdefault(choose_from, False)]
|
|
||||||
# Somehow yield have to be used instead of returning a generator
|
# Somehow yield have to be used instead of returning a generator
|
||||||
if action == 'play':
|
if action == 'play':
|
||||||
for entry in entries2play: yield entry
|
for entry in entries2play: yield entry
|
||||||
|
@ -87,13 +88,12 @@ def playlist(mode):
|
||||||
|
|
||||||
def play():
|
def play():
|
||||||
for entry in playlist(mode):
|
for entry in playlist(mode):
|
||||||
idx = entries.index(entry)
|
setno(entries, ['playing'])
|
||||||
entries[idx]['playing'] = True
|
reprint(stdscr, entries[start : start+curses.LINES-3])
|
||||||
|
entries[entries.index(entry)]['playing'] = True
|
||||||
reprint(stdscr, entries[start : start+curses.LINES-3])
|
reprint(stdscr, entries[start : start+curses.LINES-3])
|
||||||
mp.play(getlink(entry))
|
mp.play(getlink(entry))
|
||||||
mp.wait_for_playback()
|
mp.wait_for_playback()
|
||||||
entries[idx]['playing'] = False
|
|
||||||
reprint(stdscr, entries[start : start+curses.LINES-3])
|
|
||||||
|
|
||||||
|
|
||||||
def secpair2hhmmss(pos, duration):
|
def secpair2hhmmss(pos, duration):
|
||||||
|
@ -116,16 +116,16 @@ def update_status(stdscr, mp, message='', msgattr=curses.A_NORMAL):
|
||||||
' ' if mp._get_property('vid') == 'no' else 'V')
|
' ' if mp._get_property('vid') == 'no' else 'V')
|
||||||
if left != ' ':
|
if left != ' ':
|
||||||
left += ' | ' if mp._get_property('pause', bool) else ' > '
|
left += ' | ' if mp._get_property('pause', bool) else ' > '
|
||||||
stdscr.addstr(curses.LINES - 2, 0, left, curses.color_pair(8))
|
stdscr.addstr(curses.LINES - 2, 0, left, curses.color_pair(14))
|
||||||
title_len = curses.COLS - len(left + right)
|
title_len = curses.COLS - len(left + right)
|
||||||
center = mp._get_property('media-title').ljust(title_len)[:title_len]
|
center = mp._get_property('media-title').ljust(title_len)[:title_len]
|
||||||
stdscr.addstr(curses.LINES - 2, len(left), center,
|
stdscr.addstr(curses.LINES - 2, len(left), center,
|
||||||
curses.color_pair(8) | curses.A_BOLD)
|
curses.color_pair(14) | curses.A_BOLD)
|
||||||
stdscr.addstr(curses.LINES - 2, len(left + center), right,
|
stdscr.addstr(curses.LINES - 2, len(left + center), right,
|
||||||
curses.color_pair(8))
|
curses.color_pair(14))
|
||||||
else:
|
else:
|
||||||
stdscr.addstr(curses.LINES - 2, 0, right.rjust(curses.COLS),
|
stdscr.addstr(curses.LINES - 2, 0, right.rjust(curses.COLS),
|
||||||
curses.color_pair(8))
|
curses.color_pair(14))
|
||||||
stdscr.move(curses.LINES - 1, 0)
|
stdscr.move(curses.LINES - 1, 0)
|
||||||
stdscr.clrtoeol()
|
stdscr.clrtoeol()
|
||||||
stdscr.addstr(curses.LINES - 1, 0, message, msgattr)
|
stdscr.addstr(curses.LINES - 1, 0, message, msgattr)
|
||||||
|
@ -160,21 +160,30 @@ def reprint(stdscr, entries2print):
|
||||||
update_status(stdscr, mp)
|
update_status(stdscr, mp)
|
||||||
|
|
||||||
|
|
||||||
|
def initprint(stdscr, entries):
|
||||||
|
"""Print initial content."""
|
||||||
|
global start, y
|
||||||
|
start, y = 0, 1
|
||||||
|
if not entries:
|
||||||
|
return
|
||||||
|
setno(entries, ['current', 'error', 'playing', 'selected'])
|
||||||
|
entries[0]['current'] = True
|
||||||
|
reprint(stdscr, entries[:curses.LINES-3])
|
||||||
|
|
||||||
|
|
||||||
def move(stdscr, entries, y, delta):
|
def move(stdscr, entries, y, delta):
|
||||||
global start
|
global start
|
||||||
if start + y + delta < 1:
|
if start + y + delta < 1:
|
||||||
if start + y == 1:
|
if start + y == 1:
|
||||||
return 1
|
return 1
|
||||||
start = 0
|
|
||||||
setno(entries, ['current'])
|
setno(entries, ['current'])
|
||||||
|
start = 0
|
||||||
entries[0]['current'] = True
|
entries[0]['current'] = True
|
||||||
reprint(stdscr, entries[:curses.LINES-3])
|
reprint(stdscr, entries[:curses.LINES-3])
|
||||||
return 1
|
return 1
|
||||||
elif start + y + delta > len(entries):
|
elif start + y + delta > len(entries):
|
||||||
if start + y == len(entries):
|
start = max(len(entries) - curses.LINES + 3, 0)
|
||||||
return curses.LINES - 3
|
y = min(curses.LINES - 3, len(entries))
|
||||||
start = len(entries) - curses.LINES + 3
|
|
||||||
y = curses.LINES - 3
|
|
||||||
setno(entries, ['current'])
|
setno(entries, ['current'])
|
||||||
entries[-1]['current'] = True
|
entries[-1]['current'] = True
|
||||||
reprint(stdscr, entries[-curses.LINES+3:])
|
reprint(stdscr, entries[-curses.LINES+3:])
|
||||||
|
@ -217,12 +226,17 @@ video_output = config.get('mpv', 'video-output', fallback=None)
|
||||||
ytdl_opts = {'format': config.get('youtube-dl', 'format', fallback='best')}
|
ytdl_opts = {'format': config.get('youtube-dl', 'format', fallback='best')}
|
||||||
|
|
||||||
if args.json_playlist:
|
if args.json_playlist:
|
||||||
with open(args.json_playlist) as f:
|
json_file = args.json_playlist
|
||||||
|
with open(json_file) as f:
|
||||||
entries = json.load(f)
|
entries = json.load(f)
|
||||||
elif args.youtube_playlist:
|
elif args.youtube_playlist:
|
||||||
with YoutubeDL({'extract_flat': 'in_playlist', 'quiet': True}) as ytdl:
|
with YoutubeDL({'extract_flat': 'in_playlist', 'quiet': True}) as ytdl:
|
||||||
info = ytdl.extract_info(args.youtube_playlist, download=False)
|
info = ytdl.extract_info(args.youtube_playlist, download=False)
|
||||||
entries = info.get('entries', {})
|
entries = info.get('entries', {})
|
||||||
|
json_file = ''
|
||||||
|
else:
|
||||||
|
entries = []
|
||||||
|
json_file = ''
|
||||||
|
|
||||||
# Init curses screen
|
# Init curses screen
|
||||||
stdscr = curses.initscr()
|
stdscr = curses.initscr()
|
||||||
|
@ -258,17 +272,11 @@ mp.observe_property('pause', lambda foo: update_status(stdscr, mp))
|
||||||
mp.observe_property('time-pos', lambda foo: update_status(stdscr, mp))
|
mp.observe_property('time-pos', lambda foo: update_status(stdscr, mp))
|
||||||
mp.observe_property('vid', lambda foo: update_status(stdscr, mp))
|
mp.observe_property('vid', lambda foo: update_status(stdscr, mp))
|
||||||
|
|
||||||
# Print initial content
|
initprint(stdscr, entries)
|
||||||
start = 0
|
|
||||||
y = 1
|
|
||||||
entries[0]['current'] = True
|
|
||||||
reprint(stdscr, entries[:curses.LINES-3])
|
|
||||||
|
|
||||||
file = '' # initial path of the file to dump the current playlist
|
|
||||||
|
|
||||||
c = stdscr.getch()
|
c = stdscr.getch()
|
||||||
while c != 113: # letter q
|
while c != 113: # letter q
|
||||||
if c == 10: # curses.KEY_ENTER doesn't work
|
if c == 10: # curses.KEY_ENTER doesn't work
|
||||||
|
if not entries: continue
|
||||||
mp._set_property('pause', False, bool)
|
mp._set_property('pause', False, bool)
|
||||||
play_thread = Thread(target=play, daemon=True)
|
play_thread = Thread(target=play, daemon=True)
|
||||||
play_thread.start()
|
play_thread.start()
|
||||||
|
@ -283,38 +291,55 @@ while c != 113: # letter q
|
||||||
mp._set_property('vid',
|
mp._set_property('vid',
|
||||||
'auto' if mp._get_property('vid') == 'no' else 'no')
|
'auto' if mp._get_property('vid') == 'no' else 'no')
|
||||||
elif c == 87: # letter W
|
elif c == 87: # letter W
|
||||||
prompt = _('Save playlist to [{}]:').format(file)
|
if not entries: continue
|
||||||
|
prompt = _('Save playlist to [{}]:').format(json_file)
|
||||||
stdscr.addstr(curses.LINES - 1, 0, prompt)
|
stdscr.addstr(curses.LINES - 1, 0, prompt)
|
||||||
curses.curs_set(True)
|
curses.curs_set(True)
|
||||||
curses.echo()
|
curses.echo()
|
||||||
file = stdscr.getstr(curses.LINES - 1, len(prompt) + 1).decode()
|
s = stdscr.getstr(curses.LINES - 1, len(prompt) + 1).decode()
|
||||||
|
if s: json_file = s
|
||||||
curses.curs_set(False)
|
curses.curs_set(False)
|
||||||
curses.noecho()
|
curses.noecho()
|
||||||
try:
|
try:
|
||||||
makedirs(dirname(abspath(file)), exist_ok=True)
|
makedirs(dirname(abspath(json_file)), exist_ok=True)
|
||||||
with open(file, 'w') as f:
|
with open(json_file, 'w') as f:
|
||||||
json.dump(entries, f)
|
json.dump(entries, f)
|
||||||
except:
|
except:
|
||||||
update_status(stdscr, mp,
|
update_status(stdscr, mp,
|
||||||
_("'{}': Can't open file for writing").format(file),
|
_("'{}': Can't open file for writing").format(json_file),
|
||||||
curses.color_pair(1))
|
curses.color_pair(1))
|
||||||
else:
|
else:
|
||||||
update_status(stdscr, mp,
|
update_status(stdscr, mp,
|
||||||
_("'{}' written").format(file))
|
_("'{}' written").format(json_file))
|
||||||
elif c == 99: # letter c
|
elif c == 99: # letter c
|
||||||
|
if not entries: continue
|
||||||
i = start + y - 1
|
i = start + y - 1
|
||||||
entries[i]['selected'] = not entries[i].setdefault('selected', False)
|
entries[i]['selected'] = not entries[i].setdefault('selected', False)
|
||||||
y = move(stdscr, entries, y, 1)
|
y = move(stdscr, entries, y, 1)
|
||||||
|
elif c == 100: # letter d
|
||||||
|
if not entries: continue
|
||||||
|
i = start + y - 1
|
||||||
|
if i + 1 < len(entries):
|
||||||
|
entries.pop(i)
|
||||||
|
entries[i]['current'] = True
|
||||||
|
elif len(entries) > 1:
|
||||||
|
entries.pop(i)
|
||||||
|
entries[i - 1]['current'] = True
|
||||||
|
else:
|
||||||
|
entries = []
|
||||||
|
reprint(stdscr, entries[start : start+curses.LINES-3])
|
||||||
elif c == 109: # letter m
|
elif c == 109: # letter m
|
||||||
mode = MODES[(MODES.index(mode) + 1) % 8]
|
mode = MODES[(MODES.index(mode) + 1) % 8]
|
||||||
update_status(stdscr, mp)
|
update_status(stdscr, mp)
|
||||||
#elif c == 119: # letter w
|
elif c == 119: # letter w
|
||||||
# ytdl_opts = {'format': ytdlf}
|
if not entries: continue
|
||||||
# with YoutubeDL(ytdl_opts) as ytdl:
|
with YoutubeDL({'format': ytdlf}) as ytdl:
|
||||||
# ytdl.download([getlink)
|
ytdl.download([getlink(entry) for entry in choose_from(mode)])
|
||||||
elif c in (curses.KEY_UP, 107): # up arrow or letter k
|
elif c in (curses.KEY_UP, 107): # up arrow or letter k
|
||||||
|
if not entries: continue
|
||||||
y = move(stdscr, entries, y, -1)
|
y = move(stdscr, entries, y, -1)
|
||||||
elif c in (curses.KEY_DOWN, 106): # down arrow or letter j
|
elif c in (curses.KEY_DOWN, 106): # down arrow or letter j
|
||||||
|
if not entries: continue
|
||||||
y = move(stdscr, entries, y, 1)
|
y = move(stdscr, entries, y, 1)
|
||||||
elif c in (curses.KEY_LEFT, 104): # left arrow or letter h
|
elif c in (curses.KEY_LEFT, 104): # left arrow or letter h
|
||||||
if mp._get_property('duration', int):
|
if mp._get_property('duration', int):
|
||||||
|
@ -323,12 +348,16 @@ while c != 113: # letter q
|
||||||
if mp._get_property('duration', int):
|
if mp._get_property('duration', int):
|
||||||
mp.seek(2.5)
|
mp.seek(2.5)
|
||||||
elif c == curses.KEY_HOME: # home
|
elif c == curses.KEY_HOME: # home
|
||||||
|
if not entries: continue
|
||||||
y = move(stdscr, entries, y, -len(entries))
|
y = move(stdscr, entries, y, -len(entries))
|
||||||
elif c == curses.KEY_END: # end
|
elif c == curses.KEY_END: # end
|
||||||
|
if not entries: continue
|
||||||
y = move(stdscr, entries, y, len(entries))
|
y = move(stdscr, entries, y, len(entries))
|
||||||
elif c == curses.KEY_NPAGE: # page down
|
elif c == curses.KEY_NPAGE: # page down
|
||||||
|
if not entries: continue
|
||||||
y = move(stdscr, entries, y, curses.LINES - 4)
|
y = move(stdscr, entries, y, curses.LINES - 4)
|
||||||
elif c == curses.KEY_PPAGE: # page up
|
elif c == curses.KEY_PPAGE: # page up
|
||||||
|
if not entries: continue
|
||||||
y = move(stdscr, entries, y, 4 - curses.LINES)
|
y = move(stdscr, entries, y, 4 - curses.LINES)
|
||||||
elif c == curses.KEY_F5: # F5
|
elif c == curses.KEY_F5: # F5
|
||||||
reprint(stdscr, entries[start : start+curses.LINES-3])
|
reprint(stdscr, entries[start : start+curses.LINES-3])
|
||||||
|
|
7
setup.py
7
setup.py
|
@ -1,19 +1,22 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
from os import walk
|
||||||
|
from os.path import join
|
||||||
from sys import prefix
|
from sys import prefix
|
||||||
|
|
||||||
with open('README.rst') as f:
|
with open('README.rst') as f:
|
||||||
long_description = f.read()
|
long_description = f.read()
|
||||||
|
|
||||||
setup(name='comp', version='0.1.1a3',
|
setup(name='comp', version='0.1.1a4',
|
||||||
url='https://github.com/McSinyx/comp',
|
url='https://github.com/McSinyx/comp',
|
||||||
description=('Curses Online Media Player'),
|
description=('Curses Online Media Player'),
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
author='Nguyễn Gia Phong', author_email='vn.mcsinyx@gmail.com',
|
author='Nguyễn Gia Phong', author_email='vn.mcsinyx@gmail.com',
|
||||||
py_modules=['mpv'], scripts=['comp'],
|
py_modules=['mpv'], scripts=['comp'],
|
||||||
data_files=[
|
data_files=[
|
||||||
('{}/share/locale/vi/LC_MESSAGES/'.format(prefix), ['locale/vi/LC_MESSAGES/comp.mo']),
|
*((join(prefix, 'share', i[0]), [join(i[0], 'comp.mo')])
|
||||||
|
for i in walk('locale') if i[2]),
|
||||||
('/etc/comp', ['settings.ini'])
|
('/etc/comp', ['settings.ini'])
|
||||||
], classifiers=[
|
], classifiers=[
|
||||||
'Development Status :: 3 - Alpha',
|
'Development Status :: 3 - Alpha',
|
||||||
|
|
Loading…
Reference in New Issue