-
-
Notifications
You must be signed in to change notification settings - Fork 31
Description
Thanks for the great project! I was exploring streaming videos managed by YouTarr hosted via a WebDAV service, and discovered that it is very slow to index all of the subfolders. Each folder requires a new request, so with ~100 channels and 5 video history it can take a very long time to figure out what videos are available, and even longer to load all the .nfo and thumbnail files individually. I wrote a small script to make a single index.json file, which embeds the nfo and thumbnail data, so loading all the available video data in a folder is as quick as a single file download.
The exact format of the file is not particularly important, here I've used a simple json file, but having an index file in any standardised format would enable a client to access any WebDAV hostable filesystem that is synced to YouTarr to efficiently stay updated with the information YouTarr already knows about the video library, without performing exhaustive search over the whole file system each time it polls.
This is the python script I'm using combined with watchexec and it's working well so far for my custom client reader.
import os
import json
import base64
import xml.etree.ElementTree as ET
from pathlib import Path
def image_to_base64(image_path):
"""Converts an image to a base64 string with data URI prefix."""
if not image_path or not os.path.exists(image_path):
return None
try:
with open(image_path, "rb") as img_file:
b64_string = base64.b64encode(img_file.read()).decode('utf-8')
return f"data:image/jpeg;base64,{b64_string}"
except Exception as e:
print(f"Error encoding image {image_path}: {e}")
return None
def nfo_to_dict(nfo_path):
"""Parses Kodi-style .nfo XML into a python dictionary."""
if not os.path.exists(nfo_path):
return {}
try:
tree = ET.parse(nfo_path)
root = tree.getroot()
def etree_to_dict(t):
d = {}
for child in t:
if len(child) == 0:
value = child.text
else:
value = etree_to_dict(child)
if child.tag in d:
if type(d[child.tag]) is list:
d[child.tag].append(value)
else:
d[child.tag] = [d[child.tag], value]
else:
d[child.tag] = value
return d
return etree_to_dict(root)
except Exception as e:
print(f"Error parsing NFO {nfo_path}: {e}")
return {}
def scan_folders(root_path):
root = Path(root_path)
data = {"channels": []}
for channel_dir in [d for d in root.iterdir() if d.is_dir()]:
print(f"Processing Channel: {channel_dir.name}...")
channel_obj = {
"channel_name": channel_dir.name,
"poster_data": image_to_base64(channel_dir / "poster.jpg"),
"videos": []
}
for video_dir in [d for d in channel_dir.iterdir() if d.is_dir()]:
video_file = next(video_dir.glob("*.mp4"), None)
if video_file:
nfo_file = next(video_dir.glob("*.nfo"), None)
thumb_file = next(video_dir.glob("*.jpg"), None)
video_data = {
"folder_name": video_dir.name,
"video_filename": video_file.name,
"thumbnail_data": image_to_base64(thumb_file) if thumb_file else None,
"metadata": nfo_to_dict(nfo_file) if nfo_file else {}
}
channel_obj["videos"].append(video_data)
data["channels"].append(channel_obj)
return data
if __name__ == "__main__":
# This should point to the YouTarr directory (by default assumes the script is being run from that directory)
ROOT_DIR = "."
OUTPUT_FILE = "index.json"
result = scan_folders(ROOT_DIR)
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
json.dump(result, f, indent=2, ensure_ascii=False)
print(f"\nSuccess! Index created at {OUTPUT_FILE}")