Compare commits

..

6 Commits

Author SHA1 Message Date
Maksim Ivanov
1cfefbdb35 Merge pull request #45 from alpham8/feature/opensuse-support
feature: openSUSE distribution support expect package manager events
2026-05-30 21:27:20 +03:00
Thomas Wunner
0ca3c8c537 match underlying Chromium version to the compatible ffmpeg and widevine version, repeat this procedure for all installed variants of Opera browser by using temp cache for downloaded assets 2026-05-16 16:22:11 +02:00
Thomas Wunner
3eed381155 Merge branch 'main' into feature/opensuse-support
# Conflicts:
#	install.sh
#	scripts/99fix-opera
#	scripts/fix-opera.sh
2026-05-16 14:49:54 +02:00
Thomas Wunner
86fab5688a openSUSE fixes to install scripts 2026-05-16 14:39:36 +02:00
Maksim Ivanov
a46ad6f23a basic support for opera-gx (no autoupdate) 2026-03-20 17:21:53 +03:00
Thomas Wunner
b1726cb9be minor code style and runtime fixes to be more standard-compliant 2025-10-12 08:01:04 +02:00
4 changed files with 182 additions and 36 deletions

29
CHANGELOG.md Normal file
View File

@@ -0,0 +1,29 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- version-aware ffmpeg selection: detect Opera's bundled Chromium version and download the matching nwjs-ffmpeg release instead of always using the latest
- per-variant Chromium detection via binary inspection (`get_chromium_major`)
- ffmpeg download caching: skip re-download when multiple Opera variants share the same Chromium version
### Changed
- universal shebang bash interpreter to safely find bash on `$PATH`
- Widevine CDM is now downloaded once before the main loop instead of redundantly per variant
- release data is fetched once upfront (`?per_page=50`) and reused for version matching
### Removed
### Fixed
- in the preflight check: Prefer our user-friendly error messages while avoiding double error message from the operating system by redirecting also `stderr` to `/dev/null`
- potential runtime error: prefer `command -v` over `which`-command, which can have different behaviour on different Linux distributions and always use the first result in `PATH` independently of multiple defined commands
- `grep` error when `CDM_FLAG` starts with `--` by adding explicit end-of-options separator (`grep -qF -- "$CDM_FLAG"`)

17
install.sh Normal file → Executable file
View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -euo pipefail
@@ -84,6 +84,21 @@ if [[ $(uname -m) != "x86_64" ]]; then
exit 1
fi
if ! which unzip > /dev/null; then
printf '\033[1munzip\033[0m package must be installed to run this script\n'
exit 1
fi
if ! which curl > /dev/null; then
printf '\033[1mcurl\033[0m package must be installed to run this script\n'
exit 1
fi
if ! which jq > /dev/null; then
printf '\033[1mjq\033[0m package must be installed to run this script\n'
exit 1
fi
readonly SCRIPT_PATH=$(dirname $(readlink -f $0))
readonly INSTALL_PATH="/root/.scripts"
readonly USER_NAME="${SUDO_USER:-$(logname)}"

View File

@@ -1,2 +1,2 @@
DPkg::Pre-Invoke {"stat -c %Z $(readlink -f $(which opera)) > /tmp/opera.timestamp 2> /dev/null || echo 0 > /tmp/opera.timestamp";};
DPkg::Post-Invoke {"set NEW_OPERA=`stat -c %Z $(readlink -f $(which opera))` || exit 0; set OLD_OPERA=`cat /tmp/opera.timestamp` || exit 0; if [ "$NEW_OPERA" != "$OLD_OPERA" ]; then /root/.scripts/fix-opera.sh; fi; rm -f /tmp/opera.timestamp";};
DPkg::Pre-Invoke {"stat -c %Z $(readlink -f $(command -v opera)) > /tmp/opera.timestamp 2> /dev/null || echo 0 > /tmp/opera.timestamp";};
DPkg::Post-Invoke {"set NEW_OPERA=`stat -c %Z $(readlink -f $(command -v opera))` || exit 0; set OLD_OPERA=`cat /tmp/opera.timestamp` || exit 0; if [ "$NEW_OPERA" != "$OLD_OPERA" ]; then /root/.scripts/fix-opera.sh; fi; rm -f /tmp/opera.timestamp";};

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
if [[ $(whoami) != "root" ]]; then
printf 'Try to run it with sudo\n'
@@ -10,17 +10,17 @@ if [[ $(uname -m) != "x86_64" ]]; then
exit 1
fi
if ! command -v unzip > /dev/null; then
if ! command -v unzip 2>&1 /dev/null; then
printf '\033[1munzip\033[0m package must be installed to run this script\n'
exit 1
fi
if ! command -v curl > /dev/null; then
if ! command -v curl 2>&1 /dev/null; then
printf '\033[1mcurl\033[0m package must be installed to run this script\n'
exit 1
fi
if ! command -v jq > /dev/null; then
if ! command -v jq 2>&1 /dev/null; then
printf '\033[1mjq\033[0m package must be installed to run this script\n'
exit 1
fi
@@ -49,60 +49,145 @@ if [ -x "$(command -v opera-beta)" ]; then
OPERA_VERSIONS+=("opera-beta")
fi
if [ -x "$(command -v opera-gx)" ]; then
OPERA_VERSIONS+=("opera-gx")
fi
get_chromium_major() {
local opera_dir="$1"
local opera_name="$2"
local version=""
for bin in "$opera_dir/$opera_name" "$opera_dir/opera"; do
if [[ -f "$bin" ]]; then
version=$(grep -ao 'Chrome/[0-9]\+' "$bin" 2>/dev/null | head -1 | grep -o '[0-9]\+')
if [[ -n "$version" ]]; then
echo "$version"
return 0
fi
fi
done
return 1
}
get_ffmpeg_url_for_chromium() {
local chromium_major="$1"
local best_tag=""
local best_url=""
for releases_json in "$RELEASES_MAIN" "$RELEASES_ALT"; do
local match
match=$(echo "$releases_json" | jq -r --arg ver "$chromium_major" '
[.[] | select((.body // "") | test("Chromium " + $ver + "\\."))]
| sort_by(.published_at)
| if length > 0 then .[-1] | "\(.tag_name)\t\(.assets[0].browser_download_url)" else empty end
')
if [[ -n "$match" ]]; then
local tag url
tag=$(echo "$match" | cut -f1)
url=$(echo "$match" | cut -f2)
if [[ -z "$best_tag" || "$tag" > "$best_tag" ]]; then
best_tag="$tag"
best_url="$url"
fi
fi
done
if [[ -n "$best_url" ]]; then
printf '%s\t%s' "$best_tag" "$best_url"
return 0
fi
return 1
}
get_latest_ffmpeg_url() {
local url_main url_alt
url_main=$(echo "$RELEASES_MAIN" | jq -rS 'sort_by(.published_at) | .[-1].assets[0].browser_download_url')
url_alt=$(echo "$RELEASES_ALT" | jq -rS 'sort_by(.published_at) | .[-1].assets[0].browser_download_url')
if [[ $(basename "$url_alt") < $(basename "$url_main") ]]; then
echo "$url_main"
else
echo "$url_alt"
fi
}
#Getting download links
printf 'Getting download links...\n'
##ffmpeg
readonly FFMPEG_URL_MAIN=$(curl -sL4 $FFMPEG_SRC_MAIN | jq -rS 'sort_by(.published_at) | .[-1].assets[0].browser_download_url')
readonly FFMPEG_URL_ALT=$(curl -sL4 $FFMPEG_SRC_ALT | jq -rS 'sort_by(.published_at) | .[-1].assets[0].browser_download_url')
[[ $(basename $FFMPEG_URL_ALT) < $(basename $FFMPEG_URL_MAIN) ]] && readonly FFMPEG_URL=$FFMPEG_URL_MAIN || readonly FFMPEG_URL=$FFMPEG_URL_ALT
if [[ -z $FFMPEG_URL ]]; then
printf 'Failed to get ffmpeg download URL. Exiting...\n'
exit 1
fi
##Fetch all release data once
RELEASES_MAIN=$(curl -sL4 "$FFMPEG_SRC_MAIN?per_page=50")
RELEASES_ALT=$(curl -sL4 "$FFMPEG_SRC_ALT?per_page=50")
##Widevine
if $FIX_WIDEVINE; then
readonly WIDEVINE_URL=$(curl -sL4 $WIDEVINE_SRC | jq -r '.vendors."gmp-widevinecdm".platforms."Linux_x86_64-gcc3".mirrorUrls[0]')
fi
#Downloading files
printf 'Downloading files...\n'
#Downloading Widevine
mkdir -p "$FIX_DIR"
##ffmpeg
curl -L4 --progress-bar $FFMPEG_URL -o "$FIX_DIR/ffmpeg.zip"
if [ $? -ne 0 ]; then
printf 'Failed to download ffmpeg. Check your internet connection or try later\n'
exit 1
fi
##Widevine
if $FIX_WIDEVINE; then
if $FIX_WIDEVINE; then
printf 'Downloading Widevine CDM...\n'
echo -e "From URL: $WIDEVINE_URL\n"
curl -L4 --progress-bar "$WIDEVINE_URL" -o "$FIX_DIR/widevine.zip"
if [ $? -ne 0 ]; then
printf 'Failed to download Widevine CDM. Check your internet connection or try later\n'
exit 1
fi
echo "Extracting WidevineCDM..."
unzip -oj "$FIX_DIR/widevine.zip" -d "$FIX_DIR" > /dev/null 2>/dev/null
fi
#Extracting files
##ffmpeg
echo "Extracting ffmpeg..."
unzip -o "$FIX_DIR/ffmpeg.zip" -d $FIX_DIR > /dev/null
##Widevine
if $FIX_WIDEVINE; then
echo "Extracting WidevineCDM..."
unzip -oj "$FIX_DIR/widevine.zip" -d $FIX_DIR > /dev/null 2>/dev/null
fi
LAST_FFMPEG_TAG=""
for opera in ${OPERA_VERSIONS[@]}; do
echo "Doing $opera"
EXECUTABLE=$(command -v "$opera")
if [[ "$ARCH_SYSTEM" == true ]]; then
OPERA_DIR=$(dirname $(cat $EXECUTABLE | grep exec | cut -d ' ' -f 2))
elif head -1 "$EXECUTABLE" | grep -q 'bash\|sh'; then
# The executable is a shell wrapper (e.g. openSUSE packages Opera as a wrapper
# script). readlink -f on a script returns the script itself, not the real binary,
# so we parse the LIBDIR variable directly from the wrapper instead.
PARSED_LIBDIR=$(grep '^LIBDIR=' "$EXECUTABLE" | head -1 | sed 's/LIBDIR=//;s/"//g' | sed "s/\\\$PROGNAME/$opera/g")
if [[ -n "$PARSED_LIBDIR" && -d "$PARSED_LIBDIR" ]]; then
OPERA_DIR="$PARSED_LIBDIR"
else
OPERA_DIR=$(dirname $(readlink -f $EXECUTABLE))
fi
else
OPERA_DIR=$(dirname $(readlink -f $EXECUTABLE))
fi
CHROMIUM_MAJOR=$(get_chromium_major "$OPERA_DIR" "$opera")
if [[ -n "$CHROMIUM_MAJOR" ]]; then
printf 'Detected Chromium %s for %s\n' "$CHROMIUM_MAJOR" "$opera"
FFMPEG_MATCH=$(get_ffmpeg_url_for_chromium "$CHROMIUM_MAJOR")
if [[ -n "$FFMPEG_MATCH" ]]; then
FFMPEG_TAG=$(echo "$FFMPEG_MATCH" | cut -f1)
FFMPEG_URL=$(echo "$FFMPEG_MATCH" | cut -f2)
else
printf 'WARNING: No ffmpeg release found for Chromium %s, using latest\n' "$CHROMIUM_MAJOR"
FFMPEG_TAG="latest"
FFMPEG_URL=$(get_latest_ffmpeg_url)
fi
else
printf 'WARNING: Could not detect Chromium version for %s, using latest ffmpeg\n' "$opera"
FFMPEG_TAG="latest"
FFMPEG_URL=$(get_latest_ffmpeg_url)
fi
if [[ "$FFMPEG_TAG" != "$LAST_FFMPEG_TAG" ]]; then
printf 'Downloading ffmpeg (%s)...\n' "$FFMPEG_TAG"
echo -e "From: $FFMPEG_URL\n"
curl -L4 --progress-bar "$FFMPEG_URL" -o "$FIX_DIR/ffmpeg.zip"
if [ $? -ne 0 ]; then
printf 'Failed to download ffmpeg. Check your internet connection or try later\n'
exit 1
fi
echo "Extracting ffmpeg..."
unzip -o "$FIX_DIR/ffmpeg.zip" -d "$FIX_DIR" > /dev/null
LAST_FFMPEG_TAG="$FFMPEG_TAG"
else
printf 'Using cached ffmpeg (%s)\n' "$FFMPEG_TAG"
fi
OPERA_LIB_DIR="$OPERA_DIR/lib_extra"
OPERA_WIDEVINE_DIR="$OPERA_LIB_DIR/WidevineCdm"
OPERA_WIDEVINE_SO_DIR="$OPERA_WIDEVINE_DIR/_platform_specific/linux_x64"
@@ -131,6 +216,23 @@ for opera in ${OPERA_VERSIONS[@]}; do
cp -f "$FIX_DIR/$WIDEVINE_MANIFEST_NAME" "$OPERA_WIDEVINE_DIR"
chmod 0644 "$OPERA_WIDEVINE_DIR/$WIDEVINE_MANIFEST_NAME"
printf "[\n {\n \"preload\": \"$OPERA_WIDEVINE_DIR\"\n }\n]\n" > "$OPERA_WIDEVINE_CONFIG"
# Newer Chromium-based Opera versions (110+) no longer read widevine_config.json.
# On zypper-based systems (openSUSE) Opera reads OPERA_FLAGS from /etc/default/opera,
# so we inject --widevine-cdm-path there as well to cover both loading mechanisms.
if command -v zypper &>/dev/null && [[ -f /etc/default/$opera ]]; then
OPERA_DEFAULT="/etc/default/$opera"
CDM_FLAG="--widevine-cdm-path=$OPERA_WIDEVINE_DIR"
if grep -q 'OPERA_FLAGS=' "$OPERA_DEFAULT"; then
# Append the flag if not already present
if ! grep -qF -- "$CDM_FLAG" "$OPERA_DEFAULT"; then
sed -i "s|OPERA_FLAGS=\"\(.*\)\"|OPERA_FLAGS=\"\1 $CDM_FLAG\"|" "$OPERA_DEFAULT"
fi
else
echo "OPERA_FLAGS=\"$CDM_FLAG\"" >> "$OPERA_DEFAULT"
fi
printf "Widevine CDM path added to %s\n" "$OPERA_DEFAULT"
fi
fi
done