Fix the bug when the playlist is shorter than the screen

This commit is contained in:
Nguyễn Gia Phong 2017-04-10 11:30:58 +07:00 committed by Nguyễn Gia Phong
parent c1b0652078
commit fc6b4a0c51
3 changed files with 75 additions and 43 deletions

View File

@ -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
View File

@ -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])

View File

@ -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',