Compare commits

..

No commits in common. "72c9855cbe4fed6aeef057d8fe9ce4602d8d03bd" and "d59a38721ee01f7822def002804bacc171cbfaf5" have entirely different histories.

11 changed files with 147 additions and 242 deletions

View File

@ -1,26 +0,0 @@
repos:
# Using this mirror lets us use mypyc-compiled black, which is about 2x faster
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.2.0
hooks:
- id: black
# It is recommended to specify the latest version of Python
# supported by your project here, or alternatively use
# pre-commit's default_language_version, see
# https://pre-commit.com/#top_level-default_language_version
language_version: python3.11
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
name: isort (python)
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.3.0
hooks:
# Run the linter.
- id: ruff
# - id: ruff-format We don't need this because we have black and isort.

View File

@ -1,6 +0,0 @@
# Changelog
## 1.1
* chore: pre-commit configuration
* fix: fix jinja dependency and apply ruff formating

View File

@ -1 +1 @@
VERSION = "1.1" VERSION = "1.0"

View File

@ -1,10 +1,11 @@
import os import os
import subprocess
from shutil import copy from shutil import copy
import subprocess
import srt
from jinja2 import Environment, FileSystemLoader, select_autoescape from jinja2 import Environment, FileSystemLoader, select_autoescape
from markupsafe import Markup from jinja2.utils import Markup
import srt
from negromate.songs.loader import load_songs from negromate.songs.loader import load_songs
@ -15,23 +16,23 @@ class SongPage:
def get_context_data(self): def get_context_data(self):
parsed_srt = None parsed_srt = None
if self.song.srt: if self.song.srt:
with self.song.srt.open("r") as srtfile: with self.song.srt.open('r') as srtfile:
try: try:
srt_str = srtfile.read().encode("utf-8").decode("utf-8-sig") srt_str = srtfile.read().encode('utf-8').decode('utf-8-sig')
parsed_srt = list(srt.parse(srt_str)) parsed_srt = list(srt.parse(srt_str))
except Exception as e: except Exception as e:
print("{}: srt parse error: {}".format(self.song.path.name, e)) print("{}: srt parse error: {}".format(self.song.path.name, e))
root_path = os.path.relpath(self.song.root, self.song.path) root_path = os.path.relpath(self.song.root, self.song.path)
return { return {
"song": self, 'song': self,
"parsed_srt": parsed_srt, 'parsed_srt': parsed_srt,
"root_path": root_path, 'root_path': root_path,
} }
def render(self, builder, context): def render(self, builder, context):
ctx = self.get_context_data() ctx = self.get_context_data()
ctx.update(context) ctx.update(context)
builder.render("song.html", self.song.path, ctx) builder.render('song.html', self.song.path, ctx)
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self.song, name) return getattr(self.song, name)
@ -46,10 +47,10 @@ class Builder:
self.env = Environment( self.env = Environment(
loader=FileSystemLoader(template_folder), loader=FileSystemLoader(template_folder),
autoescape=select_autoescape(["html"]), autoescape=select_autoescape(['html']),
) )
self.env.filters["url"] = self.url self.env.filters['url'] = self.url
self.env.filters["display_boolean"] = self.display_boolean self.env.filters['display_boolean'] = self.display_boolean
self.current_path = self.root_folder self.current_path = self.root_folder
def url(self, path): def url(self, path):
@ -57,17 +58,17 @@ class Builder:
def display_boolean(self, value): def display_boolean(self, value):
if value: if value:
return Markup("✓") return Markup('✓')
else: else:
return Markup("✗") return Markup('✗')
def render(self, template, target, context): def render(self, template, target, context):
html_file = target / "index.html" html_file = target / 'index.html'
page_template = self.env.get_template(template) page_template = self.env.get_template(template)
root_path = os.path.relpath(self.root_folder, target) root_path = os.path.relpath(self.root_folder, target)
context["root_path"] = root_path context['root_path'] = root_path
with html_file.open("w") as page: with html_file.open('w') as page:
page.write(page_template.render(context)) page.write(page_template.render(context))
def build(self): def build(self):
@ -76,52 +77,50 @@ class Builder:
pending_songs = [SongPage(s) for s in pending_songs] pending_songs = [SongPage(s) for s in pending_songs]
global_context = { global_context = {
"songs": songs, 'songs': songs,
"root_folder": self.root_folder, 'root_folder': self.root_folder,
} }
for song in songs: for song in songs:
self.current_path = song.path self.current_path = song.path
song.render(self, global_context) song.render(self, global_context)
self.render("index.html", self.root_folder, global_context) self.render('index.html', self.root_folder, global_context)
home = self.root_folder / "home" home = self.root_folder / 'home'
self.current_path = home self.current_path = home
if not home.exists(): if not home.exists():
home.mkdir() home.mkdir()
self.render("home.html", home, global_context) self.render('home.html', home, global_context)
playlist = self.root_folder / "playlist" playlist = self.root_folder / 'playlist'
self.current_path = playlist self.current_path = playlist
if not playlist.exists(): if not playlist.exists():
playlist.mkdir() playlist.mkdir()
self.render("playlist.html", playlist, global_context) self.render('playlist.html', playlist, global_context)
todo = self.root_folder / "todo" todo = self.root_folder / 'todo'
self.current_path = todo self.current_path = todo
if not todo.exists(): if not todo.exists():
todo.mkdir() todo.mkdir()
todo_context = { todo_context = {
"pending_songs": pending_songs, 'pending_songs': pending_songs,
} }
todo_context.update(global_context) todo_context.update(global_context)
self.render("todo.html", todo, todo_context) self.render('todo.html', todo, todo_context)
static = self.root_folder / "static" static = self.root_folder / 'static'
if not static.exists(): if not static.exists():
static.mkdir() static.mkdir()
subprocess.check_call( subprocess.check_call([
[ 'rsync',
"rsync", '-ra',
"-ra",
str(self.static_folder), str(self.static_folder),
str(self.root_folder.absolute()), str(self.root_folder.absolute()),
] ])
)
libreto = self.root_folder / "static/libreto/libreto.pdf" libreto = self.root_folder / 'static/libreto/libreto.pdf'
copy(str(self.libreto.absolute()), str(libreto.absolute())) copy(str(self.libreto.absolute()), str(libreto.absolute()))

View File

@ -1,45 +1,36 @@
from pathlib import Path from pathlib import Path
from ..builder import Builder from ..builder import Builder
name = "build" name = 'build'
help_text = "Generate static website" help_text = 'Generate static website'
initial_config = { initial_config = {
"template_folder": "~/negro_mate/web/templates", 'template_folder': '~/negro_mate/web/templates',
"static_folder": "~/negro_mate/web/static", 'static_folder': '~/negro_mate/web/static',
} }
def options(parser, config, **kwargs): def options(parser, config, **kwargs):
parser.add_argument( parser.add_argument(
"-s", '-s', '--song_folder', type=Path,
"--song_folder", default=config['global']['song_folder'],
type=Path, help="Folder with the song database, defaults to {}".format(
default=config["global"]["song_folder"], config['global']['song_folder']))
help="Folder with the song database, defaults to {}".format(config["global"]["song_folder"]),
)
parser.add_argument( parser.add_argument(
"-l", '-l', '--lyrics_file', type=Path,
"--lyrics_file", default=config['global']['lyrics_file'],
type=Path, help="File with the lyrics of the songs, defaults to {}".format(
default=config["global"]["lyrics_file"], config['global']['lyrics_file']))
help="File with the lyrics of the songs, defaults to {}".format(config["global"]["lyrics_file"]),
)
parser.add_argument( parser.add_argument(
"-t", '-t', '--template_folder', type=Path,
"--template_folder", default=config['build']['template_folder'],
type=Path, help="Folder with jinja2 templates, defaults to {}".format(
default=config["build"]["template_folder"], config['build']['template_folder']))
help="Folder with jinja2 templates, defaults to {}".format(config["build"]["template_folder"]),
)
parser.add_argument( parser.add_argument(
"-S", '-S', '--static_folder', type=Path,
"--static_folder", default=config['build']['static_folder'],
type=Path, help="Folder with static content, defaults to {}".format(
default=config["build"]["static_folder"], config['build']['static_folder']))
help="Folder with static content, defaults to {}".format(config["build"]["static_folder"]),
)
def run(args, **kwargs): def run(args, **kwargs):

View File

@ -1,52 +1,48 @@
from pathlib import Path
import getpass import getpass
import subprocess import subprocess
import urllib.request import urllib.request
from pathlib import Path
from negromate.songs import logger from negromate.songs import logger
name = "ipfs" name = 'ipfs'
help_text = "Upload the web to IPFS" help_text = 'Upload the web to IPFS'
initial_config = { initial_config = {
"api": "http://ipfs.negromate.rocks", 'api': 'http://ipfs.negromate.rocks',
"pinfile": "~/.negromate/ipfs.hash", 'pinfile': '~/.negromate/ipfs.hash',
"realm": "IPFS Gitea Negromate", 'realm': 'IPFS Gitea Negromate',
} }
def options(parser, config, **kwargs): def options(parser, config, **kwargs):
parser.add_argument( parser.add_argument(
"-s", '-s', '--song_folder', type=Path,
"--song_folder", default=config['global']['song_folder'],
type=Path, help="Folder with the song database, defaults to {}".format(
default=config["global"]["song_folder"], config['global']['song_folder']))
help="Folder with the song database, defaults to {}".format(config["global"]["song_folder"]),
)
parser.add_argument( parser.add_argument(
"-a", "--api", default=config[name]["api"], help="IPFS API server, defaults to {}.".format(config[name]["api"]) '-a', '--api', default=config[name]['api'],
) help="IPFS API server, defaults to {}.".format(config[name]['api']))
parser.add_argument( parser.add_argument(
"-r", '-r', '--realm', default=config[name]['realm'],
"--realm", help="IPFS API basic authentication realm, defaults to {}.".format(config[name]['realm']))
default=config[name]["realm"],
help="IPFS API basic authentication realm, defaults to {}.".format(config[name]["realm"]),
)
parser.add_argument( parser.add_argument(
"-p", '-p', '--pinfile', default=config[name]['pinfile'], type=Path,
"--pinfile", help="file to store the current ipfs hash, defaults to {}".format(
default=config[name]["pinfile"], config[name]['pinfile']))
type=Path,
help="file to store the current ipfs hash, defaults to {}".format(config[name]["pinfile"]),
)
def run(args, **kwargs): def run(args, **kwargs):
# Setup HTTP Basic authentication # Setup HTTP Basic authentication
user = input("Username: ") user = input('Username: ')
password = getpass.getpass("Password:") password = getpass.getpass('Password:')
auth_handler = urllib.request.HTTPBasicAuthHandler() auth_handler = urllib.request.HTTPBasicAuthHandler()
auth_handler.add_password(realm=args.realm, uri=args.api, user=user, passwd=password) auth_handler.add_password(
realm=args.realm,
uri=args.api,
user=user,
passwd=password)
opener = urllib.request.build_opener(auth_handler) opener = urllib.request.build_opener(auth_handler)
urllib.request.install_opener(opener) urllib.request.install_opener(opener)
@ -58,41 +54,37 @@ def run(args, **kwargs):
"--quieter", "--quieter",
args.song_folder.expanduser(), args.song_folder.expanduser(),
] ]
new_hash = subprocess.check_output(command).decode("utf-8").strip() new_hash = subprocess.check_output(command).decode('utf-8').strip()
logger.info("New hash: {}".format(new_hash)) logger.info('New hash: {}'.format(new_hash))
# pin in server # pin in server
data = urllib.parse.urlencode( data = urllib.parse.urlencode({
{ 'arg': new_hash,
"arg": new_hash, 'progress': 'false',
"progress": "false", })
} url = '{}/api/v0/pin/add?{}'.format(args.api, data)
)
url = "{}/api/v0/pin/add?{}".format(args.api, data)
logger.debug("server pin request: {}".format(url)) logger.debug("server pin request: {}".format(url))
request = urllib.request.Request(url, method="POST") request = urllib.request.Request(url, method='POST')
urllib.request.urlopen(request) urllib.request.urlopen(request)
logger.info("Hash pinned on server.") logger.info('Hash pinned on server.')
# update ipns on server # update ipns on server
data = urllib.parse.urlencode( data = urllib.parse.urlencode({
{ 'arg': new_hash,
"arg": new_hash, 'resolve': 'true',
"resolve": "true", })
}
)
url = "{}/api/v0/name/publish?{}".format(args.api, data) url = "{}/api/v0/name/publish?{}".format(args.api, data)
logger.debug("server ipns request: {}".format(url)) logger.debug("server ipns request: {}".format(url))
request = urllib.request.Request(url, method="POST") request = urllib.request.Request(url, method='POST')
urllib.request.urlopen(request) urllib.request.urlopen(request)
logger.info("IPNS name updated.") logger.info('IPNS name updated.')
# read previous hash and update value # read previous hash and update value
pinfile = args.pinfile.expanduser() pinfile = args.pinfile.expanduser()
if pinfile.exists(): if pinfile.exists():
with pinfile.open() as f: with pinfile.open() as f:
previous_hash = f.read() previous_hash = f.read()
logger.info("Previous hash: {}".format(previous_hash)) logger.info('Previous hash: {}'.format(previous_hash))
else: else:
if not pinfile.parent.exists(): if not pinfile.parent.exists():
pinfile.parent.mkdir() pinfile.parent.mkdir()
@ -101,28 +93,26 @@ def run(args, **kwargs):
if previous_hash is not None and previous_hash != new_hash: if previous_hash is not None and previous_hash != new_hash:
# remove previous pin on local # remove previous pin on local
command = [ command = [
"ipfs", 'ipfs',
"pin", 'pin',
"rm", 'rm',
previous_hash, previous_hash,
] ]
result = subprocess.run(command) result = subprocess.run(command)
if result.returncode != 0: if result.returncode != 0:
logger.info("Previous {} hash not removed: {}".format(previous_hash, result.stdout)) logger.info('Previous {} hash not removed: {}'.format(previous_hash, result.stdout))
else: else:
logger.info("Previous hash unpinned on local") logger.info('Previous hash unpinned on local')
# remove previous pin on server # remove previous pin on server
data = urllib.parse.urlencode( data = urllib.parse.urlencode({
{ 'arg': previous_hash,
"arg": previous_hash, })
}
)
url = "{}/api/v0/pin/rm?{}".format(args.api, data) url = "{}/api/v0/pin/rm?{}".format(args.api, data)
logger.debug("server unpin request: {}".format(url)) logger.debug("server unpin request: {}".format(url))
request = urllib.request.Request(url, method="POST") request = urllib.request.Request(url, method='POST')
urllib.request.urlopen(request) urllib.request.urlopen(request)
logger.info("Previous hash unpinned on server") logger.info('Previous hash unpinned on server')
with pinfile.open("w") as f: with pinfile.open('w') as f:
f.write(new_hash) f.write(new_hash)

View File

@ -1,51 +1,38 @@
import subprocess import subprocess
from pathlib import Path from pathlib import Path
name = 'rsync'
name = "rsync" help_text = 'Sincronize the static web with the server'
help_text = "Sincronize the static web with the server"
initial_config = { initial_config = {
"host": "negromate.rocks", 'host': 'negromate.rocks',
"user": "root", 'user': 'root',
"port": "22", 'port': '22',
"destination": "/var/www/html", 'destination': "/var/www/html",
} }
def options(parser, config, **kwargs): def options(parser, config, **kwargs):
parser.add_argument( parser.add_argument(
"-s", '-s', '--song_folder', type=Path,
"--song_folder", default=config['global']['song_folder'],
type=Path, help="Folder with the song database, defaults to {}".format(
default=config["global"]["song_folder"], config['global']['song_folder']))
help="Folder with the song database, defaults to {}".format(config["global"]["song_folder"]),
)
parser.add_argument( parser.add_argument(
"-H", "--host", default=config[name]["host"], help="Target server, defaults to {}.".format(config[name]["host"]) '-H', '--host', default=config[name]['host'],
) help="Target server, defaults to {}.".format(config[name]['host']))
parser.add_argument( parser.add_argument(
"-u", '-u', '--user', default=config[name]['user'],
"--user", help="User in the server, defaults to {}.".format(config[name]['user']))
default=config[name]["user"],
help="User in the server, defaults to {}.".format(config[name]["user"]),
)
parser.add_argument( parser.add_argument(
"-p", '-p', '--port', default=config[name]['port'], type=int,
"--port", help="Port of the ssh server, defaults to {}.".format(config[name]['port']))
default=config[name]["port"],
type=int,
help="Port of the ssh server, defaults to {}.".format(config[name]["port"]),
)
parser.add_argument( parser.add_argument(
"-d", '-d', '--destination', default=config[name]['destination'],
"--destination", help="Folder of the server, defaults to {}".format(config[name]['destination']))
default=config[name]["destination"],
help="Folder of the server, defaults to {}".format(config[name]["destination"]),
)
def run(args, **kwargs): def run(args, **kwargs):
contents = str(args.song_folder.expanduser()) + "/" contents = str(args.song_folder.expanduser()) + '/'
destination = "{user}@{host}:{folder}".format( destination = "{user}@{host}:{folder}".format(
user=args.user, user=args.user,
host=args.host, host=args.host,
@ -54,7 +41,7 @@ def run(args, **kwargs):
command = [ command = [
"rsync", "rsync",
"-av", "-av",
"--rsh=ssh -p {}".format(args.port), '--rsh=ssh -p {}'.format(args.port),
contents, contents,
destination, destination,
] ]

View File

@ -1,40 +1,30 @@
from functools import partial from functools import partial
from http.server import SimpleHTTPRequestHandler, test from http.server import test, SimpleHTTPRequestHandler
from pathlib import Path from pathlib import Path
name = "run" name = 'run'
help_text = "Start web server to test the website" help_text = 'Start web server to test the website'
initial_config = { initial_config = {
"port": "8000", 'port': '8000',
"bind": "", 'bind': '',
} }
def options(parser, config, **kwargs): def options(parser, config, **kwargs):
parser.add_argument( parser.add_argument(
"-s", '-s', '--song_folder', type=Path,
"--song_folder", default=config['global']['song_folder'],
type=Path, help="Folder with the song database, defaults to {}".format(
default=config["global"]["song_folder"], config['global']['song_folder']))
help="Folder with the song database, defaults to {}".format(config["global"]["song_folder"]),
)
parser.add_argument( parser.add_argument(
"-p", '-p', '--port', default=config[name]['port'], type=int,
"--port", help='Specify alternate port, defaults to {}'.format(config[name]['port']))
default=config[name]["port"],
type=int,
help="Specify alternate port, defaults to {}".format(config[name]["port"]),
)
parser.add_argument( parser.add_argument(
"--bind", '--bind', '-b', default=config[name]['bind'], metavar='ADDRESS',
"-b", help='Specify alternate bind address, defaults to {}'.format(
default=config[name]["bind"], config[name]['bind'] or 'all interfaces',
metavar="ADDRESS", ))
help="Specify alternate bind address, defaults to {}".format(
config[name]["bind"] or "all interfaces",
),
)
def run(args, **kwargs): def run(args, **kwargs):

View File

@ -1,23 +1,3 @@
[build-system] [build-system]
requires = ["setuptools", "wheel"] requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta" build-backend = "setuptools.build_meta"
[tool.black]
line_length = 120
[tool.isort]
profile = "black"
lines_after_imports = 2
[tool.pylint.'MESSAGES CONTROL']
max-line-length = 120
disable = "invalid-name, unused-wildcard-import, wildcard-import"
[tool.ruff]
line-length = 120
exclude = [
"build",
]
include = ["negromate/*"]
fix = false
force-exclude = true

View File

@ -1,4 +1,4 @@
webvtt-py webvtt-py
Jinja2==3.1.3 Jinja2
ass==0.4.4 ass==0.4.4
negromate.songs==1.3 negromate.songs==1.2

View File

@ -20,9 +20,9 @@ packages = find_namespace:
zip_safe = false zip_safe = false
python_requires = >= 3.4 python_requires = >= 3.4
install_requires = install_requires =
Jinja2 ==3.1.3 Jinja2
ass ==0.5.2 ass ==0.5.2
negromate.songs >=1.4 negromate.songs ==1.3
[options.entry_points] [options.entry_points]
negromate.commands = negromate.commands =