Files
mymovies/build_catalog.sh

152 lines
5.7 KiB
Bash

#!/usr/bin/env bash
# Сборка catalog.json из папок фильмов.
# Запускать НА spb-easy-dale (где смонтирован volume):
# bash /data/coolify/applications/e9ghwyp5n5kyfshk55krbhki/build_catalog.sh
#
# Структура (ты заливаешь по SSH в MOVIES_DIR):
# <id>/
# poster.jpg (обложка, имя неважно, берётся первая .jpg/.png)
# *.torrent (торрент, берётся первый .torrent)
# meta.txt (поля через "key: value", см. ниже)
#
# meta.txt поддерживает: title, original_title, year, genre(через запятую),
# rating, description. Если meta.txt нет — берётся <id> как title.
set -euo pipefail
MOVIES_DIR="${MOVIES_DIR:-/data/coolify/applications/e9ghwyp5n5kyfshk55krbhki/movies}"
OUT="$MOVIES_DIR/catalog.json"
[[ -d "$MOVIES_DIR" ]] || { echo "Нет папки $MOVIES_DIR"; exit 1; }
python3 - "$MOVIES_DIR" "$OUT" <<'PY'
import sys, os, json, datetime
movies_dir, out = sys.argv[1], sys.argv[2]
movies = []
def decode_bencode(b, pos):
"""Minimal bencode decoder. Returns (value, new_pos)."""
c = b[pos:pos+1]
if c == b'd':
pos += 1; dd = {}
while b[pos:pos+1] != b'e':
kk, pos = decode_bencode(b, pos)
vv, pos = decode_bencode(b, pos)
dd[kk] = vv
return dd, pos+1
if c == b'l':
pos += 1; ll = []
while b[pos:pos+1] != b'e':
vv, pos = decode_bencode(b, pos)
ll.append(vv)
return ll, pos+1
if c == b'i':
e = b.index(b'e', pos)
return int(b[pos+1:e]), e+1
# string: <len>:<bytes> -- read the length digits first, then ':'
co = pos
while b[co:co+1] != b':':
co += 1
n = int(b[pos:co])
s = b[co+1:co+1+n]
return s, co+1+n
def encode_bencode(obj):
"""Re-encode a decoded bencode value back to bytes (canonical)."""
if isinstance(obj, int):
return b'i' + str(obj).encode('ascii') + b'e'
if isinstance(obj, bytes):
return str(len(obj)).encode('ascii') + b':' + obj
if isinstance(obj, str):
b = obj.encode('utf-8')
return str(len(b)).encode('ascii') + b':' + b
if isinstance(obj, list):
return b'l' + b''.join(encode_bencode(x) for x in obj) + b'e'
if isinstance(obj, dict):
keys = sorted(obj.keys())
return b'd' + b''.join(encode_bencode(k) + encode_bencode(obj[k]) for k in keys) + b'e'
raise TypeError(obj)
for entry in sorted(os.listdir(movies_dir)):
d = os.path.join(movies_dir, entry)
if not os.path.isdir(d) or entry.startswith('.'):
continue
meta = {'id': entry}
# читаем meta.txt
metafile = os.path.join(d, 'meta.txt')
if os.path.exists(metafile):
for line in open(metafile, encoding='utf-8', errors='replace'):
line = line.strip()
if not line or ':' not in line:
continue
k, v = line.split(':', 1)
k, v = k.strip().lower(), v.strip()
if k in ('genre',):
meta['genre'] = [x.strip() for x in v.split(',') if x.strip()]
elif k in ('year',):
meta['year'] = int(v)
elif k in ('rating',):
meta['rating'] = float(v)
elif k in ('title', 'original_title', 'description'):
meta[k] = v
# обложка: poster.* для карточки, movie.* для модалки торрента
imgs = [f for f in os.listdir(d) if f.lower().endswith(('.jpg', '.jpeg', '.png', '.webp'))]
def first(prefix):
for f in imgs:
if f.lower().startswith(prefix):
return f
return None
# poster: ТОЛЬКО poster.* (вертикальный). Без фолбэка на movie.jpg!
poster = first('poster.')
# movie: ТОЛЬКО movie.* (горизонтальный, = обложка/фон)
movie = first('movie.')
if poster:
meta['poster'] = f"movies/{entry}/{poster}"
if movie:
meta['img'] = f"movies/{entry}/{movie}"
# cover (обложка) = movie.jpg (горизонтальный), иначе poster
cover = movie or poster
if cover:
meta['cover'] = f"movies/{entry}/{cover}"
# img fallback: если нет movie.jpg, но есть poster — используем poster
if not movie and poster:
meta['img'] = f"movies/{entry}/{poster}"
# торрент
torrent = None
for f in os.listdir(d):
if f.lower().endswith('.torrent'):
torrent = f; break
if torrent:
meta['torrent'] = f"movies/{entry}/{torrent}"
# extract info-hash -> magnet (native Lampa torrent playback needs torrent_hash)
try:
import hashlib
tdata = open(os.path.join(d, torrent), 'rb').read()
parsed, _ = decode_bencode(tdata, 0) # parse whole file from the start
info = parsed[b'info']
# re-encode the info dict to get the exact bencoded bytes for hashing
encoded = encode_bencode(info)
h = hashlib.sha1(encoded).hexdigest()
meta['hash'] = h
meta['magnet'] = f"magnet:?xt=urn:btih:{h}"
except Exception as e:
print(' ! hash error for', entry, e)
# fallback title
if 'title' not in meta:
meta['title'] = entry
movies.append(meta)
catalog = {
'name': 'Мой видеокаталог',
'updated': datetime.date.today().isoformat(),
'movies': movies,
}
with open(out, 'w', encoding='utf-8') as f:
json.dump(catalog, f, ensure_ascii=False, indent=2)
print(f'Готово: {len(movies)} фильм(ов) -> {out}')
for m in movies:
print(' -', m.get('title'), m.get('year', ''))
PY