diff --git a/MANIFEST.in b/MANIFEST.in index 97464d1..ae4b02e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include README.md -include beetsplug/webm3u/config_default.yaml +include beetsplug/webm3u/templates/*.html +include beetsplug/webm3u/static/** diff --git a/beetsplug/webm3u/__init__.py b/beetsplug/webm3u/__init__.py index 9789944..44b242a 100644 --- a/beetsplug/webm3u/__init__.py +++ b/beetsplug/webm3u/__init__.py @@ -1,4 +1,4 @@ -from flask import Flask +from flask import Flask, render_template from beets.plugins import BeetsPlugin from beets.ui import Subcommand, decargs from optparse import OptionParser @@ -6,7 +6,7 @@ from beetsplug.webm3u.routes import bp -class PlaylistServerPlugin(BeetsPlugin): +class WebM3UPlugin(BeetsPlugin): def __init__(self): super().__init__() self.config.add( @@ -63,7 +63,7 @@ def create_app(): @app.route('/') def home(): - return '' + return render_template('index.html') app.register_blueprint(bp) diff --git a/beetsplug/webm3u/routes.py b/beetsplug/webm3u/routes.py index cbc6340..7e1c58c 100644 --- a/beetsplug/webm3u/routes.py +++ b/beetsplug/webm3u/routes.py @@ -1,13 +1,16 @@ import os -from flask import Flask, Blueprint, send_from_directory, send_file, abort +from flask import Flask, Blueprint, send_from_directory, send_file, abort, render_template, request, jsonify from beets import config +from pathlib import Path +MIMETYPE_HTML = 'text/html' +MIMETYPE_JSON = 'application/json' bp = Blueprint('webm3u_bp', __name__, template_folder='templates') @bp.route('/playlists/', defaults={'path': ''}) @bp.route('/playlists/') -def index_page(path): +def playlists(path): root_dir = config['webm3u']['playlist_dir'].get() if not root_dir: root_dir = config['smartplaylist']['playlist_dir'].get() @@ -19,12 +22,64 @@ def index_page(path): # TODO: transform item URIs within playlist return send_file(abs_path) else: - # Generate html/json directory listing - return 'playlist dir' - #return send_from_directory(root_dir, path, as_attachment=True) + pl = _playlists(abs_path) + dirs = _directories(abs_path) + mimetypes = (MIMETYPE_JSON, MIMETYPE_HTML) + mimetype = request.accept_mimetypes.best_match(mimetypes, MIMETYPE_JSON) + if mimetype == MIMETYPE_HTML: + return render_template('playlists.html', + path=path, + playlists=pl, + directories=dirs, + humanize=_humanize_size, + ) + else: + return jsonify({ + 'directories': [{'name': d} for d in dirs], + 'playlists': pl, + }) + +@bp.route('/music/', defaults={'path': ''}) +@bp.route('/music/') +def music(path): + root_dir = config['directory'].get() + return send_from_directory(root_dir, path) + +def _playlists(dir): + l = [f for f in os.listdir(dir) if _is_playlist(dir, f)] + l.sort() + return [_playlist_dto(dir, f) for f in l] + +def _playlist_dto(dir, filename): + st = os.stat(os.path.join(dir, filename)) + return { + 'name': Path(filename).stem, + 'path': filename, + 'size': st.st_size, + } + +def _is_playlist(dir, filename): + f = os.path.join(dir, filename) + return os.path.isfile(f) and (f.endswith('.m3u') or f.endswith('.m3u8')) + +def _directories(dir): + l = [d for d in os.listdir(dir) if os.path.isdir(_join(dir, d))] + l.sort() + return l + +def _join(dir, filename): + return os.path.join(dir, filename) def _check_path(root_dir, path): path = os.path.normpath(path) root_dir = os.path.normpath(root_dir) if path != root_dir and not path.startswith(root_dir+os.sep): raise Exception('request path {} is outside the root directory {}'.format(path, root_dir)) + +def _humanize_size(num): + suffix = 'B' + for unit in ("", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"): + if abs(num) < 1024.0: + return f"{num:3.1f}{unit}{suffix}" + num /= 1024.0 + return f"{num:.1f}Yi{suffix}" diff --git a/beetsplug/webm3u/static/style.css b/beetsplug/webm3u/static/style.css new file mode 100644 index 0000000..aff351c --- /dev/null +++ b/beetsplug/webm3u/static/style.css @@ -0,0 +1,23 @@ +body { + font-family: Verdana, sans-serif; + font-size: 1.2em; + line-height: 1.5em; +} +ul, li { + margin: 0; + padding: 0; + list-style-type: none; +} +li a { + display: block; + padding: 0.5em 1em; +} +li a span { + font-size: 0.7em +} +li:nth-child(odd) { + background-color: #fafafa; +} +li a:hover, li a:active { + background: #eeeeee; +} diff --git a/beetsplug/webm3u/templates/index.html b/beetsplug/webm3u/templates/index.html new file mode 100644 index 0000000..446e0fd --- /dev/null +++ b/beetsplug/webm3u/templates/index.html @@ -0,0 +1,21 @@ + + + + + + Beets music library + + + +

Beets music library

+ +

+ 🢘 back +

+ + + + diff --git a/beetsplug/webm3u/templates/playlists.html b/beetsplug/webm3u/templates/playlists.html new file mode 100644 index 0000000..c2da6ac --- /dev/null +++ b/beetsplug/webm3u/templates/playlists.html @@ -0,0 +1,34 @@ + + + + + + Playlists + + + +

Playlists

+ +

+ 🢘 back +

+ + + + \ No newline at end of file diff --git a/example_beets_config.yaml b/example_beets_config.yaml index 68fb008..b59ac0d 100644 --- a/example_beets_config.yaml +++ b/example_beets_config.yaml @@ -24,6 +24,8 @@ smartplaylist: playlists: - name: all.m3u8 query: '' + - name: subdir/another.m3u8 + query: '' ytimport: directory: /data/ytimport