Merge pull request #2628 from anarcat/lyrics-html

print lyrics as HTML
This commit is contained in:
Adrian Sampson 2017-07-17 10:56:29 -04:00
commit aefb1c4947
3 changed files with 174 additions and 10 deletions

View file

@ -19,12 +19,15 @@
from __future__ import absolute_import, division, print_function
import difflib
import errno
import itertools
import json
import struct
import os.path
import re
import requests
import unicodedata
from unidecode import unidecode
import warnings
import six
from six.moves import urllib
@ -74,6 +77,41 @@ URL_CHARACTERS = {
}
USER_AGENT = 'beets/{}'.format(beets.__version__)
# the content for the base index.rst generated
REST_INDEX_TEMPLATE = u'''Lyrics
======
* :ref:`Song index <genindex>`
* :ref:`search`
Artist index:
.. toctree::
:maxdepth: 1
:glob:
artists/*
'''
# the content for the base conf.py generated
REST_CONF_TEMPLATE = u'''# -*- coding: utf-8 -*-
master_doc = 'index'
project = u'Lyrics'
copyright = u'none'
author = u'Various Authors'
latex_documents = [
(master_doc, 'Lyrics.tex', project,
author, 'manual'),
]
epub_title = project
epub_author = author
epub_publisher = author
epub_copyright = copyright
epub_exclude_files = ['search.html']
epub_tocdepth = 1
epub_tocdup = False
'''
# Utilities.
@ -595,12 +633,21 @@ class LyricsPlugin(plugins.BeetsPlugin):
"76V-uFL5jks5dNvcGCdarqFjDhP9c",
'fallback': None,
'force': False,
'local': False,
'sources': self.SOURCES,
})
self.config['bing_client_secret'].redact = True
self.config['google_API_key'].redact = True
self.config['google_engine_ID'].redact = True
self.config['genius_api_key'].redact = True
# state information for the ReST writer
# the current artist
self.artist = u'Unknown artist'
# the current album, False means no album yet
self.album = False
# the current rest file content. None means the file is not
# open yet.
self.rest = None
available_sources = list(self.SOURCES)
sources = plugins.sanitize_choices(
@ -658,27 +705,107 @@ class LyricsPlugin(plugins.BeetsPlugin):
action='store_true', default=False,
help=u'print lyrics to console',
)
cmd.parser.add_option(
u'-r', u'--write-rest', dest='writerest',
action='store', default='.', metavar='dir',
help=u'write lyrics to given directory as ReST files',
)
cmd.parser.add_option(
u'-f', u'--force', dest='force_refetch',
action='store_true', default=False,
help=u'always re-download lyrics',
)
cmd.parser.add_option(
u'-l', u'--local', dest='local_only',
action='store_true', default=False,
help=u'do not fetch missing lyrics',
)
def func(lib, opts, args):
# The "write to files" option corresponds to the
# import_write config value.
write = ui.should_write()
if opts.writerest:
self.writerest_indexes(opts.writerest)
for item in lib.items(ui.decargs(args)):
self.fetch_item_lyrics(
lib, item, write,
opts.force_refetch or self.config['force'],
)
if opts.printlyr and item.lyrics:
ui.print_(item.lyrics)
if not opts.local_only and not self.config['local']:
self.fetch_item_lyrics(
lib, item, write,
opts.force_refetch or self.config['force'],
)
if item.lyrics:
if opts.printlyr:
ui.print_(item.lyrics)
if opts.writerest:
self.writerest(opts.writerest, item)
if opts.writerest:
# flush last artist
self.writerest(opts.writerest, None)
ui.print_(u'ReST files generated. to build, use one of:')
ui.print_(u' sphinx-build -b html %s _build/html'
% opts.writerest)
ui.print_(u' sphinx-build -b epub %s _build/epub'
% opts.writerest)
ui.print_((u' sphinx-build -b latex %s _build/latex '
u'&& make -C _build/latex all-pdf')
% opts.writerest)
cmd.func = func
return [cmd]
def writerest(self, directory, item):
"""Write the item to an ReST file
This will keep state (in the `rest` variable) in order to avoid
writing continuously to the same files.
"""
if item is None or self.artist != item.artist:
if self.rest is not None:
slug = re.sub(r'\W+', '-',
unidecode(self.artist.strip()).lower())
path = os.path.join(directory, 'artists', slug + u'.rst')
with open(path, 'wb') as output:
output.write(self.rest.encode('utf-8'))
self.rest = None
if item is None:
return
self.artist = item.artist
self.rest = u"%s\n%s\n\n.. contents::\n :local:\n\n" \
% (self.artist.strip(),
u'=' * len(self.artist.strip()))
if self.album != item.album:
tmpalbum = self.album = item.album
if self.album == '':
tmpalbum = u'Unknown album'
self.rest += u"%s\n%s\n\n" % (tmpalbum.strip(),
u'-' * len(tmpalbum.strip()))
title_str = u":index:`%s`" % item.title.strip()
block = u'| ' + item.lyrics.replace(u'\n', u'\n| ')
self.rest += u"%s\n%s\n\n%s\n" % (title_str,
u'~' * len(title_str),
block)
def writerest_indexes(self, directory):
"""Write conf.py and index.rst files necessary for Sphinx
We write minimal configurations that are necessary for Sphinx
to operate. We do not overwrite existing files so that
customizations are respected."""
try:
os.makedirs(os.path.join(directory, 'artists'))
except OSError as e:
if e.errno == errno.EEXIST:
pass
else:
raise
indexfile = os.path.join(directory, 'index.rst')
if not os.path.exists(indexfile):
with open(indexfile, 'w') as output:
output.write(REST_INDEX_TEMPLATE)
conffile = os.path.join(directory, 'conf.py')
if not os.path.exists(conffile):
with open(conffile, 'w') as output:
output.write(REST_CONF_TEMPLATE)
def imported(self, session, task):
"""Import hook for fetching lyrics automatically.
"""

View file

@ -4,7 +4,11 @@ Changelog
1.4.6 (in development)
----------------------
Changelog goes here!
New features:
* :doc:`/plugins/lyrics`: The plugin can now produce reStructuredText files
for beautiful, readable books of lyrics. Thanks to :user:`anarcat`.
:bug:`2628`
Fixes:

View file

@ -87,11 +87,44 @@ The ``-p`` option to the ``lyrics`` command makes it print lyrics out to the
console so you can view the fetched (or previously-stored) lyrics.
The ``-f`` option forces the command to fetch lyrics, even for tracks that
already have lyrics.
already have lyrics. Inversely, the ``-l`` option restricts operations
to lyrics that are locally available, which show lyrics faster without using
the network at all.
Rendering Lyrics into Other Formats
-----------------------------------
The ``-r directory`` option renders all lyrics as `reStructuredText`_ (ReST)
documents in ``directory`` (by default, the current directory). That
directory, in turn, can be parsed by tools like `Sphinx`_ to generate HTML,
ePUB, or PDF documents.
A minimal ``conf.py`` and ``index.rst`` files are created the first time the
command is run. They are not overwritten on subsequent runs, so you can safely
modify these files to customize the output.
.. _Sphinx: http://www.sphinx-doc.org/
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
Sphinx supports various `builders
<http://www.sphinx-doc.org/en/stable/builders.html>`_, but here are a
few suggestions.
* Build an HTML version::
sphinx-build -b html . _build/html
* Build an ePUB3 formatted file, usable on ebook readers::
sphinx-build -b epub3 . _build/epub
* Build a PDF file, which incidentally also builds a LaTeX file::
sphinx-build -b latex %s _build/latex && make -C _build/latex all-pdf
.. _activate-google-custom-search:
Activate Google custom search
Activate Google Custom Search
------------------------------
Using the Google backend requires `BeautifulSoup`_, which you can install