diff --git a/beetsplug/smartplaylist.py b/beetsplug/smartplaylist.py new file mode 100644 index 000000000..44aae61b5 --- /dev/null +++ b/beetsplug/smartplaylist.py @@ -0,0 +1,91 @@ +# This file is part of beets. +# Copyright 2013, Dang Mai . +# +# 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. + +"""Generates smart playlists based on beets queries. +""" +from __future__ import print_function + +from beets.plugins import BeetsPlugin +from beets import config, ui +from beets.util import normpath, syspath +import os + +# Global variables so that smartplaylist can detect database changes and run +# only once before beets exits. +database_changed = False +library = None + + +def update_playlists(lib): + from beets.util.functemplate import Template + print("Updating smart playlists...") + playlists = config['smartplaylist']['playlists'].get(list) + playlist_dir = config['smartplaylist']['playlist_dir'].get(unicode) + relative_to = config['smartplaylist']['relative_to'].get() + if relative_to: + relative_to = normpath(relative_to) + + for playlist in playlists: + items = lib.items(playlist['query']) + m3us = {} + basename = playlist['name'].encode('utf8') + # As we allow tags in the m3u names, we'll need to iterate through + # the items and generate the correct m3u file names. + for item in items: + m3u_name = item.evaluate_template(Template(basename), lib=lib) + if not (m3u_name in m3us): + m3us[m3u_name] = [] + if relative_to: + m3us[m3u_name].append(os.path.relpath(item.path, relative_to)) + else: + m3us[m3u_name].append(item.path) + # Now iterate through the m3us that we need to generate + for m3u in m3us: + m3u_path = normpath(os.path.join(playlist_dir, m3u)) + with open(syspath(m3u_path), 'w') as f: + for path in m3us[m3u]: + f.write(path + '\n') + print("... Done") + + +class SmartPlaylistPlugin(BeetsPlugin): + def __init__(self): + super(SmartPlaylistPlugin, self).__init__() + self.config.add({ + 'relative_to': None, + 'playlist_dir': u'.', + 'playlists': [] + }) + + def commands(self): + def update(lib, opts, args): + update_playlists(lib) + spl_update = ui.Subcommand('splupdate', + help='update the smart playlists') + spl_update.func = update + return [spl_update] + + +@SmartPlaylistPlugin.listen('database_change') +def handle_change(lib): + global library + global database_changed + library = lib + database_changed = True + + +@SmartPlaylistPlugin.listen('cli_exit') +def update(): + if database_changed: + update_playlists(library) diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 898795150..2217f4d91 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -59,6 +59,7 @@ disabled by default, but you can turn them on as described above. ihate convert info + smartplaylist Autotagger Extensions '''''''''''''''''''''' @@ -92,6 +93,7 @@ Interoperability * :doc:`mpdupdate`: Automatically notifies `MPD`_ whenever the beets library changes. * :doc:`importfeeds`: Keep track of imported files via ``.m3u`` playlist file(s) or symlinks. +* :doc:`smartplaylist`: Generate smart playlists based on beets queries. Miscellaneous ''''''''''''' diff --git a/docs/plugins/smartplaylist.rst b/docs/plugins/smartplaylist.rst new file mode 100644 index 000000000..56fd39bd2 --- /dev/null +++ b/docs/plugins/smartplaylist.rst @@ -0,0 +1,51 @@ +Smart Playlist Plugin +===================== + +``smartplaylist`` is a plugin to generate smart playlists in m3u format based on +beets queries every time your library changes. This plugin is specifically +created to work well with `MPD`_'s playlist functionality. + +.. _MPD: http://mpd.wikia.com/wiki/Music_Player_Daemon_Wiki + +To use it, enable the plugin by putting ``smartplaylist`` in the ``plugins`` +section in your ``config.yaml``. Then configure your smart playlists like the +following example:: + + smartplaylist: + relative_to: ~/Music + playlist_dir: ~/.mpd/playlists + playlists: + - query: '' + name: all.m3u + + - query: 'artist:Beatles' + name: beatles.m3u + +If you intend to use this plugin to generate playlists for MPD, you should set +``relative_to`` to your MPD music directory (by default, ``relative_to`` is +``None``, and the absolute paths to your music files will be generated). + +``playlist_dir`` is where the generated playlist files will be put. + +You can generate as many playlists as you want by adding them to the +``playlists`` section, using the normal querying format (see +:doc:`/reference/query`) for ``query`` and the file name to be generated for +``name`` (*note*: if you have existing files with the same names, you should +back them up, as they will be overwritten when the plugin runs). + +For more advanced usage, you can also specify metadata (see +:doc:`/reference/pathformat/`) in the ``name`` field, for example:: + + - query: 'year::201(0|1)' + name: 'ReleasedIn$year.m3u' + +This will query all the songs in 2010 and 2011, and generate the 2 playlist +files `ReleasedIn2010.m3u` and `ReleasedIn2011.m3u` using those songs. + +If you add a smart playlist to your ``config.yaml`` file and don't want to wait +until the next time your library changes for ``smartplugin`` to run, you can +invoke it manually from the command-line:: + + $ beet splupdate + +which will generate your new smart playlists. \ No newline at end of file