songlist.py
dump(un)
¶
Show entire playlist.
Source code in mps_youtube/commands/songlist.py
@command(r'(un)?dump', 'dump', 'undump')
def dump(un):
""" Show entire playlist. """
func, args = g.last_search_query
if func is paginatesongs:
paginatesongs(dumps=(not un), **args)
else:
un = "" if not un else un
g.message = "%s%sdump%s may only be used on an open YouTube playlist"
g.message = g.message % (c.y, un, c.w)
g.content = content.generate_songlist_display()
nextprev(np, page=None)
¶
Get next / previous search results.
Source code in mps_youtube/commands/songlist.py
@command(r'(n|p)\s*(\d{1,2})?')
def nextprev(np, page=None):
""" Get next / previous search results. """
if isinstance(g.content, content.PaginatedContent):
page_count = g.content.numPages()
function = g.content.getPage
args = {}
else:
page_count = math.ceil(g.result_count/util.getxy().max_results)
function, args = g.last_search_query
good = False
if function:
if np == "n":
if g.current_page + 1 < page_count:
g.current_page += 1
good = True
elif np == "p":
if page and int(page) in range(1,20):
g.current_page = int(page)-1
good = True
elif g.current_page > 0:
g.current_page -= 1
good = True
if good:
function(page=g.current_page, **args)
else:
norp = "next" if np == "n" else "previous"
g.message = "No %s items to display" % norp
if not isinstance(g.content, content.PaginatedContent):
g.content = content.generate_songlist_display()
return good
paginatesongs(func, page=0, splash=True, dumps=False, length=None, msg=None, failmsg=None, loadmsg=None)
¶
A utility function for handling lists of songs, so that the pagination and the dump command will work properly.
:param func: Either a function taking a start and end index,
or a slicable object. Either way, it should produce an iterable
of :class:mps_youtube.playlist.Video
objects.
:param page: The page number to display
:param splash: Whether or not to display a splash screen while
loading.
:param dumps: Used by :func:dump
command to load all songs, instead
of only those that fit on a page
:param length: The total number of songs. It it is not provided,
len(func)
will be used instead.
:param msg: Message to display after loading successfully
:param failmsg: Message to display on failure (if no songs are
returned by func
:param loadmsg: Message to display while loading
:type page: int
:type splash: bool
:type dumps: bool
:type length: int
:type msg: str
:type failmsg: str
:type loadmsg: str
Source code in mps_youtube/commands/songlist.py
def paginatesongs(func, page=0, splash=True, dumps=False,
length=None, msg=None, failmsg=None, loadmsg=None):
"""
A utility function for handling lists of songs, so that
the pagination and the dump command will work properly.
:param func: Either a function taking a start and end index,
or a slicable object. Either way, it should produce an iterable
of :class:`mps_youtube.playlist.Video` objects.
:param page: The page number to display
:param splash: Whether or not to display a splash screen while
loading.
:param dumps: Used by :func:`dump` command to load all songs, instead
of only those that fit on a page
:param length: The total number of songs. It it is not provided,
``len(func)`` will be used instead.
:param msg: Message to display after loading successfully
:param failmsg: Message to display on failure (if no songs are
returned by func
:param loadmsg: Message to display while loading
:type page: int
:type splash: bool
:type dumps: bool
:type length: int
:type msg: str
:type failmsg: str
:type loadmsg: str
"""
if splash:
g.message = loadmsg or ''
g.content = content.logo(col=c.b)
screen.update()
max_results = util.getxy().max_results
if dumps:
s = 0
e = None
else:
s = page * max_results
e = (page + 1) * max_results
if callable(func):
songs = func(s, e)
else:
songs = func[s:e]
if length is None:
length = len(func)
args = {'func':func, 'length':length, 'msg':msg,
'failmsg':failmsg, 'loadmsg': loadmsg}
g.last_search_query = (paginatesongs, args)
g.browse_mode = "normal"
g.current_page = page
g.result_count = length
g.model.songs = songs
g.content = content.generate_songlist_display()
g.last_opened = ""
g.message = msg or ''
if not songs:
g.message = failmsg or g.message
if songs:
# preload first result url
streams.preload(songs[0], delay=0)
plist(parturl)
¶
Retrieve YouTube playlist.
Source code in mps_youtube/commands/songlist.py
@command(r'pl\s+%s' % PL, 'pl')
def plist(parturl):
""" Retrieve YouTube playlist. """
if parturl in g.pafy_pls:
ytpl, plitems = g.pafy_pls[parturl]
else:
util.dbg("%sFetching playlist using pafy%s", c.y, c.w)
ytpl = pafy.get_playlist(parturl)
plitems = util.IterSlicer(ytpl['videos'])
g.pafy_pls[parturl] = (ytpl, plitems)
def pl_seg(s, e):
return [Video(i['id'], i['title'], util.parse_video_length(i['duration'])) for i in plitems[s:e]]
msg = "Showing YouTube playlist %s" % (c.y + ytpl['info']['title'] + c.w)
loadmsg = "Retrieving YouTube playlist"
paginatesongs(pl_seg, length=len(ytpl['videos']), msg=msg, loadmsg=loadmsg)
reverse_playlist()
¶
Reverse order of entire loaded playlist.
Source code in mps_youtube/commands/songlist.py
@command(r'reverse all', 'reverse all')
def reverse_playlist():
""" Reverse order of entire loaded playlist. """
# Prevent crash if no last query
if g.last_search_query == (None, None) or \
'func' not in g.last_search_query[1]:
g.content = content.logo()
g.message = "No playlist loaded"
return
songs_list_or_func = g.last_search_query[1]['func']
if callable(songs_list_or_func):
songs = reversed(songs_list_or_func(0,None))
else:
songs = reversed(songs_list_or_func)
paginatesongs(list(songs))
g.message = c.y + "Reversed entire playlist" + c.w
g.content = content.generate_songlist_display()
reverse_songs()
¶
Reverse order of displayed items.
Source code in mps_youtube/commands/songlist.py
@command(r'reverse', 'reverse')
def reverse_songs():
""" Reverse order of displayed items. """
g.model.songs = g.model.songs[::-1]
g.message = c.y + "Reversed displayed songs" + c.w
g.content = content.generate_songlist_display()
reverse_songs_range(lower, upper)
¶
Reverse the songs within a specified range.
Source code in mps_youtube/commands/songlist.py
@command(r'reverse\s*(\d{1,4})\s*-\s*(\d{1,4})\s*', 'reverse')
def reverse_songs_range(lower, upper):
""" Reverse the songs within a specified range. """
lower, upper = int(lower), int(upper)
if lower > upper: lower, upper = upper, lower
g.model.songs[lower-1:upper] = reversed(g.model.songs[lower-1:upper])
g.message = c.y + "Reversed range: " + str(lower) + "-" + str(upper) + c.w
g.content = content.generate_songlist_display()
shuffle_fn()
¶
Shuffle displayed items.
Source code in mps_youtube/commands/songlist.py
@command(r'shuffle', 'shuffle')
def shuffle_fn():
""" Shuffle displayed items. """
random.shuffle(g.model.songs)
g.message = c.y + "Items shuffled" + c.w
g.content = content.generate_songlist_display()
songlist_mv_sw(action, a, b)
¶
Move a song or swap two songs.
Source code in mps_youtube/commands/songlist.py
@command(r'(mv|sw)\s*(\d{1,4})\s*[\s,]\s*(\d{1,4})', 'mv', 'sw')
def songlist_mv_sw(action, a, b):
""" Move a song or swap two songs. """
i, j = int(a) - 1, int(b) - 1
if action == "mv":
g.model.songs.insert(j, g.model.songs.pop(i))
g.message = util.F('song move') % (g.model[j].title, b)
elif action == "sw":
g.model[i], g.model[j] = g.model[j], g.model[i]
g.message = util.F('song sw') % (min(a, b), max(a, b))
g.content = content.generate_songlist_display()
songlist_rm_add(action, songrange)
¶
Remove or add tracks. works directly on user input.
Source code in mps_youtube/commands/songlist.py
@command(r'(rm|add)\s*(-?\d[-,\d\s]{,250})', 'rm', 'add')
def songlist_rm_add(action, songrange):
""" Remove or add tracks. works directly on user input. """
selection = util.parse_multi(songrange)
if action == "add":
duplicate_songs = []
for songnum in selection:
if g.model[songnum - 1] in g.active:
duplicate_songs.append(str(songnum))
g.active.songs.append(g.model[songnum - 1])
d = g.active.duration
g.message = util.F('added to pl') % (len(selection), len(g.active), d)
if duplicate_songs:
duplicate_songs = ', '.join(sorted(duplicate_songs))
g.message += '\n'
g.message += util.F('duplicate tracks') % duplicate_songs
elif action == "rm":
selection = sorted(set(selection), reverse=True)
removed = str(tuple(reversed(selection))).replace(",", "")
for x in selection:
g.model.songs.pop(x - 1)
try:
g.active.songs.pop(g.current_page * util.getxy().max_results + x - 1)
except IndexError:
pass
g.message = util.F('songs rm') % (len(selection), removed)
g.content = content.generate_songlist_display()