jellycon/resources/lib/image_server.py
2023-01-12 21:02:32 -05:00

210 lines
5.6 KiB
Python

from __future__ import (
division, absolute_import, print_function, unicode_literals
)
import threading
import io
import base64
import re
from random import shuffle
import xbmcvfs
import xbmc
import xbmcaddon
import requests
from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from six.moves.urllib.parse import urlparse
from six import ensure_text
from .jellyfin import api
from .lazylogger import LazyLogger
from .item_functions import get_art
from .utils import translate_path
pil_loaded = False
try:
from PIL import Image, ImageOps
pil_loaded = True
except ImportError:
pil_loaded = False
PORT_NUMBER = 24276
log = LazyLogger(__name__)
def get_image_links(url):
settings = xbmcaddon.Addon()
server = settings.getSetting('server_address')
if server is None:
return []
url = re.sub("(?i)EnableUserData=[a-z]+", "EnableUserData=False", url)
url = re.sub("(?i)EnableImageTypes=[,a-z]+", "EnableImageTypes=Primary", url)
url = url.replace("{field_filters}", "BasicSyncInfo")
url = re.sub("(?i)Fields=[,a-z]+", "Fields=BasicSyncInfo", url)
if not re.search('enableimagetypes=', url, re.IGNORECASE):
url += "&EnableImageTypes=Primary"
if not re.search('fields=', url, re.IGNORECASE):
url += "&Fields=BasicSyncInfo"
if not re.search('EnableUserData=', url, re.IGNORECASE):
url += "&EnableUserData=False"
result = api.get(url)
items = result.get("Items")
if not items:
return []
art_urls = []
for iteem in items:
art = get_art(item=iteem, server=server)
art_urls.append(art)
shuffle(art_urls)
return art_urls
def build_image(path):
log.debug("build_image()")
log.debug("Request Path : {0}".format(path))
request_path = path[1:]
if request_path == "favicon.ico":
return []
decoded_url = ensure_text(base64.b64decode(request_path))
log.debug("decoded_url : {0}".format(decoded_url))
image_urls = get_image_links(decoded_url)
width, height = 500, 750
collage = Image.new('RGB', (width, height), (5, 5, 5))
cols = 2
rows = 2
thumbnail_width = int(width / cols)
thumbnail_height = int(height / rows)
size = (thumbnail_width, thumbnail_height)
image_count = 0
for art in image_urls:
thumb_url = art.get("thumb")
if thumb_url:
url_bits = urlparse(thumb_url.strip())
host_name = url_bits.hostname
port = url_bits.port
url_path = url_bits.path
url_query = url_bits.query
server = "%s:%s" % (host_name, port)
url_full_path = url_path + "?" + url_query
log.debug("Loading image from : {0} {1} {2}".format(image_count, server, url_full_path))
try:
image_response = requests.get(thumb_url)
image_data = image_response.content
loaded_image = Image.open(io.BytesIO(image_data))
image = ImageOps.fit(loaded_image, size, method=Image.ANTIALIAS, bleed=0.0, centering=(0.5, 0.5))
x = int(image_count % cols) * thumbnail_width
y = int(image_count/cols) * thumbnail_height
collage.paste(image, (x, y))
del loaded_image
del image
del image_data
except Exception as con_err:
log.debug("Error loading image : {0}".format(con_err))
image_count += 1
if image_count == cols * rows:
break
del image_urls
img_byte_arr = io.BytesIO()
collage.save(img_byte_arr, format='JPEG')
image_bytes = img_byte_arr.getvalue()
return image_bytes
class HttpImageHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
log_line = format % args
log.debug(log_line)
return
def do_GET(self):
log.debug("HttpImageHandler:do_GET()")
self.serve_image()
return
def do_HEAD(self):
log.debug("HttpImageHandler:do_HEAD()")
self.send_response(200)
self.end_headers()
return
def serve_image(self):
if pil_loaded:
image_bytes = build_image(self.path)
self.send_response(200)
self.send_header('Content-type', 'image/jpeg')
self.send_header('Content-Length', str(len(image_bytes)))
self.end_headers()
self.wfile.write(image_bytes)
else:
image_path = translate_path("special://home/addons/plugin.video.jellycon/icon.png").decode('utf-8')
self.send_response(200)
self.send_header('Content-type', 'image/png')
modified = xbmcvfs.Stat(image_path).st_mtime()
self.send_header('Last-Modified', "%s" % modified)
image = xbmcvfs.File(image_path)
size = image.size()
self.send_header('Content-Length', str(size))
self.end_headers()
self.wfile.write(image.readBytes())
image.close()
del image
class HttpImageServerThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.keep_running = True
def stop(self):
log.debug("HttpImageServerThread:stop called")
self.keep_running = False
self.server.shutdown()
def run(self):
log.debug("HttpImageServerThread:started")
self.server = HTTPServer(('', PORT_NUMBER), HttpImageHandler)
while self.keep_running:
self.server.serve_forever()
xbmc.sleep(1000)
log.debug("HttpImageServerThread:exiting")