From 22f341eb979fcb4a46caeefdb7830d8ddc67db75 Mon Sep 17 00:00:00 2001 From: david hamp-gonsalves Date: Fri, 18 Apr 2014 10:33:53 -0300 Subject: [PATCH 1/4] added play plugin and docs --- beetsplug/play.py | 100 ++++++++++++++++++++++++++++++++++++++++++ docs/plugins/play.rst | 19 ++++++++ 2 files changed, 119 insertions(+) create mode 100644 beetsplug/play.py create mode 100644 docs/plugins/play.rst diff --git a/beetsplug/play.py b/beetsplug/play.py new file mode 100644 index 000000000..013b5b5ca --- /dev/null +++ b/beetsplug/play.py @@ -0,0 +1,100 @@ +# This file is a plugin on beets. +# Copyright (c) <2013> David Hamp-Gonsalves +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""sends the results of a query to the configured music player as a playlist""" + +from beets.plugins import BeetsPlugin +from beets.ui import Subcommand +from beets import config +from beets import ui + +import subprocess +import os +from tempfile import NamedTemporaryFile + + +def check_config(): + + if not config['play']['command'].get(): + ui.print_(ui.colorize('red', 'no player command is set. Verify configuration.')) + return + +def play_music(lib, opts, args): + """execute query, create temporary playlist and execute player command passing that playlist""" + check_config() + + #get the music player command to launch and pass playlist to + command = config['play']['command'].get() + isDebug = config['play']['debug'].get() + + if opts.album: #search by album + #get all the albums to be added to playlist + albums = lib.albums(ui.decargs(args)) + paths = [] + + for album in albums: + paths.append(album.item_dir()) + itemType = 'album' + + else: #search by item name + paths = [item.path for item in lib.items(ui.decargs(args))] + itemType = 'track' + + if len(paths) > 1: + itemType += 's' + + if not paths: + ui.print_(ui.colorize('yellow', 'no {0} to play.'.format(itemType))) + return + + #warn user before playing any huge playlists + if len(paths) > 100: + ui.print_(ui.colorize('yellow', 'do you really want to play {0} {1}?'.format(len(paths), itemType))) + opts = ('Continue', 'Abort') + if ui.input_options(opts) == 'a': + return + + m3u = NamedTemporaryFile('w', suffix='.m3u', delete=False) + for item in paths: + m3u.write(item + '\n') + m3u.close() + + #prevent output from player poluting our console(unless debug is on) + if not isDebug: + FNULL = open(os.devnull, 'w') + subprocess.Popen([command, m3u.name], stdout=FNULL, stderr=subprocess.STDOUT) + FNULL.close() + else: + subprocess.Popen([command, m3u.name]) + + ui.print_('playing {0} {1}.'.format(len(paths), itemType)) + + +class PlayPlugin(BeetsPlugin): + + def __init__(self): + super(PlayPlugin, self).__init__() + + config['play'].add({ + 'command': None, + 'debug': False + }) + + def commands(self): + play_command = Subcommand('play', help='send query results to music player as playlist.') + play_command.parser.add_option('-a', '--album', + action='store_true', default=False, + help='query and load albums(folders) rather then tracks.') + play_command.func = play_music + return [play_command] diff --git a/docs/plugins/play.rst b/docs/plugins/play.rst new file mode 100644 index 000000000..94b816333 --- /dev/null +++ b/docs/plugins/play.rst @@ -0,0 +1,19 @@ +Play Plugin +============ + +The ``play`` plugin allows you to pass the results of a query to a music player in the form of a m3u playlist. + +To use the plugin, first enable it in your configuration (see +:ref:`using-plugins`). Then, add an ``play:`` section to your configuration +file:: + + play: + #Command to use to open playlist with music player(ie VLC on OSX: /Applications/VLC.app/Contents/MacOS/VLC). + #Command must have a path or be in your PATH. + command: cvlc + #Debug(optional) displays output from player for aiding in setting up command correctly. + debug: + +How it works +============ +The plugin works by turning your query results into a temporary m3u file. Then the command you have configured it executed by the shell and the playlist is passed as the last parameter. From f8b6c1ea114e833558563f75718d7fc18368a70d Mon Sep 17 00:00:00 2001 From: david hamp-gonsalves Date: Sat, 19 Apr 2014 08:52:18 -0300 Subject: [PATCH 2/4] added default commands to use system file associations --- beetsplug/play.py | 86 ++++++++++++++++++++++++------------------ docs/plugins/index.rst | 2 + docs/plugins/play.rst | 10 ++--- 3 files changed, 57 insertions(+), 41 deletions(-) diff --git a/beetsplug/play.py b/beetsplug/play.py index 013b5b5ca..425ef2a38 100644 --- a/beetsplug/play.py +++ b/beetsplug/play.py @@ -12,34 +12,41 @@ # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. -"""sends the results of a query to the configured music player as a playlist""" +"""Sends the results of a query to the configured music player as a playlist. +""" + from beets.plugins import BeetsPlugin from beets.ui import Subcommand from beets import config from beets import ui - +import platform import subprocess import os from tempfile import NamedTemporaryFile -def check_config(): - - if not config['play']['command'].get(): - ui.print_(ui.colorize('red', 'no player command is set. Verify configuration.')) - return - def play_music(lib, opts, args): - """execute query, create temporary playlist and execute player command passing that playlist""" - check_config() + """Execute query, create temporary playlist and execute player + command passing that playlist. + """ - #get the music player command to launch and pass playlist to command = config['play']['command'].get() - isDebug = config['play']['debug'].get() + is_debug = config['play']['debug'].get() - if opts.album: #search by album - #get all the albums to be added to playlist + # If a command isn't set then let the OS decide how to open the playlist. + if not command: + sys_name = platform.system() + if sys_name == 'Darwin': + command = 'open' + elif sys_name == 'Windows': + command = 'start' + else: + # If not Mac or Win then assume Linux(or posix based). + command = 'xdg-open' + + # Preform search by album and add folders rather then tracks to playlist. + if opts.album: albums = lib.albums(ui.decargs(args)) paths = [] @@ -47,42 +54,48 @@ def play_music(lib, opts, args): paths.append(album.item_dir()) itemType = 'album' - else: #search by item name + # Preform item query and add tracks to playlist. + else: paths = [item.path for item in lib.items(ui.decargs(args))] itemType = 'track' - if len(paths) > 1: - itemType += 's' + itemType += 's' if len(paths) > 1 else '' if not paths: - ui.print_(ui.colorize('yellow', 'no {0} to play.'.format(itemType))) - return + ui.print_(ui.colorize('yellow', 'no {0} to play.'.format(itemType))) + return - #warn user before playing any huge playlists + # Warn user before playing any huge playlists. if len(paths) > 100: - ui.print_(ui.colorize('yellow', 'do you really want to play {0} {1}?'.format(len(paths), itemType))) - opts = ('Continue', 'Abort') - if ui.input_options(opts) == 'a': - return + ui.print_(ui.colorize('yellow', + 'do you really want to play {0} {1}?' + .format(len(paths), itemType))) + if ui.input_options(('Continue', 'Abort')) == 'a': + return + + # Create temporary m3u file to hold our playlist. m3u = NamedTemporaryFile('w', suffix='.m3u', delete=False) for item in paths: m3u.write(item + '\n') m3u.close() - #prevent output from player poluting our console(unless debug is on) - if not isDebug: + # Prevent player output from poluting our console(unless debug is on). + if not is_debug: FNULL = open(os.devnull, 'w') - subprocess.Popen([command, m3u.name], stdout=FNULL, stderr=subprocess.STDOUT) + + subprocess.Popen([command, m3u.name], + stdout=FNULL, stderr=subprocess.STDOUT) + FNULL.close() else: subprocess.Popen([command, m3u.name]) ui.print_('playing {0} {1}.'.format(len(paths), itemType)) - - + + class PlayPlugin(BeetsPlugin): - + def __init__(self): super(PlayPlugin, self).__init__() @@ -92,9 +105,10 @@ class PlayPlugin(BeetsPlugin): }) def commands(self): - play_command = Subcommand('play', help='send query results to music player as playlist.') - play_command.parser.add_option('-a', '--album', - action='store_true', default=False, - help='query and load albums(folders) rather then tracks.') - play_command.func = play_music - return [play_command] + play_command = Subcommand('play', + help='send query results to music player as playlist.') + play_command.parser.add_option('-a', '--album', + action='store_true', default=False, + help='query and load albums(folders) rather then tracks.') + play_command.func = play_music + return [play_command] diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 210c2d6b2..05a0b7aef 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -48,6 +48,7 @@ by typing ``beet version``. ihate convert info + play smartplaylist mbsync missing @@ -109,6 +110,7 @@ Interoperability changes. * :doc:`importfeeds`: Keep track of imported files via ``.m3u`` playlist file(s) or symlinks. * :doc:`smartplaylist`: Generate smart playlists based on beets queries. +* :doc:`play`: Play beets queries in your music player. Miscellaneous ------------- diff --git a/docs/plugins/play.rst b/docs/plugins/play.rst index 94b816333..f1cb9ec9e 100644 --- a/docs/plugins/play.rst +++ b/docs/plugins/play.rst @@ -8,11 +8,11 @@ To use the plugin, first enable it in your configuration (see file:: play: - #Command to use to open playlist with music player(ie VLC on OSX: /Applications/VLC.app/Contents/MacOS/VLC). - #Command must have a path or be in your PATH. - command: cvlc - #Debug(optional) displays output from player for aiding in setting up command correctly. - debug: + # Command(optional) override the system default for m3u files. + # You can define a command to be executed by the shell which the m3u path will be appended to. + command: command: /Applications/VLC.app/Contents/MacOS/VLC + # Debug(optional) displays output from player for aiding in setting up command correctly. + debug: True How it works ============ From 44e84e775a2f8b46060c97c504d4ec3099bf586d Mon Sep 17 00:00:00 2001 From: david hamp-gonsalves Date: Sat, 19 Apr 2014 09:24:37 -0300 Subject: [PATCH 3/4] correcting sytles --- beetsplug/play.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/beetsplug/play.py b/beetsplug/play.py index 425ef2a38..c26b97531 100644 --- a/beetsplug/play.py +++ b/beetsplug/play.py @@ -59,7 +59,7 @@ def play_music(lib, opts, args): paths = [item.path for item in lib.items(ui.decargs(args))] itemType = 'track' - itemType += 's' if len(paths) > 1 else '' + itemType += 's' if len(paths) > 1 else '' if not paths: ui.print_(ui.colorize('yellow', 'no {0} to play.'.format(itemType))) @@ -68,8 +68,8 @@ def play_music(lib, opts, args): # Warn user before playing any huge playlists. if len(paths) > 100: ui.print_(ui.colorize('yellow', - 'do you really want to play {0} {1}?' - .format(len(paths), itemType))) + 'do you really want to play {0} {1}?' + .format(len(paths), itemType))) if ui.input_options(('Continue', 'Abort')) == 'a': return @@ -85,7 +85,7 @@ def play_music(lib, opts, args): FNULL = open(os.devnull, 'w') subprocess.Popen([command, m3u.name], - stdout=FNULL, stderr=subprocess.STDOUT) + stdout=FNULL, stderr=subprocess.STDOUT) FNULL.close() else: @@ -106,9 +106,9 @@ class PlayPlugin(BeetsPlugin): def commands(self): play_command = Subcommand('play', - help='send query results to music player as playlist.') + help='send query results to music player as playlist.') play_command.parser.add_option('-a', '--album', - action='store_true', default=False, - help='query and load albums(folders) rather then tracks.') + action='store_true', default=False, + help='query and load albums(folders) rather then tracks.') play_command.func = play_music return [play_command] From f67490c4642b09483d1f868ff8df1905b45f40a4 Mon Sep 17 00:00:00 2001 From: david hamp-gonsalves Date: Sat, 19 Apr 2014 09:34:50 -0300 Subject: [PATCH 4/4] reworking messaging --- beetsplug/play.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/beetsplug/play.py b/beetsplug/play.py index c26b97531..7d69d5428 100644 --- a/beetsplug/play.py +++ b/beetsplug/play.py @@ -62,14 +62,14 @@ def play_music(lib, opts, args): itemType += 's' if len(paths) > 1 else '' if not paths: - ui.print_(ui.colorize('yellow', 'no {0} to play.'.format(itemType))) + ui.print_(ui.colorize('yellow', 'No {0} to play.'.format(itemType))) return # Warn user before playing any huge playlists. if len(paths) > 100: - ui.print_(ui.colorize('yellow', - 'do you really want to play {0} {1}?' - .format(len(paths), itemType))) + ui.print_(ui.colorize( + 'yellow', + 'You are about to queue {0} {1}.'.format(len(paths), itemType))) if ui.input_options(('Continue', 'Abort')) == 'a': return @@ -85,13 +85,13 @@ def play_music(lib, opts, args): FNULL = open(os.devnull, 'w') subprocess.Popen([command, m3u.name], - stdout=FNULL, stderr=subprocess.STDOUT) + stdout=FNULL, stderr=subprocess.STDOUT) FNULL.close() else: subprocess.Popen([command, m3u.name]) - ui.print_('playing {0} {1}.'.format(len(paths), itemType)) + ui.print_('Playing {0} {1}.'.format(len(paths), itemType)) class PlayPlugin(BeetsPlugin): @@ -105,10 +105,12 @@ class PlayPlugin(BeetsPlugin): }) def commands(self): - play_command = Subcommand('play', - help='send query results to music player as playlist.') - play_command.parser.add_option('-a', '--album', - action='store_true', default=False, - help='query and load albums(folders) rather then tracks.') + play_command = Subcommand( + 'play', + help='Send query results to music player as playlist.') + play_command.parser.add_option( + '-a', '--album', + action='store_true', default=False, + help='Query and load albums(as folders) rather then tracks.') play_command.func = play_music return [play_command]