All TVs with a general purpose processors nowadays come with pre-installed malware, whether it is adware or spyware. Think whatever you want about that, but if you bought an LG TV and don't want to purchase an another general purpose external processor on top of the one you already bought (like nvidia shield, apple tv, etc.), there are some steps you can take to make your TV usable by disabling built in software and adding some of our own.
Note that this guide assumes that the only things you want to do with your TV is to play video files (up to 4K DoVi with FLAC audio) and sometimes watch youtube at undetermined video quality without ads
Requirements:
- An LG TV (preferably an OLED TV, because anything inferior is inferior)
- Local Plex server instance, no paid version required
- nginx server or any other robust web server
Step 0
Before buying the TV, do research to see if the WebOS version that comes with the TV preinstalled is rootable. If not, refrain from buying.
Step 1
To make this guide future proof, the first step would be to configure your router to force-route DNS requests to a local filtering DNS server like pihole or other. For RouterOS, following entries work provided you are using IPv4 under NAT:
chain=dstnat action=dst-nat to-addresses=192.168.0.1 to-ports=53
protocol=udp src-address=192.168.0.10 dst-port=53 log=no log-prefix=""
chain=dstnat action=dst-nat to-addresses=192.168.0.1 to-ports=53
protocol=udp src-address=192.168.0.11 dst-port=53 log=no log-prefix=""
chain=dstnat action=dst-nat to-addresses=192.168.0.1 to-ports=53
protocol=tcp src-address=192.168.0.10 dst-port=53 log=no log-prefix=""
chain=dstnat action=dst-nat to-addresses=192.168.0.1 to-ports=53
protocol=tcp src-address=192.168.0.11 dst-port=53 log=no log-prefix=""
Next, block all DNS requests, but the ones that you actually want and recognize.
For pihole, add the following entries to "Domain management" page:
| Domain/RegEx | Type |
|---|---|
| .* | Regex blacklist |
| youtube.com | Exact whitelist |
| (\.|^)ytimg\.com$ | Regex whitelist |
| (\.|^)googlevideo\.com$ | Regex whitelist |
| sponsorblock.inf.re | Exact whitelist |
| (\.|^)githubusercontent\.com$ | Regex whitelist |
| plex.yourdomain.local | Exact whitelist |
| repo.webosbrew.org | Exact whitelist |
Now, we run into first WebOS strangeness - system time is not synced using NTP, which is the standard protocol to sync time since 1985. Instead, WebOS uses a completely undocumented in-house hack with zero benefits over NTP. To allow time synchronization, you need to whitelist (\.|^)lgtvsdp\.com$ and redirect it to your web server which will respond with x-server-time header.
For pihole, add the following entries to "Domain management" page:
| Domain/RegEx | Type |
|---|---|
| (\.|^)lgtvsdp\.com$ | Regex whitelist |
As pihole capabilities are pretty limited, you will now need to turn on your TV for the first time and look at the list of blocked DNS requests. Deny all and any requests to update anything if prompted by WebOS. Search for lgtvsdp.com and check what subdomain is being queried (it varies by region). In "Local DNS Records [A/AAAA]" page, add following entries which will redirect this request to a web server. My region is "eu7" and my nginx server is at 192.168.0.1:
| Domain | IP |
|---|---|
| eu7.lgtvsdp.com | 192.168.0.1 |
In web server configuration, set up such that a request on a page with such domain will respond with x-server-time header. For nginx, it would be something like that. It doesn't seem that WebOS verifies the SSL certificate for this request. Note that I'm restricting this request to TVs IPs (one for ethernet connection, other one for WiFi) and SSL is yours to set up.
server {
server_name "~.*(lgtvsdp)\.com$";
listen 0.0.0.0:443 ssl;
listen [::]:443 ssl;
include /etc/nginx/ssl.conf;
set $not_tv_ip 1;
if ($proxy_protocol_addr = '192.168.0.10') {
set $not_tv_ip 0;
}
if ($proxy_protocol_addr = '192.168.0.11') {
set $not_tv_ip 0;
}
if ($not_tv_ip) {
return 404;
}
location / {
deny all;
if ($msec ~ "(.*)\.(.*)") {
set $epoch_millis "$1$2";
add_header x-server-time $epoch_millis;
}
return 200;
}
}
Step 2
When you get your TV out of the box, preinstalled WebOS is restricted to running under a jailed non-root user with countermeasures in place to prevent you from getting control of your device (which I don't know how is still not illegal), therefore you must root it. Using the TV without root privileges is very cumbersome, doesn't allow you to do much.
To make this guide again future proof, search for a rooting guide by your own, but the current tools are at https://cani.rootmy.tv/. Root the TV.
After rooting, go into webosbrew settings and enable SSH server (if not enabled) and block system updates.
Step 3
To remove the annoying and time consuming box asking you to update WebOS to a non-rootable version, create an empty file in /home/root/:
touch /home/root/empty_file
Next, create an executable file in /var/lib/webosbrew/init.d named updateRemove:
#!/bin/sh
mount --bind "/home/root/empty_file" "/usr/sbin/update"
Step 4
Traverse through all WebOS settings and adjust to your liking, turn off anything that smells like bullshit.
Enter Homebrew channel application and install applications you need:
- Kodi
- LG App Update Blocker
- YouTube AdFree
In Kodi:
- Install PlexKodiConnect and configure it. While there, in Playback, set "Preferred playback method" to "Try Direct Path", set "Video Quality if Transcoding necessary" to highest, set "Force transcode AV1" to quality that is supported by your server hardware, enable "Force Transcode Hi10P"
- Enter Settings / Player / Videos and set "Allowed HDR dynamic metadata formats" to only "Dolby Vision". This ensures compatibility with select OLED models by completely disabling HDR10+.
Step 5
Enter home app, select apps you installed one-by-one and hold a number on your remote to make the app accessible by shortcut.
Now, when you turn on your TV, you don't need to load home app to launch the application you actually want to launch.
Enjoy!