diff --git a/packages/addons/addon-depends/steamlink-depends/steamlink-libX11/package.mk b/packages/addons/addon-depends/steamlink-depends/steamlink-libX11/package.mk new file mode 100644 index 00000000000..e779ed63530 --- /dev/null +++ b/packages/addons/addon-depends/steamlink-depends/steamlink-libX11/package.mk @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) + +. $(get_pkg_directory libX11)/package.mk + +PKG_NAME="steamlink-libX11" +PKG_LONGDESC="libX11 for steamlink-rpi" +PKG_URL="" +PKG_DEPENDS_TARGET+=" libX11" +PKG_BUILD_FLAGS+=" -sysroot" + +unpack() { + mkdir -p ${PKG_BUILD} + tar --strip-components=1 -xf ${SOURCES}/${PKG_NAME:10}/${PKG_NAME:10}-${PKG_VERSION}.tar.bz2 -C ${PKG_BUILD} +} diff --git a/packages/addons/addon-depends/steamlink-depends/steamlink-libX11/patches/libX11-disable_nls_tests.patch b/packages/addons/addon-depends/steamlink-depends/steamlink-libX11/patches/libX11-disable_nls_tests.patch new file mode 100644 index 00000000000..3afa79a753f --- /dev/null +++ b/packages/addons/addon-depends/steamlink-depends/steamlink-libX11/patches/libX11-disable_nls_tests.patch @@ -0,0 +1,18 @@ +diff -Naur libX11-1.6.0/nls/Makefile.am libX11-1.6.0.patch/nls/Makefile.am +--- libX11-1.6.0/nls/Makefile.am 2013-06-04 04:21:16.000000000 +0200 ++++ libX11-1.6.0.patch/nls/Makefile.am 2013-06-04 17:07:11.962522739 +0200 +@@ -36,10 +36,10 @@ + < locale.dir.l1 > locale.dir.l2 + cat locale.dir.l2 locale.dir.l1 > locale.dir + +-if HAVE_PERL +-LOG_COMPILER = $(PERL) +-TESTS = compose-check.pl +-endif HAVE_PERL ++# if HAVE_PERL ++# LOG_COMPILER = $(PERL) ++# TESTS = compose-check.pl ++# endif HAVE_PERL + + + # Per-locale data files diff --git a/packages/addons/addon-depends/steamlink-depends/steamlink-libXext/package.mk b/packages/addons/addon-depends/steamlink-depends/steamlink-libXext/package.mk new file mode 100644 index 00000000000..c67f02754a2 --- /dev/null +++ b/packages/addons/addon-depends/steamlink-depends/steamlink-libXext/package.mk @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) + +. $(get_pkg_directory libXext)/package.mk + +PKG_NAME="steamlink-libXext" +PKG_LONGDESC="libXext for steamlink-rpi" +PKG_URL="" +PKG_DEPENDS_TARGET+=" libXext" +PKG_BUILD_FLAGS+=" -sysroot" + +unpack() { + mkdir -p ${PKG_BUILD} + tar --strip-components=1 -xf ${SOURCES}/${PKG_NAME:10}/${PKG_NAME:10}-${PKG_VERSION}.tar.bz2 -C ${PKG_BUILD} +} diff --git a/packages/addons/addon-depends/steamlink-depends/steamlink-libjpeg-turbo/package.mk b/packages/addons/addon-depends/steamlink-depends/steamlink-libjpeg-turbo/package.mk new file mode 100644 index 00000000000..cbb9b8cdec9 --- /dev/null +++ b/packages/addons/addon-depends/steamlink-depends/steamlink-libjpeg-turbo/package.mk @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) + +. $(get_pkg_directory libjpeg-turbo)/package.mk + +PKG_NAME="steamlink-libjpeg-turbo" +PKG_LONGDESC="libjpeg-turbo for steamlink" +PKG_URL="" +PKG_DEPENDS_UNPACK+=" libjpeg-turbo" +PKG_BUILD_FLAGS+=" -sysroot" + +PKG_CMAKE_OPTS_TARGET+=" -DENABLE_STATIC=OFF \ + -DENABLE_SHARED=ON \ + -DWITH_JPEG8=OFF" + +unpack() { + mkdir -p ${PKG_BUILD} + tar --strip-components=1 -xf ${SOURCES}/${PKG_NAME:10}/${PKG_NAME:10}-${PKG_VERSION}.tar.gz -C ${PKG_BUILD} +} diff --git a/packages/addons/addon-depends/steamlink-depends/steamlink-libpng/package.mk b/packages/addons/addon-depends/steamlink-depends/steamlink-libpng/package.mk new file mode 100644 index 00000000000..297e28f4e29 --- /dev/null +++ b/packages/addons/addon-depends/steamlink-depends/steamlink-libpng/package.mk @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) + +. $(get_pkg_directory libpng)/package.mk + +PKG_NAME="steamlink-libpng" +PKG_LONGDESC="libpng for steamlink-rpi" +PKG_URL="" +PKG_DEPENDS_TARGET+=" libpng" +PKG_BUILD_FLAGS+=" -sysroot" + +PKG_CONFIGURE_OPTS_TARGET+=" --disable-static \ + --enable-shared" + +unpack() { + mkdir -p ${PKG_BUILD} + tar --strip-components=1 -xf ${SOURCES}/${PKG_NAME:10}/${PKG_NAME:10}-${PKG_VERSION}.tar.xz -C ${PKG_BUILD} +} + +post_makeinstall_target() { + : +} diff --git a/packages/addons/script/steamlink-rpi/changelog.txt b/packages/addons/script/steamlink-rpi/changelog.txt new file mode 100644 index 00000000000..ed5784c55b0 --- /dev/null +++ b/packages/addons/script/steamlink-rpi/changelog.txt @@ -0,0 +1,2 @@ +100 +- Initial Release diff --git a/packages/addons/script/steamlink-rpi/icon/icon.png b/packages/addons/script/steamlink-rpi/icon/icon.png new file mode 100644 index 00000000000..649937176bb Binary files /dev/null and b/packages/addons/script/steamlink-rpi/icon/icon.png differ diff --git a/packages/addons/script/steamlink-rpi/package.mk b/packages/addons/script/steamlink-rpi/package.mk new file mode 100644 index 00000000000..3463eb5bfd8 --- /dev/null +++ b/packages/addons/script/steamlink-rpi/package.mk @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) + +PKG_NAME="steamlink-rpi" +PKG_VERSION="1.0" +PKG_REV="100" +PKG_ARCH="arm" +PKG_ADDON_PROJECTS="RPi2" +PKG_LICENSE="custom" +PKG_SITE="https://support.steampowered.com/kb_article.php?ref=6153-IFGH-6589" +PKG_DEPENDS_TARGET="toolchain steamlink-libjpeg-turbo steamlink-libpng steamlink-libXext steamlink-libX11" +PKG_SECTION="script" +PKG_SHORTDESC="Steam Link App for Raspberry Pi" +PKG_LONGDESC="Installs the Steam Link App for Raspberry Pi 3 or newer from Valve for use in streaming from Steam clients. Addon is not associated with Valve. Steam and the Steam logo are trademarks and/or registered trademarks of Valve Corporation in the U.S. and/or other countries." +PKG_TOOLCHAIN="manual" + +PKG_IS_ADDON="yes" +PKG_ADDON_NAME="steamlink-rpi" +PKG_ADDON_TYPE="xbmc.python.script" +PKG_ADDON_PROVIDES="executable" + +PKG_STEAMLINK_VERSION="1.1.64.162" +PKG_STEAMLINK_HASH="1ea1c41802bd6cc3efdba7ba258b9d25d5c07cdd1d0ffe9f2d47597588a09155" + +addon() { + # Add needed libraries + mkdir -p ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs + + # libpng + cp -L $(get_install_dir steamlink-libpng)/usr/lib/libpng16.so.16 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/ + + # libjpeg-turbo + cp -L $(get_install_dir steamlink-libjpeg-turbo)/usr/lib/libjpeg.so.62 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/ + + # libXext + cp -L $(get_install_dir steamlink-libXext)/usr/lib/libXext.so.6 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/ + + # libX11 + cp -L $(get_install_dir steamlink-libX11)/usr/lib/libX11.so.6 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/ + cp -L $(get_install_dir steamlink-libX11)/usr/lib/libX11-xcb.so.1 ${ADDON_BUILD}/${PKG_ADDON_ID}/system-libs/ +} + +post_install_addon() { + # Add steamlink version to download to addon + sed -e "s/@STEAMLINK_VERSION@/${PKG_STEAMLINK_VERSION}/" \ + -e "s/@STEAMLINK_HASH@/${PKG_STEAMLINK_HASH}/" \ + -i ${ADDON_BUILD}/${PKG_ADDON_ID}/default.py +} diff --git a/packages/addons/script/steamlink-rpi/source/bin/steamlink-start.sh b/packages/addons/script/steamlink-rpi/source/bin/steamlink-start.sh new file mode 100755 index 00000000000..ffdc72a5e06 --- /dev/null +++ b/packages/addons/script/steamlink-rpi/source/bin/steamlink-start.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) + +. /etc/profile +oe_setup_addon script.steamlink-rpi + +# Steamlink not ready; abort +if [ ! -f ${ADDON_DIR}/prep.ok ]; then + if [ -f /tmp/steamlink.watchdog ]; then + rm /tmp/steamlink.watchdog + fi + exit 0 +fi + +# Adapt to meet steamlink requirements + +# mount an overlay for udev rules and reload udev rules +if [ $(grep -c "/lib/udev/rules.d" /proc/mounts) -eq 0 ]; then + if [ ! -d ${ADDON_DIR}/steamlink/.overlay ]; then + mkdir -p ${ADDON_DIR}/steamlink/.overlay + fi + mount -t overlay overlay -o lowerdir=/lib/udev/rules.d,upperdir=${ADDON_DIR}/steamlink/udev/rules.d/,workdir=${ADDON_DIR}/steamlink/.overlay /lib/udev/rules.d + udevadm trigger +fi + +# use alsa for steamlink; make sure a sound card is present +if [ ! -d /proc/asound ]; then + dtparam audio=on +fi + +# Launch steamlink +# xxx: shutdown kodi or controller input goes to kodi too +systemctl stop kodi +# xxx: shutdown pulseaudio or no sound +systemctl stop pulseaudio +${ADDON_DIR}/steamlink/steamlink.sh +sleep 2 +# Cleanup +umount /lib/udev/rules.d +rm /tmp/steamlink.watchdog +systemctl start pulseaudio +systemctl start kodi + +exit 0 diff --git a/packages/addons/script/steamlink-rpi/source/default.py b/packages/addons/script/steamlink-rpi/source/default.py new file mode 100644 index 00000000000..230be11d60e --- /dev/null +++ b/packages/addons/script/steamlink-rpi/source/default.py @@ -0,0 +1,132 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2019-present Team LibreELEC (https://libreelec.tv) + +import os +import tarfile +import subprocess +import xbmcaddon +import xbmcgui +from hashlib import sha256 +from pathlib import Path +from shutil import rmtree +from sys import exit +from tempfile import TemporaryDirectory +from time import sleep +from urllib.request import urlretrieve + + +STEAMLINK_VERSION = "@STEAMLINK_VERSION@" +STEAMLINK_HASH = "@STEAMLINK_HASH@" +STEAMLINK_TARBALL_NAME = f"steamlink-rpi3-{STEAMLINK_VERSION}.tar.gz" +STEAMLINK_URL = f"http://media.steampowered.com/steamlink/rpi/{STEAMLINK_TARBALL_NAME}" +ADDON_DIR = xbmcaddon.Addon().getAddonInfo("path") +PROGRESS_BAR = xbmcgui.DialogProgress() + + +def GetSHA256Hash(file_name): + """ Get sha256sum of file_name in 8kb chunks """ + with open(file_name,"rb") as file: + SHA256HASH = sha256() + while True: + data_block = file.read(8192) + if not data_block: + break + SHA256HASH.update(data_block) + return SHA256HASH.hexdigest() + +def GetRPiProcessor(): + """ Use vcgencmd to obtain cpu identifier as int """ + VC_CMD_OUTPUT=subprocess.check_output(["vcgencmd", "otp_dump"], encoding="utf-8") + + for line in VC_CMD_OUTPUT.splitlines(): + if line[0:3] == "30:": + PROCESSOR=line.split(":")[1] # entire processor id + return int(PROCESSOR[4:5]) # only cpu id + + return 0 + +def OutputFileContents(file): + """ Read everything in file """ + with open(file) as data: + return data.read() + +def ProgressBarReport(chunk_count, chunk_size, total_size): + """ Use urlretrieve's reporthook to report progress """ + if total_size != -1: + progress_percentage = int(chunk_count * chunk_size / total_size * 100) + PROGRESS_BAR.update(progress_percentage) + else: + PROGRESS_BAR.update(0, "Filesize Unknown") + +def DownloadSteamlink(): + """ Download Steam Link for RPi """ + with TemporaryDirectory() as temp_dir: + STEAMLINK_TEMP_PATH = os.path.join(temp_dir, STEAMLINK_TARBALL_NAME) + + PROGRESS_BAR.create("Steam Link", f"Downloading Steam Link Version: {STEAMLINK_VERSION}...") + urlretrieve(STEAMLINK_URL, STEAMLINK_TEMP_PATH, ProgressBarReport) + + if tarfile.is_tarfile(STEAMLINK_TEMP_PATH): + DOWNLOAD_HASH = GetSHA256Hash(STEAMLINK_TEMP_PATH) + if STEAMLINK_HASH == DOWNLOAD_HASH: + PROGRESS_BAR.update(100, f"Extracting Steam Link Version {STEAMLINK_VERSION}...") + STEAMLINK_TARBALL = tarfile.open(STEAMLINK_TEMP_PATH) + STEAMLINK_TARBALL.extractall(path=f"{ADDON_DIR}/") + PROGRESS_BAR.close() + else: + PROGRESS_BAR.update(0, "Download Error: bad file hash. Try again later.") + sleep(5) + PROGRESS_BAR.close() + exit(1) + else: + PROGRESS_BAR.update(0, "Download Error: bad download or missing file") + sleep(5) + PROGRESS_BAR.close() + exit(1) + +def PrepareSteamlink(): + """ System preparation before launching Steam Link """ + + # Disable Steam Link's cpu check + if not os.path.isfile(f"{ADDON_DIR}/steamlink/.ignore_cpuinfo"): + Path(f"{ADDON_DIR}/steamlink/.ignore_cpuinfo").touch() + + # Add system libraries to bundled + for file in os.listdir(f"{ADDON_DIR}/system-libs/"): + os.symlink(f"{ADDON_DIR}/system-libs/{file}", f"{ADDON_DIR}/steamlink/lib/{file}") + + # systemd setup + if not os.path.isfile(f"{str(Path.home())}/.config/system.d/steamlink-rpi.watchdog.service"): + os.symlink(f"{ADDON_DIR}/system.d/steamlink-rpi.watchdog.service", f"{str(Path.home())}/.config/system.d/steamlink-rpi.watchdog.service") + subprocess.run(["systemctl", "enable", f"{str(Path.home())}/.config/system.d/steamlink-rpi.watchdog.service"]) + + # Finalize + Path(f"{ADDON_DIR}/prep.ok").touch() + +def StartSteamlink(): + # Check if running on RPi3 or higher + if not os.path.isfile(f"{ADDON_DIR}/steamlink/.ignore_cpuinfo") and GetRPiProcessor() < 2: + xbmcgui.Dialog.notification("Steam Link", "Steam Link will not run on this hardware. Aborting...", xbmcgui.NOTIFICATION_INFO, 5000) + exit(1) + + # Check if addon wants to update Steam Link + if os.path.isfile(f"{ADDON_DIR}/steamlink/version.txt"): + STEAMLINK_INSTALLED_VERSION = OutputFileContents(f"{ADDON_DIR}/steamlink/version.txt").rstrip() + + # Update Steamlink handling + if STEAMLINK_VERSION != STEAMLINK_INSTALLED_VERSION: + rmtree(f"{ADDON_DIR}/steamlink/") + os.remove(f"{ADDON_DIR}/prep.ok") + + # Download Steam Link if not present + if not os.path.isfile(f"{ADDON_DIR}/prep.ok"): + DownloadSteamlink() + PrepareSteamlink() + + # Start Steamlink + xbmcgui.Dialog().notification("Steam Link", "Starting Steam Link", xbmcgui.NOTIFICATION_INFO, 3000) + Path("/tmp/steamlink.watchdog").touch() + subprocess.run(["systemctl", "start", "steamlink-rpi.watchdog.service"]) + + +StartSteamlink() diff --git a/packages/addons/script/steamlink-rpi/source/system.d/steamlink-rpi.watchdog.service b/packages/addons/script/steamlink-rpi/source/system.d/steamlink-rpi.watchdog.service new file mode 100644 index 00000000000..3a52b1b4156 --- /dev/null +++ b/packages/addons/script/steamlink-rpi/source/system.d/steamlink-rpi.watchdog.service @@ -0,0 +1,11 @@ +[Unit] +Description="Watchdog to start Steamlink" +After=graphical.target +ConditionPathExists=/tmp/steamlink.watchdog + +[Service] +Type=oneshot +ExecStart=/storage/.kodi/addons/script.steamlink-rpi/bin/steamlink-start.sh + +[Install] +WantedBy=graphical.target