MediaDash/utils.py

197 lines
5.7 KiB
Python

from flask_nav.renderers import Renderer, SimpleRenderer
from dominate import tags
import asteval
import operator as op
import textwrap
import math
import sys
import random
import string
from functools import wraps
from urllib.request import urlopen
from io import BytesIO
import subprocess as SP
import shlex
import json
import os
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
def handle_config(cfg=None):
if cfg is None:
if os.path.isfile("config.json"):
with open("config.json") as fh:
return json.load(fh)
with open("config.json", "w") as fh:
cfg = json.dump(cfg, fh, indent=4)
return
def with_application_context(app):
def inner(func):
@wraps(func)
def wrapper(*args, **kwargs):
with app.app_context():
return func(*args, **kwargs)
return wrapper
return inner
def getsize(text, font_size):
font = ImageFont.truetype("arial.ttf", font_size)
return font.getsize_multiline(text)
def does_text_fit(text, width, height, font_size):
w, h = getsize(text, font_size)
return w < width and h < height
def make_placeholder_image(text, width, height, poster=None, wrap=0):
width = int(width)
height = int(height)
wrap = int(wrap)
font_size = 1
bounds = (0, 1)
if wrap:
text = textwrap.fill(text, wrap)
while True:
if not does_text_fit(text, width, height, bounds[1]):
break
bounds = (bounds[1], bounds[1] * 2)
prev_bounds = None
while True:
if does_text_fit(text, width, height, bounds[1]):
bounds = (int(round(sum(bounds) / 2, 0)), bounds[1])
else:
bounds = (bounds[0], int(round(sum(bounds) / 2, 0)))
if prev_bounds == bounds:
break
prev_bounds = bounds
font_size = bounds[0]
io = BytesIO()
im = Image.new("RGBA", (width, height), "#222")
draw = ImageDraw.Draw(im)
font = ImageFont.truetype("arial.ttf", font_size)
w, h = getsize(text, font_size)
if poster:
try:
with urlopen(poster) as fh:
poster = Image.open(fh)
except Exception as e:
poster = None
else:
poster_size = poster.size
factor = width / poster_size[0]
new_size = (
math.ceil(poster_size[0] * factor),
math.ceil(poster_size[1] * factor),
)
poster = poster.resize(new_size)
mid = -int((poster.size[1] - height) / 2)
im.paste(poster, (0, mid))
draw.text(((width - w) / 2, (height - h) / 2), text, fill="#eee", font=font)
im.save(io, "PNG")
io.seek(0)
return io
def make_tree(files, child_key="children"):
tree = {}
for file in files:
root = tree
parts = file["name"].split("/")
for item in parts:
if item not in root:
root[item] = {}
prev_root = root
root = root[item]
prev_root[item] = {"__info__": file}
return tree
class BootsrapRenderer(Renderer):
def visit_Navbar(self, node):
sub = []
for item in node.items:
sub.append(self.visit(item))
ret = tags.ul(sub, cls="navbar-nav mr-auto")
return ret
def visit_View(self, node):
classes = ["nav-link"]
if node.active:
classes.append("active")
return tags.li(
tags.a(node.text, href=node.get_url(), cls=" ".join(classes)),
cls="nav-item",
)
def visit_Subgroup(self, node):
url = "#"
classes = []
child_active = False
if node.title == "":
active = False
for item in node.items:
if item.active:
classes.append("active")
break
node, *children = node.items
for c in children:
if c.active:
child_active = True
break
node.items = children
node.title = node.text
url = node.get_url()
dropdown = tags.ul(
[
tags.li(
tags.a(
item.text,
href=item.get_url(),
cls="nav-link active" if item.active else "nav-link",
style="",
),
cls="nav-item",
)
for item in node.items
],
cls="dropdown-menu ",
)
link = tags.a(
node.title,
href=url,
cls="nav-link active" if node.active else "nav-link",
style="",
)
toggle = tags.a(
[],
cls="dropdown-toggle nav-link active"
if child_active
else "dropdown-toggle nav-link",
data_toggle="dropdown",
href="#",
style="padding-left: 0px; padding-top: 10px",
)
# almost the same as visit_Navbar, but written a bit more concise
return [link, tags.li([toggle, dropdown], cls="dropdown nav-item")]
def eval_expr(expr, ctx=None):
aeval = asteval.Interpreter(minimal=True, use_numpy=False, symtable=ctx)
return aeval(expr)
def sort_by(values, expr):
return sorted(value, key=lambda v: eval_expr(expr, v))
def genpw(num=20):
return "".join(random.choice(string.ascii_lowercase+string.ascii_uppercase+string.digits) for _ in range(num))