Add local Kodi repo tooling and repository addon
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -9,5 +9,11 @@
|
|||||||
|
|
||||||
# Local tests (not committed)
|
# Local tests (not committed)
|
||||||
/tests/
|
/tests/
|
||||||
|
/TESTING/
|
||||||
/.pytest_cache/
|
/.pytest_cache/
|
||||||
/pytest.ini
|
/pytest.ini
|
||||||
|
|
||||||
|
# Python artifacts
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
.coverage
|
||||||
|
|||||||
@@ -15,6 +15,12 @@ ViewIT ist ein Kodi‑Addon zum Durchsuchen und Abspielen von Inhalten der unter
|
|||||||
- Kodi‑ZIP bauen: `./scripts/build_kodi_zip.sh` → `dist/<addon_id>-<version>.zip`
|
- Kodi‑ZIP bauen: `./scripts/build_kodi_zip.sh` → `dist/<addon_id>-<version>.zip`
|
||||||
- Addon‑Version in `addon/addon.xml`
|
- Addon‑Version in `addon/addon.xml`
|
||||||
|
|
||||||
|
## Lokales Kodi-Repository
|
||||||
|
- Repository bauen (inkl. ZIPs + `addons.xml` + `addons.xml.md5`): `./scripts/build_local_kodi_repo.sh`
|
||||||
|
- Lokal bereitstellen: `./scripts/serve_local_kodi_repo.sh`
|
||||||
|
- Standard-URL: `http://127.0.0.1:8080/repo/addons.xml`
|
||||||
|
- Optional eigene URL beim Build setzen: `REPO_BASE_URL=http://<host>:<port>/repo ./scripts/build_local_kodi_repo.sh`
|
||||||
|
|
||||||
## Entwicklung (kurz)
|
## Entwicklung (kurz)
|
||||||
- Hauptlogik: `addon/default.py`
|
- Hauptlogik: `addon/default.py`
|
||||||
- Plugins: `addon/plugins/*_plugin.py`
|
- Plugins: `addon/plugins/*_plugin.py`
|
||||||
|
|||||||
17
repository.viewit/addon.xml
Normal file
17
repository.viewit/addon.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
|
<addon id="repository.viewit" name="ViewIT Repository" version="1.0.0" provider-name="ViewIT">
|
||||||
|
<extension point="xbmc.addon.repository" name="ViewIT Repository">
|
||||||
|
<dir>
|
||||||
|
<info compressed="false">http://127.0.0.1:8080/repo/addons.xml</info>
|
||||||
|
<checksum>http://127.0.0.1:8080/repo/addons.xml.md5</checksum>
|
||||||
|
<datadir zip="true">http://127.0.0.1:8080/repo/</datadir>
|
||||||
|
</dir>
|
||||||
|
</extension>
|
||||||
|
<extension point="xbmc.addon.metadata">
|
||||||
|
<summary lang="de_DE">Lokales Repository fuer ViewIT Updates</summary>
|
||||||
|
<summary lang="en_GB">Local repository for ViewIT updates</summary>
|
||||||
|
<description lang="de_DE">Stellt das ViewIT Addon ueber ein Kodi Repository bereit.</description>
|
||||||
|
<description lang="en_GB">Provides the ViewIT addon via a Kodi repository.</description>
|
||||||
|
<platform>all</platform>
|
||||||
|
</extension>
|
||||||
|
</addon>
|
||||||
110
scripts/build_local_kodi_repo.sh
Executable file
110
scripts/build_local_kodi_repo.sh
Executable file
@@ -0,0 +1,110 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
DIST_DIR="${ROOT_DIR}/dist"
|
||||||
|
REPO_DIR="${DIST_DIR}/repo"
|
||||||
|
PLUGIN_ADDON_XML="${ROOT_DIR}/addon/addon.xml"
|
||||||
|
REPO_SRC_DIR="${ROOT_DIR}/repository.viewit"
|
||||||
|
REPO_ADDON_XML="${REPO_SRC_DIR}/addon.xml"
|
||||||
|
REPO_BASE_URL="${REPO_BASE_URL:-http://127.0.0.1:8080/repo}"
|
||||||
|
|
||||||
|
if [[ ! -f "${PLUGIN_ADDON_XML}" ]]; then
|
||||||
|
echo "Missing: ${PLUGIN_ADDON_XML}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "${REPO_ADDON_XML}" ]]; then
|
||||||
|
echo "Missing: ${REPO_ADDON_XML}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "${REPO_DIR}"
|
||||||
|
|
||||||
|
PLUGIN_ZIP="$("${ROOT_DIR}/scripts/build_kodi_zip.sh")"
|
||||||
|
cp -f "${PLUGIN_ZIP}" "${REPO_DIR}/"
|
||||||
|
|
||||||
|
read -r REPO_ADDON_ID REPO_ADDON_VERSION < <(python3 - "${REPO_ADDON_XML}" <<'PY'
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
root = ET.parse(sys.argv[1]).getroot()
|
||||||
|
print(root.attrib.get("id", "repository.viewit"), root.attrib.get("version", "0.0.0"))
|
||||||
|
PY
|
||||||
|
)
|
||||||
|
|
||||||
|
TMP_DIR="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "${TMP_DIR}"' EXIT
|
||||||
|
TMP_REPO_ADDON_DIR="${TMP_DIR}/${REPO_ADDON_ID}"
|
||||||
|
mkdir -p "${TMP_REPO_ADDON_DIR}"
|
||||||
|
|
||||||
|
if command -v rsync >/dev/null 2>&1; then
|
||||||
|
rsync -a --delete "${REPO_SRC_DIR}/" "${TMP_REPO_ADDON_DIR}/"
|
||||||
|
else
|
||||||
|
cp -a "${REPO_SRC_DIR}/." "${TMP_REPO_ADDON_DIR}/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
python3 - "${TMP_REPO_ADDON_DIR}/addon.xml" "${REPO_BASE_URL}" <<'PY'
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
addon_xml = sys.argv[1]
|
||||||
|
base_url = sys.argv[2].rstrip("/")
|
||||||
|
|
||||||
|
tree = ET.parse(addon_xml)
|
||||||
|
root = tree.getroot()
|
||||||
|
dir_node = root.find(".//dir")
|
||||||
|
if dir_node is None:
|
||||||
|
raise SystemExit("Invalid repository addon.xml: missing <dir>")
|
||||||
|
|
||||||
|
info = dir_node.find("info")
|
||||||
|
checksum = dir_node.find("checksum")
|
||||||
|
datadir = dir_node.find("datadir")
|
||||||
|
if info is None or checksum is None or datadir is None:
|
||||||
|
raise SystemExit("Invalid repository addon.xml: missing info/checksum/datadir")
|
||||||
|
|
||||||
|
info.text = f"{base_url}/addons.xml"
|
||||||
|
checksum.text = f"{base_url}/addons.xml.md5"
|
||||||
|
datadir.text = f"{base_url}/"
|
||||||
|
|
||||||
|
tree.write(addon_xml, encoding="utf-8", xml_declaration=True)
|
||||||
|
PY
|
||||||
|
|
||||||
|
REPO_ZIP_NAME="${REPO_ADDON_ID}-${REPO_ADDON_VERSION}.zip"
|
||||||
|
REPO_ZIP_PATH="${REPO_DIR}/${REPO_ZIP_NAME}"
|
||||||
|
rm -f "${REPO_ZIP_PATH}"
|
||||||
|
(cd "${TMP_DIR}" && zip -r "${REPO_ZIP_PATH}" "${REPO_ADDON_ID}" >/dev/null)
|
||||||
|
|
||||||
|
python3 - "${PLUGIN_ADDON_XML}" "${TMP_REPO_ADDON_DIR}/addon.xml" "${REPO_DIR}/addons.xml" <<'PY'
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
plugin_xml = Path(sys.argv[1])
|
||||||
|
repo_xml = Path(sys.argv[2])
|
||||||
|
target = Path(sys.argv[3])
|
||||||
|
|
||||||
|
addons = ET.Element("addons")
|
||||||
|
for source in (plugin_xml, repo_xml):
|
||||||
|
root = ET.parse(source).getroot()
|
||||||
|
addons.append(root)
|
||||||
|
|
||||||
|
target.write_text('<?xml version="1.0" encoding="UTF-8"?>\n' + ET.tostring(addons, encoding="unicode"), encoding="utf-8")
|
||||||
|
PY
|
||||||
|
|
||||||
|
python3 - "${REPO_DIR}/addons.xml" "${REPO_DIR}/addons.xml.md5" <<'PY'
|
||||||
|
import hashlib
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
addons_xml = Path(sys.argv[1])
|
||||||
|
md5_file = Path(sys.argv[2])
|
||||||
|
md5 = hashlib.md5(addons_xml.read_bytes()).hexdigest()
|
||||||
|
md5_file.write_text(md5, encoding="ascii")
|
||||||
|
PY
|
||||||
|
|
||||||
|
echo "Repo built:"
|
||||||
|
echo " ${REPO_DIR}/addons.xml"
|
||||||
|
echo " ${REPO_DIR}/addons.xml.md5"
|
||||||
|
echo " ${REPO_ZIP_PATH}"
|
||||||
|
echo " ${REPO_DIR}/$(basename "${PLUGIN_ZIP}")"
|
||||||
17
scripts/serve_local_kodi_repo.sh
Executable file
17
scripts/serve_local_kodi_repo.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
DIST_DIR="${ROOT_DIR}/dist"
|
||||||
|
HOST="${HOST:-127.0.0.1}"
|
||||||
|
PORT="${PORT:-8080}"
|
||||||
|
|
||||||
|
if [[ ! -f "${DIST_DIR}/repo/addons.xml" ]]; then
|
||||||
|
echo "Missing ${DIST_DIR}/repo/addons.xml" >&2
|
||||||
|
echo "Run ./scripts/build_local_kodi_repo.sh first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Serving local Kodi repo from ${DIST_DIR}"
|
||||||
|
echo "Repository URL: http://${HOST}:${PORT}/repo/addons.xml"
|
||||||
|
(cd "${DIST_DIR}" && python3 -m http.server "${PORT}" --bind "${HOST}")
|
||||||
Reference in New Issue
Block a user