mirror of
https://github.com/jellyfin/jellyfin-mpv-shim.git
synced 2024-11-27 00:00:37 +00:00
Replace old trickplay support with new official support.
This commit is contained in:
parent
6660a7f181
commit
2234b0082e
@ -1,53 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
import struct
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
|
||||
BIF_MAGIC = b"\x89BIF\r\n\x1a\n"
|
||||
BIF_SUPPORTED = 0
|
||||
|
||||
|
||||
def decode_file(filename):
|
||||
with open(filename, "rb") as fh:
|
||||
return decode(fh)
|
||||
|
||||
|
||||
def _read_i32(fh):
|
||||
return struct.unpack("<I", fh.read(4))[0]
|
||||
|
||||
|
||||
def decode(fh):
|
||||
if fh.read(8) != BIF_MAGIC:
|
||||
raise ValueError("Data provided is not a BIF file.")
|
||||
|
||||
bif_version = _read_i32(fh)
|
||||
if bif_version != BIF_SUPPORTED:
|
||||
raise ValueError(f"BIF version {bif_version} is not supported.")
|
||||
|
||||
image_count = _read_i32(fh)
|
||||
multiplier = _read_i32(fh)
|
||||
|
||||
fh.read(44) # unused data
|
||||
|
||||
index = [] # timestamp, offset
|
||||
for _ in range(image_count):
|
||||
index.append((_read_i32(fh), _read_i32(fh)))
|
||||
|
||||
images = []
|
||||
for i in range(len(index)):
|
||||
timestamp, offset = index[i]
|
||||
|
||||
if i != timestamp:
|
||||
raise ValueError("BIF file is not contiguous.")
|
||||
|
||||
fh.seek(offset)
|
||||
if i + 1 == len(index):
|
||||
images.append(fh.read())
|
||||
else:
|
||||
images.append(fh.read(index[i + 1][1] - offset))
|
||||
|
||||
return {"multiplier": multiplier, "images": images}
|
||||
|
||||
|
||||
def decompress_tiles(width, height, tile_width, tile_height, count, tiles, fh):
|
||||
image_count = 0
|
||||
@ -97,21 +51,3 @@ def decompress_bif(images, fh):
|
||||
fh.write(image.tobytes())
|
||||
|
||||
return {"count": image_count, "height": height, "width": width}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
bif_data = decode_file(sys.argv[1])
|
||||
print(f"Images: {len(bif_data['images'])}")
|
||||
print(f"Multiplier: {bif_data['multiplier']}")
|
||||
|
||||
# for timestamp, image in enumerate(bif_data["images"]):
|
||||
# with open(f"{timestamp}.jpg", "wb") as fh:
|
||||
# fh.write(image)
|
||||
|
||||
with open("raw_images.bin", "wb") as fh:
|
||||
bif_size_info = decompress_bif(bif_data["images"], fh)
|
||||
|
||||
print(f"Width: {bif_size_info['width']}")
|
||||
print(f"Height: {bif_size_info['height']}")
|
||||
|
@ -130,7 +130,6 @@ class Settings(SettingsBase):
|
||||
skip_intro_always: bool = False
|
||||
skip_intro_prompt: bool = False
|
||||
thumbnail_enable: bool = True
|
||||
thumbnail_jellyscrub: bool = False
|
||||
thumbnail_osc_builtin: bool = True
|
||||
thumbnail_preferred_size: int = 320
|
||||
|
||||
|
@ -271,42 +271,29 @@ class Video(object):
|
||||
yield data.getvalue()
|
||||
|
||||
def get_hls_tile_images(self, width, count):
|
||||
for i in range(1, count + 1):
|
||||
for i in range(0, count):
|
||||
data = BytesIO()
|
||||
self.client.jellyfin._get_stream(
|
||||
f"Trickplay/{self.media_source['Id']}/{width}/{i}.jpg", data
|
||||
f"Videos/{self.item['Id']}/Trickplay/{width}/{i}.jpg?MediaSourceId={self.media_source['Id']}", data
|
||||
)
|
||||
yield data.getvalue()
|
||||
|
||||
def get_bif(self, prefer_width=320):
|
||||
# requires JellyScrub plugin
|
||||
manifest = self.client.jellyfin._get(
|
||||
f"Trickplay/{self.media_source['Id']}/GetManifest"
|
||||
)
|
||||
manifest = self.item.get("Trickplay")
|
||||
print(manifest)
|
||||
if (
|
||||
manifest is not None
|
||||
and manifest.get("WidthResolutions") is not None
|
||||
and len(manifest["WidthResolutions"]) > 0
|
||||
and manifest.get(self.media_source['Id']) is not None
|
||||
and len(manifest[self.media_source['Id']]) > 0
|
||||
):
|
||||
available_widths = manifest["WidthResolutions"]
|
||||
if type(manifest["WidthResolutions"]) is dict:
|
||||
available_widths = [int(x) for x in manifest["WidthResolutions"].keys()]
|
||||
available_widths = [int(x) for x in manifest[self.media_source['Id']].keys()]
|
||||
|
||||
if prefer_width is not None:
|
||||
width = min(available_widths, key=lambda x: abs(x - prefer_width))
|
||||
else:
|
||||
width = max(available_widths)
|
||||
|
||||
if type(manifest["WidthResolutions"]) is dict:
|
||||
return manifest["WidthResolutions"][str(width)]
|
||||
else:
|
||||
data = BytesIO()
|
||||
self.client.jellyfin._get_stream(
|
||||
f"Trickplay/{self.media_source['Id']}/{width}/GetBIF", data
|
||||
)
|
||||
|
||||
data.seek(0)
|
||||
return data
|
||||
return manifest[self.media_source['Id']][str(width)]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -558,9 +558,6 @@ class OSDMenu(object):
|
||||
self.get_settings_toggle(
|
||||
_("Enable thumbnail previews"), "thumbnail_enable"
|
||||
),
|
||||
self.get_settings_toggle(
|
||||
_("Use JellyScrub thumbnails"), "thumbnail_jellyscrub"
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -50,77 +50,67 @@ class TrickPlay(threading.Thread):
|
||||
continue
|
||||
|
||||
video = self.player.get_video()
|
||||
if settings.thumbnail_jellyscrub:
|
||||
try:
|
||||
data = video.get_bif(settings.thumbnail_preferred_size)
|
||||
try:
|
||||
data = video.get_bif(settings.thumbnail_preferred_size)
|
||||
if (
|
||||
not self.player.has_video()
|
||||
or video != self.player.get_video()
|
||||
):
|
||||
# Video changed while we were getting the bif file
|
||||
continue
|
||||
|
||||
if data:
|
||||
with open(img_file, "wb") as fh:
|
||||
img_count = math.ceil(
|
||||
data["ThumbnailCount"]
|
||||
/ data["TileWidth"]
|
||||
/ data["TileHeight"]
|
||||
)
|
||||
bifdecode.decompress_tiles(
|
||||
data["Width"],
|
||||
data["Height"],
|
||||
data["TileWidth"],
|
||||
data["TileHeight"],
|
||||
data["ThumbnailCount"],
|
||||
video.get_hls_tile_images(
|
||||
data["Width"], img_count
|
||||
),
|
||||
fh,
|
||||
)
|
||||
|
||||
bif_meta = {
|
||||
"count": data["ThumbnailCount"],
|
||||
"multiplier": data["Interval"],
|
||||
"width": data["Width"],
|
||||
"height": data["Height"],
|
||||
}
|
||||
|
||||
if (
|
||||
not self.player.has_video()
|
||||
or video != self.player.get_video()
|
||||
):
|
||||
# Video changed while we were getting the bif file
|
||||
# Video changed while we were decompressing the bif file
|
||||
continue
|
||||
|
||||
if data:
|
||||
if type(data) is not dict:
|
||||
bif = bifdecode.decode(data)
|
||||
|
||||
with open(img_file, "wb") as fh:
|
||||
bif_meta = bifdecode.decompress_bif(
|
||||
bif["images"], fh
|
||||
)
|
||||
bif_meta["multiplier"] = bif["multiplier"]
|
||||
else:
|
||||
with open(img_file, "wb") as fh:
|
||||
img_count = math.ceil(
|
||||
data["TileCount"]
|
||||
/ data["TileWidth"]
|
||||
/ data["TileHeight"]
|
||||
)
|
||||
bifdecode.decompress_tiles(
|
||||
data["Width"],
|
||||
data["Height"],
|
||||
data["TileWidth"],
|
||||
data["TileHeight"],
|
||||
data["TileCount"],
|
||||
video.get_hls_tile_images(
|
||||
data["Width"], img_count
|
||||
),
|
||||
fh,
|
||||
)
|
||||
|
||||
bif_meta = {
|
||||
"count": data["TileCount"],
|
||||
"multiplier": data["Interval"],
|
||||
"width": data["Width"],
|
||||
"height": data["Height"],
|
||||
}
|
||||
|
||||
if (
|
||||
not self.player.has_video()
|
||||
or video != self.player.get_video()
|
||||
):
|
||||
# Video changed while we were decompressing the bif file
|
||||
continue
|
||||
|
||||
self.player.script_message(
|
||||
"shim-trickplay-bif",
|
||||
str(bif_meta["count"]),
|
||||
str(bif_meta["multiplier"]),
|
||||
str(bif_meta["width"]),
|
||||
str(bif_meta["height"]),
|
||||
img_file,
|
||||
)
|
||||
log.info(
|
||||
f"Collected {bif_meta['count']} bif preview images"
|
||||
)
|
||||
continue
|
||||
else:
|
||||
log.warning("No bif file available")
|
||||
except:
|
||||
log.error(
|
||||
"Could not get bif file. Do you have the plugin installed?",
|
||||
exc_info=True,
|
||||
self.player.script_message(
|
||||
"shim-trickplay-bif",
|
||||
str(bif_meta["count"]),
|
||||
str(bif_meta["multiplier"]),
|
||||
str(bif_meta["width"]),
|
||||
str(bif_meta["height"]),
|
||||
img_file,
|
||||
)
|
||||
log.info(
|
||||
f"Collected {bif_meta['count']} trickplay preview images"
|
||||
)
|
||||
continue
|
||||
else:
|
||||
log.warning("No trickplay data available")
|
||||
except:
|
||||
log.error(
|
||||
"Could not get trickplay data.",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
chapter_data = video.get_chapters()
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user