webos-debloat: reversible debloat for rooted LG webOS TVs (hide bloat tiles, stop preloading, turn off promos/ACR)
webos-debloat
One shell script that makes a rooted LG webOS TV lighter and quieter:
- hides bloat tiles and background apps from the launcher
- stops preloading apps the TV keeps in RAM whether or not you open them
- turns off home-screen promotions, promo videos, sports alerts, content recommendations and Live Plus (ACR)
Nothing is deleted and the root filesystem is never touched. It only edits LG's own app block list, the preload manager's policy and a few system settings. So every change can be undone, and firmware updates install as they would on a stock TV.
Status: tested on one TV, not everywhere. It was written on a 2026 LG OLED running webOS 26 (firmware 11.2, US region). On other models and versions it should work in principle: app IDs that aren't installed and settings keys that don't exist are skipped. But it hasn't been tried there. Start with--planand--dry-run.
Requirements
- Root on the TV via Homebrew Channel. Which rooting method works depends on your firmware; see webosbrew.org.
- A root shell on the TV, either SSH (recommended) or Homebrew Channel's telnet.
- For
--install-boot-hook, Homebrew Channel's startup scripts must be enabled. They run/var/lib/webosbrew/init.d/*at boot.
Get the script onto the TV
With SSH enabled in Homebrew Channel:
ssh root@ 'cat > /tmp/webos-debloat.sh' < webos-debloat.sh
With telnet only, serve this folder from your computer and fetch the script from the TV:
python3 -m http.server 8000 # on your computer
curl -o /tmp/webos-debloat.sh http://:8000/webos-debloat.sh # on the TV
/tmp is wiped on reboot. That's fine: --install-boot-hook copies the script to /var/lib/webos-debloat/.
Quick start
On the TV:
sh /tmp/webos-debloat.sh --list # what's installed
sh /tmp/webos-debloat.sh --plan # what --apply would hide
sh /tmp/webos-debloat.sh --apply # hide the default categories
sh /tmp/webos-debloat.sh --preload-off # the step that actually frees RAM
sh /tmp/webos-debloat.sh --settings-off # promos, alerts, recommendations, ACR
sh /tmp/webos-debloat.sh --install-boot-hook # keep it applied across reboots
sh /tmp/webos-debloat.sh --status
To see the effect, run grep MemAvailable /proc/meminfo before and after.
Categories
--apply hides the default categories. Pick others with --only a,b,c, or hide single apps with --id .
| category | what | default |
|---|---|---|
ad | ACR, ad overlays, "food delivery" overlays, video ads | yes |
sdk | example apps that ship in the firmware | yes |
demo | store demo, factory and diagnostics apps | yes |
remote | LG remote-support service | yes |
promo | LG Channels, Sports, Music, Photo & Video, user guide, weather | no |
visible | launcher tiles: LG Channels, Home Hub, Premium, Multi View settings, … | no |
extras | AI/voice assistant, membership, family care, customer support, Remote PC, … | no |
all | all of the above | no |
extras hides the voice assistant and LG account pages, so skip it if you use them. Some apps are never hidden, whatever you ask for (PROTECTED_IDS in the script): Home, Settings, Live TV, inputs, the browser, first-run setup, Homebrew Channel and anything else whose absence is known to break the TV's own UI.
Undo
S=/var/lib/webos-debloat/webos-debloat.sh # or wherever your copy is
sh $S --remove-boot-hook
sh $S --restore # un-hide what this script hid (LG's own blocks stay)
sh $S --preload-on
sh $S --settings-restore # restores the values from before --settings-off
The first run backs up the files it edits to /var/lib/webos-debloat/*.orig and never overwrites those backups.
Things learned along the way
- Hiding is mostly cosmetic. It removes tiles from the app list but doesn't stop processes or free memory. On webOS 26 it also doesn't clean up the content rows inside LG Home.
- The memory goes to preloading. The preload manager keeps some apps resident before anyone opens them. List them with
luna-send -n 1 luna://com.webos.service.preloadmanager/getPreloadPolicy '{}'.setPreloadPolicyonly lasts until the preload manager reloads, which on webOS 26 also happens a few minutes after boot.- The
allowedAppsmap in/var/preferences/webos-preload-manager-conf.jsonis what persists. - The script sets both, and the boot hook replays them.
- It leaves Home, Live TV and the HDMI inputs preloaded. That is what keeps the Home button and input switching fast.
- "Off" is usually suspend. With the default power settings, turning the TV off suspends it. Boot hooks run only after a real reboot, not on power-on. Everything this script changes is stored in
/var, so it doesn't matter here. If you write your own hooks, keep it in mind: on resume webOS launches LG Home with"reason":"boot", which you can watch for withluna-send -i luna://com.webos.service.applicationmanager/getAppLifeEvents '{"subscribe":true}'. - Settings keys live in
/etc/palm/description.categorykeysmap.json(which category each key belongs to) and/etc/palm/defaultSettings.json. That's the place to look for more things to switch off.
Security note
Homebrew Channel's telnet is an unauthenticated root shell open to your whole network. Once you're set up, enable SSH with a key in Homebrew Channel's settings and turn telnet off.
License
MIT. See LICENSE, or the SPDX header in webos-debloat.sh if you only grabbed the script. Not affiliated with LG. Rooting your TV may void its warranty; use at your own risk.
| #!/bin/sh | |
| # webos-debloat.sh: reversible, firmware-update-safe debloat for rooted LG webOS TVs. | |
| # SPDX-License-Identifier: MIT | |
| # | |
| # DESIGN RULES (read this before changing anything): | |
| # 1. NOTHING IS DELETED. No app directory is removed, no rootfs file is | |
| # written, no binary is replaced. The script only edits LG's own | |
| # "blocked system app list" JSON, which the application manager reads | |
| # when it renders the launcher, plus the preload manager's policy and | |
| # a handful of system settings. | |
| # 2. Because the rootfs is untouched, OTA firmware updates, app-store | |
| # updates and the recovery path all behave exactly as stock. That is | |
| # the whole point of using this mechanism instead of `dev/remove`. | |
| # 3. Every change is reversible (--restore, --preload-on, --settings-restore). | |
| # 4. Original files are backed up exactly once and never overwritten, so a | |
| # later bad run cannot quietly destroy the audit trail. | |
| # | |
| # WHAT IT CHANGES | |
| # Hiding: /var/preferences/com.webos.applicationManager/blockedSystemAppList/.json | |
| # contains {"blocked_system_applist":["com.webos.app.x", ...]} | |
| # Any app ID in that array is hidden from the launcher. The binaries stay | |
| # on disk and launch normally if something else calls them. Removing the | |
| # ID from the array brings the tile back. This is LG's own regional app | |
| # blocking mechanism, not a hack. The application manager may rewrite the | |
| # file on its own (e.g. after a component update), so `--install-boot-hook` | |
| # re-applies it at boot. | |
| # | |
| # Preloads: hiding is cosmetic; it does NOT stop a process or free memory. | |
| # The preload manager keeps some apps (LG Channels, the browser) resident | |
| # whether or not they are visible or ever opened. `--preload-off` turns | |
| # that off, which is what actually frees RAM. | |
| # | |
| # Settings: `--settings-off` switches off home-screen promotions, promo | |
| # videos, sports alerts and content recommendations through the settings | |
| # service. These persist on their own. | |
| # | |
| # USAGE | |
| # sh webos-debloat.sh --list # inventory installed apps | |
| # sh webos-debloat.sh --plan # show what --apply would hide | |
| # sh webos-debloat.sh --apply # hide the default categories | |
| # sh webos-debloat.sh --apply --only ad,sdk,demo,remote,promo | |
| # sh webos-debloat.sh --apply --only visible # visible tiles | |
| # sh webos-debloat.sh --apply --only extras # tracking/AI bloat | |
| # sh webos-debloat.sh --apply --only all # everything | |
| # sh webos-debloat.sh --apply --id com.webos.app.lgchannels # single app | |
| # sh webos-debloat.sh --restore --id com.webos.app.lgchannels # undo that one | |
| # sh webos-debloat.sh --restore # un-hide everything we added | |
| # sh webos-debloat.sh --status # what is currently changed | |
| # sh webos-debloat.sh --install-boot-hook # re-apply at every boot | |
| # sh webos-debloat.sh --remove-boot-hook | |
| # sh webos-debloat.sh --preload-off # stop preloading LG Channels + browser | |
| # sh webos-debloat.sh --preload-off --id com.webos.app.livemenu | |
| # sh webos-debloat.sh --preload-on # undo every --preload-off | |
| # sh webos-debloat.sh --settings-off # promos, sports alerts, recommendations | |
| # sh webos-debloat.sh --settings-restore # put those settings back | |
| # | |
| # --dry-run previews --apply, --restore, --preload-* and --settings-* | |
| # without changing anything. --region XX picks the list file when the TV | |
| # has none yet (default USA). --preload-sync is what the boot hook runs. | |
| # | |
| # REQUIREMENTS | |
| # Root on the TV (via Homebrew Channel). Run it on the TV itself, e.g.: | |
| # ssh root@ 'sh /tmp/webos-debloat.sh --list' | |
| # | |
| # SAFETY | |
| # The PROTECTED_IDS list below is never hidden regardless of what you ask | |
| # for. It contains every app whose removal has a known way of breaking the | |
| # TV's own UI (settings panel, input switching, first-run flow, launcher). | |
| # ID lists are whitespace-separated strings and are split on purpose. | |
| # shellcheck disable=SC2086 | |
| set -u | |
| APPS_DIR=/var/preferences/com.webos.applicationManager/blockedSystemAppList | |
| BACKUP_DIR=/var/lib/webos-debloat | |
| HOOK=/var/lib/webosbrew/init.d/50-debloat | |
| STATE=$BACKUP_DIR/state | |
| PRELOAD_STATE=$BACKUP_DIR/preload-off | |
| PRELOAD_CONF=/var/preferences/webos-preload-manager-conf.json | |
| PRELOAD_SVC=luna://com.webos.service.preloadmanager | |
| SAM_SVC=luna://com.webos.service.applicationmanager | |
| SETTINGS_SVC=luna://com.webos.settingsservice | |
| SETTINGS_STATE=$BACKUP_DIR/settings-off | |
| APP_BASES=" | |
| /media/system/apps/usr/palm/applications | |
| /usr/palm/applications | |
| /mnt/otncabi/usr/palm/applications | |
| /mnt/otycabi/usr/palm/applications | |
| " | |
| DRY_RUN=0 | |
| CATS="" | |
| EXTRA_IDS="" | |
| # --------------------------------------------------------------------------- | |
| # Never hide these. Each one has a known failure mode if it disappears. | |
| # --------------------------------------------------------------------------- | |
| PROTECTED_IDS=' | |
| com.webos.app.livetv | |
| com.webos.app.home | |
| com.webos.app.launcher | |
| com.palm.app.settings | |
| com.webos.app.settings | |
| com.webos.app.quicksettings | |
| com.webos.app.firstuse | |
| com.webos.app.eula | |
| com.webos.app.webapphost | |
| com.webos.app.container | |
| container | |
| com.webos.app.inputcommon | |
| inputcommon | |
| com.webos.app.inputlist | |
| com.webos.app.notificationcenter | |
| com.webos.app.browser | |
| com.webos.app.mediaplayer | |
| com.webos.app.mvpd | |
| com.webos.app.install | |
| com.webos.app.screensaver | |
| com.webos.app.volume | |
| com.webos.app.epg | |
| com.webos.app.tvguide | |
| com.webos.app.hdmi1 | |
| com.webos.app.hdmi2 | |
| com.webos.app.hdmi3 | |
| com.webos.app.hdmi4 | |
| org.webosbrew.hbchannel | |
| com.tvweb.dashboard | |
| com.tvweb.home | |
| ' | |
| # --------------------------------------------------------------------------- | |
| # Curated bloat, grouped. Only IDs that are actually installed get written, | |
| # so IDs from other webOS versions are simply skipped. | |
| # --------------------------------------------------------------------------- | |
| IDS_AD_ACR=' | |
| com.webos.app.acrcomponent | |
| com.webos.app.acrhdmi1 | |
| com.webos.app.acrhdmi2 | |
| com.webos.app.acrhdmi3 | |
| com.webos.app.acrhdmi4 | |
| com.webos.app.acroverlay | |
| com.webos.app.adoverlay | |
| com.webos.app.adoverlayex | |
| com.webos.app.adhdmi1 | |
| com.webos.app.adhdmi2 | |
| com.webos.app.adhdmi3 | |
| com.webos.app.adhdmi4 | |
| com.webos.app.fooddelivery | |
| com.webos.app.fooddeliveryex | |
| com.webos.app.fooddeliveryhdmi1 | |
| com.webos.app.fooddeliveryhdmi2 | |
| com.webos.app.fooddeliveryhdmi3 | |
| com.webos.app.fooddeliveryhdmi4 | |
| com.webos.app.overlaymembership | |
| com.webos.app.videoads | |
| com.webos.app.cmp-client | |
| com.webos.app.newandhot | |
| ' | |
| IDS_REMOTE=' | |
| com.webos.app.remoteservice | |
| ' | |
| IDS_SDK=' | |
| com.webos.exampleapp.enyoapp.epg | |
| com.webos.exampleapp.groupowner | |
| com.webos.exampleapp.nav | |
| com.webos.exampleapp.qmlapp.client.negative.one | |
| com.webos.exampleapp.qmlapp.client.negative.two | |
| com.webos.exampleapp.qmlapp.client.positive.one | |
| com.webos.exampleapp.qmlapp.client.positive.two | |
| com.webos.exampleapp.qmlapp.discover | |
| com.webos.exampleapp.qmlapp.epg | |
| com.webos.exampleapp.qmlapp.hbbtv | |
| com.webos.exampleapp.qmlapp.livetv | |
| com.webos.exampleapp.qmlapp.search | |
| com.webos.exampleapp.systemui | |
| ' | |
| IDS_DEMO=' | |
| com.webos.app.store-demo | |
| com.webos.app.sync-demo | |
| com.webos.app.factorywin | |
| com.webos.app.svcdiagnostics | |
| com.webos.app.quickrecovery | |
| com.webos.app.renewupdate | |
| ' | |
| # Opt-in only: these are promotional / content-discovery tiles. Hiding them is | |
| # safe but some people actually use the guide and the music player, so they | |
| # are not in the default set. | |
| IDS_PROMO=' | |
| com.webos.app.discovery | |
| com.webos.app.lgchannels | |
| com.webos.app.channelplus | |
| com.webos.app.sportsevent | |
| com.webos.app.music | |
| com.webos.app.photovideo | |
| com.webos.app.userguide | |
| com.webos.app.help | |
| com.webos.app.today | |
| com.webos.app.recent | |
| com.webos.app.accuweather | |
| com.webos.app.tvplus | |
| com.webos.app.manufacturer | |
| ' | |
| # Visible launcher tiles that are safe to hide. These are the tiles people | |
| # actually see; the default categories above mostly target background | |
| # services. Essentials (Home, Settings, Browser, inputs, etc.) are in | |
| # PROTECTED_IDS and are never hidden even if listed here. | |
| IDS_VISIBLE=' | |
| com.webos.app.lgchannels | |
| com.lge.app.viewster | |
| com.webos.app.homeconnect | |
| com.webos.app.lglink | |
| com.webos.app.premium | |
| com.webos.app.lifeonscreen | |
| com.webos.app.lifeonscreen-legacy | |
| com.webos.app.quicklinkcopilot | |
| com.webos.app.sportsteamsettings | |
| com.webos.app.discovery | |
| com.webos.app.storagemanager | |
| com.webos.app.camera | |
| com.webos.app.multiviewsettings | |
| com.webos.app.mediadiscovery | |
| ' | |
| # Opt-in: additional tracking / AI / promo / support apps that are installed | |
| # but invisible in the launcher. These are safe to hide, but some are | |
| # features (LG Account, Remote PC, mobile connection) so they are not in | |
| # the default set. Inputs, accessibility, setup, keyboard, updates, casting | |
| # and TV tuning are deliberately excluded. | |
| IDS_EXTRAS=' | |
| com.webos.app.adapp | |
| com.webos.app.aiagent | |
| com.webos.app.aiplatform | |
| com.webos.app.aiplatformsupport | |
| com.webos.app.aipromotion | |
| com.webos.app.assistant | |
| com.webos.app.voice | |
| com.webos.app.beanbrowser | |
| com.webos.app.crb | |
| com.webos.app.livedmost | |
| com.webos.app.liveginga | |
| com.webos.app.livehbbtv | |
| com.webos.app.liveinteractivecontent | |
| com.webos.app.masterbrand | |
| com.webos.app.magicnum | |
| com.webos.app.cpcover | |
| com.webos.app.store-streamer | |
| com.webos.app.universalqcard | |
| com.webos.app.totalmusic | |
| com.webos.app.tvuserguide | |
| com.webos.app.helpandtips | |
| com.webos.app.membership | |
| com.webos.app.minimalmembership | |
| com.webos.app.familycare | |
| com.webos.app.learning | |
| com.webos.app.homeoffice | |
| com.webos.app.sportsalarm | |
| com.webos.app.self-diagnosis | |
| com.webos.app.customersupport | |
| com.webos.app.remotecontrolguide | |
| com.webos.app.seniorhome | |
| com.webos.app.simplecalendar | |
| com.webos.app.similarlist | |
| com.webos.app.homeconnect-overlay | |
| com.webos.app.rdp | |
| com.webos.app.mobileconnection | |
| ' | |
| DEFAULT_CATS="ad,sdk,demo,remote" | |
| # Default --preload-off targets: apps the preload manager keeps resident in | |
| # RAM even when nobody opens them. Disabling a preload only costs a slower | |
| # first launch. Home, Live TV and HDMI preloads are deliberately left alone: | |
| # they are what make the Home button and input switching fast. | |
| PRELOAD_OFF_IDS=' | |
| com.webos.app.lgchannels | |
| com.webos.app.browser | |
| ' | |
| # --settings-off targets, as settings-service category:key. All take "on" or | |
| # "off"; keys this firmware doesn't have are skipped. livePlus is LG's | |
| # automatic content recognition (ACR). Deliberately left alone: quickStartMode | |
| # (fast power-on) and homeAutoLaunch (open Home at power-on). | |
| SETTINGS_OFF=' | |
| general:homePromotion | |
| general:sportsAlarm | |
| option:livePromotion | |
| option:webOSPromotionVideo | |
| option:promotionStreamer | |
| option:livePlus | |
| other:contentRecommendation | |
| ' | |
| # --------------------------------------------------------------------------- | |
| # helpers | |
| # --------------------------------------------------------------------------- | |
| log() { printf '%s\n' "$*"; } | |
| warn() { printf 'WARN: %s\n' "$*" >&2; } | |
| err() { printf 'ERROR: %s\n' "$*" >&2; } | |
| die() { err "$*"; exit 1; } | |
| require_root() { | |
| [ "$(id -u 2>/dev/null)" = "0" ] || die "must run as root on the TV" | |
| } | |
| has_cmd() { command -v "$1" >/dev/null 2>&1; } | |
| # the leading comment block, minus the shebang | |
| usage() { awk 'NR > 1 { if (!/^#/) exit; sub(/^# ?/, ""); print }' "$0"; } | |
| # one ID per line out of the JSON array, tolerant of pretty-printing | |
| read_ids() { | |
| [ -f "$1" ] || return 0 | |
| awk ' | |
| { buf = buf $0 "\n" } | |
| END { | |
| i = index(buf, "\"blocked_system_applist\"") | |
| if (i > 0) { | |
| rest = substr(buf, i) | |
| j = index(rest, "[") | |
| if (j > 0) { | |
| rest = substr(rest, j + 1) | |
| k = index(rest, "]") | |
| if (k > 0) { | |
| arr = substr(rest, 1, k - 1) | |
| n = split(arr, parts, ",") | |
| for (p = 1; p <= n; p++) { | |
| s = parts[p] | |
| gsub(/[ \t\r\n]/, "", s) | |
| gsub(/"/, "", s) | |
| if (s != "") print s | |
| } | |
| } | |
| } | |
| } | |
| }' "$1" | |
| } | |
| write_ids() { | |
| # $1 = file, remaining args = ids | |
| file=$1; shift | |
| body="" | |
| for id in "$@"; do | |
| [ -n "$id" ] || continue | |
| if [ -n "$body" ]; then body="$body,\"$id\""; else body="\"$id\""; fi | |
| done | |
| tmp="$file.tmp.$" | |
| printf '{"blocked_system_applist":[%s]}\n' "$body" > "$tmp" || die "cannot write $tmp" | |
| mv -f "$tmp" "$file" || die "cannot replace $file" | |
| } | |
| in_list() { | |
| needle=$1; shift | |
| for x in "$@"; do [ "$x" = "$needle" ] && return 0; done | |
| return 1 | |
| } | |
| is_protected() { in_list "$1" $PROTECTED_IDS; } | |
| # does the app actually exist on this TV? | |
| app_present() { | |
| id=$1 | |
| for base in $APP_BASES; do | |
| [ -f "$base/$id/appinfo.json" ] && return 0 | |
| done | |
| [ -d "/media/cryptofs/apps/usr/palm/applications/$id" ] && return 0 | |
| return 1 | |
| } | |
| appinfo_field() { | |
| # $1 file, $2 key (string value) | |
| sed -n "s/.*\"$2\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" "$1" 2>/dev/null | head -n 1 | |
| } | |
| app_visible() { | |
| # prints yes/no | |
| v=$(sed -n 's/.*"visible"[[:space:]]*:[[:space:]]*\(true\|false\).*/\1/p' "$1" 2>/dev/null | head -n 1) | |
| [ "$v" = "false" ] && printf 'no\n' || printf 'yes\n' | |
| } | |
| pick_list_file() { | |
| if [ -d "$APPS_DIR" ]; then | |
| for f in "$APPS_DIR"/*.json; do | |
| [ -e "$f" ] || continue | |
| printf '%s\n' "$f" | |
| return 0 | |
| done | |
| fi | |
| region=${WEBOS_REGION:-USA} | |
| printf '%s/%s.json\n' "$APPS_DIR" "$region" | |
| } | |
| ensure_dir() { | |
| [ -d "$APPS_DIR" ] && return 0 | |
| if [ "$DRY_RUN" = "1" ]; then | |
| warn "dry-run: would create $APPS_DIR" | |
| return 0 | |
| fi | |
| mkdir -p "$APPS_DIR" 2>/dev/null || die "cannot create $APPS_DIR (is /var writable?)" | |
| } | |
| backup_once() { | |
| file=$1 | |
| [ -f "$file" ] || return 0 | |
| mkdir -p "$BACKUP_DIR" 2>/dev/null || true | |
| base=$(basename "$file") | |
| if [ ! -f "$BACKUP_DIR/$base.orig" ]; then | |
| cp "$file" "$BACKUP_DIR/$base.orig" || warn "could not back up $file" | |
| log "backed up original to $BACKUP_DIR/$base.orig" | |
| fi | |
| } | |
| # --------------------------------------------------------------------------- | |
| # --list | |
| # --------------------------------------------------------------------------- | |
| cmd_list() { | |
| log "Installed apps (appid | visible | title | source dir)" | |
| log "--------------------------------------------------------------------" | |
| seen="" | |
| for base in $APP_BASES; do | |
| [ -d "$base" ] || continue | |
| for d in "$base"/*; do | |
| [ -d "$d" ] || continue | |
| [ -f "$d/appinfo.json" ] || continue | |
| id=$(appinfo_field "$d/appinfo.json" id) | |
| [ -n "$id" ] || id=$(basename "$d") | |
| case " $seen " in *" $id "*) continue;; esac | |
| seen="$seen $id" | |
| title=$(appinfo_field "$d/appinfo.json" title) | |
| vis=$(app_visible "$d/appinfo.json") | |
| printf '%-42s %-4s %-28s %s\n' "$id" "$vis" "${title:--}" "$(basename "$base")" | |
| done | |
| done | |
| log "--------------------------------------------------------------------" | |
| log "Total: $(printf '%s\n' $seen | wc -l | tr -d ' ') apps" | |
| } | |
| # --------------------------------------------------------------------------- | |
| # category resolution | |
| # --------------------------------------------------------------------------- | |
| resolve_cats() { | |
| # A bare --id means "only this app", not "default categories plus this app". | |
| if [ -z "$CATS" ] && [ -n "$EXTRA_IDS" ]; then | |
| printf '%s\n' $EXTRA_IDS | |
| return 0 | |
| fi | |
| [ -n "$CATS" ] || CATS=$DEFAULT_CATS | |
| wanted="" | |
| old_ifs=$IFS; IFS=',' | |
| for c in $CATS; do | |
| case "$c" in | |
| ad) wanted="$wanted $IDS_AD_ACR" ;; | |
| remote) wanted="$wanted $IDS_REMOTE" ;; | |
| sdk) wanted="$wanted $IDS_SDK" ;; | |
| demo) wanted="$wanted $IDS_DEMO" ;; | |
| promo) wanted="$wanted $IDS_PROMO" ;; | |
| visible) wanted="$wanted $IDS_VISIBLE" ;; | |
| extras) wanted="$wanted $IDS_EXTRAS" ;; | |
| all) wanted="$wanted $IDS_AD_ACR $IDS_REMOTE $IDS_SDK $IDS_DEMO $IDS_PROMO $IDS_VISIBLE $IDS_EXTRAS" ;; | |
| *) warn "unknown category '$c' (known: ad, remote, sdk, demo, promo, visible, extras, all)" ;; | |
| esac | |
| done | |
| IFS=$old_ifs | |
| printf '%s\n' $wanted $EXTRA_IDS | |
| } | |
| # IDs that --restore/--status treat as ours. With no selection this is every | |
| # category, since earlier runs may have used --only all. IDs that LG itself | |
| # blocked (present in the .orig backup) are never ours, so a restore can't | |
| # un-hide something stock firmware hides. | |
| our_ids() { | |
| orig=$(read_ids "$BACKUP_DIR/$(basename "$1").orig") | |
| if [ -z "$CATS" ] && [ -z "$EXTRA_IDS" ]; then | |
| ids=$(CATS=all resolve_cats) | |
| else | |
| ids=$(resolve_cats) | |
| fi | |
| for id in $ids; do | |
| in_list "$id" $orig || printf '%s\n' "$id" | |
| done | |
| } | |
| # IDs from the chosen categories that exist here and are not protected | |
| present_targets() { | |
| seen="" | |
| for id in $(resolve_cats); do | |
| case " $seen " in *" $id "*) continue;; esac | |
| seen="$seen $id" | |
| if is_protected "$id"; then | |
| continue | |
| fi | |
| if app_present "$id"; then | |
| printf '%s\n' "$id" | |
| fi | |
| done | |
| } | |
| # --------------------------------------------------------------------------- | |
| # --plan | |
| # --------------------------------------------------------------------------- | |
| cmd_plan() { | |
| file=$(pick_list_file) | |
| log "Target list file : $file" | |
| if [ -z "$CATS" ] && [ -n "$EXTRA_IDS" ]; then | |
| log "Selection : --id only ($EXTRA_IDS)" | |
| else | |
| log "Categories : ${CATS:-$DEFAULT_CATS}${EXTRA_IDS:+ + id:$EXTRA_IDS}" | |
| fi | |
| log "" | |
| n=0 | |
| for id in $(present_targets); do | |
| title="" | |
| for base in $APP_BASES; do | |
| if [ -f "$base/$id/appinfo.json" ]; then | |
| title=$(appinfo_field "$base/$id/appinfo.json" title) | |
| break | |
| fi | |
| done | |
| printf ' hide %-42s %s\n' "$id" "${title:--}" | |
| n=$((n + 1)) | |
| done | |
| log "" | |
| log "Would hide $n app(s). Nothing was changed." | |
| [ "$n" = "0" ] && warn "no target apps found - run --list to see what this TV actually has" | |
| return 0 | |
| } | |
| # --------------------------------------------------------------------------- | |
| # --apply | |
| # --------------------------------------------------------------------------- | |
| cmd_apply() { | |
| require_root | |
| file=$(pick_list_file) | |
| ensure_dir | |
| backup_once "$file" | |
| # existing (LG's own regional blocks) must be preserved | |
| existing=$(read_ids "$file" | grep -v '^ |