Compare commits

..

2 Commits

11 changed files with 242 additions and 147 deletions

26
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,26 @@
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.

6
CHANGELOG.md Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,23 @@
[build-system]
requires = ["setuptools", "wheel"]
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
Jinja2
Jinja2==3.1.3
ass==0.4.4
negromate.songs==1.2
negromate.songs==1.3

View File

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