Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
A script that fetches a ChromeOS image for ARM32 and extracts the Widevine binary, saving it in a compressed archive for use with Vivaldi

The included script '' fetches a ChromeOS image for ARM and extracts the Widevine binary, saving it in a compressed archive. Since it downloads a fairly large file (2Gb+ on disk after download) it is recommended that you run the script on a machine that has plenty of disk space.

To install the resultant archive, issue the following on your ARM machine–after copying over the archive if needed:

sudo tar Cfx / widevine-flash-20200124_armhf.tgz

(Where 'widevine-flash-20200124_armhf.tgz' is updated to reflect the actual name of the created archive)

After install of these libraries, issue the following two commands and restart Vivaldi–assumes bash as your shell:

mkdir -p ~/.config/vivaldi{,-snapshot}/WidevineCdm
echo '{"Path":"/opt/WidevineCdm"}' | tee ~/.config/vivaldi/WidevineCdm/latest-component-updated-widevine-cdm > ~/.config/vivaldi-snapshot/WidevineCdm/latest-component-updated-widevine-cdm

(Triple click to select each complete line above)

This created package is designed to work on Vivaldi 2.10 (not pure Chromium 79+, which would require patches to detect Widevine on ARM).

#!/bin/sh -eu
# Make sure we have wget or curl
available () {
command -v "$1" >/dev/null 2>&1
if available wget; then
DL="wget -O-"
DL_SL="wget -qO-"
elif available curl; then
DL="curl -L"
DL_SL="curl -s"
echo "Install Wget or cURL" >&2
exit 1
# Find a URL to a suitable armhf ChromeOS recovery image
CHROMEOS_URL="$($DL_SL | grep -A11 CB5-312T | sed -n 's/^url=//p')"
CHROMEOS_IMG="$(basename "$CHROMEOS_URL" .zip)"
if [ -e "$CHROMEOS_IMG" ]; then
# Fetch the recovery image (2Gb+ on disk after download)
# Note the next free loop device in a variable
LOOPD="$(losetup -f)"
# If root, we can mount silently (no popup windows after mount)
if [ "$USER" = "root" ]; then
MNTPNT="$(mktemp -d -t ChromeOS.XXXXXX)"
mount -o ro "${LOOPD}p3" "$MNTPNT"
# Associate all the partitions on the disk image with loop devices:
udisksctl loop-setup -rf "$CHROMEOS_IMG_PATH/$CHROMEOS_IMG"
sleep 1
# Mount the third partition of the disk image (if the previous did not do it automatically)
if ! lsblk -lo MOUNTPOINT "${LOOPD}p3" | tail -n1 | grep -q \.; then
udisksctl mount -b "${LOOPD}p3"
# Note the mount point in a variable
MNTPNT="$(lsblk -lo MOUNTPOINT "${LOOPD}p3" | tail -n1)"
# Copy over files and make manifest
CHRFILES="$(mktemp -d -t ChromeOS_Files.XXXXXX)"
install -Dm644 "$MNTPNT"/opt/google/chrome/ "$CHRFILES"/opt/WidevineCdm/_platform_specific/linux_arm/
WVVER="$(grep -Eaom1 '([0-9]+\.){3}[0-9]+' "$CHRFILES"/opt/WidevineCdm/_platform_specific/linux_arm/"
WVMGR="$(echo $WVVER | cut -d. -f1)"
WVMIN="$(echo $WVVER | cut -d. -f2)"
echo "{\"version\":\"$WVVER\",\"x-cdm-codecs\":\"vp8,vp9.0,avc1,av01\",\"x-cdm-host-versions\":\"$WVMIN\",\"x-cdm-interface-versions\":\"$WVMIN\",\"x-cdm-module-versions\":\"$WVMGR\"}" > "$CHRFILES"/opt/WidevineCdm/manifest.json
# Extract the libs out and copy them to a compressed tar archive
ARCHIVE_NAME="widevine-flash-$(date '+%Y%m%d')_armhf.tgz"
echo "Extracting and compressing files"
tar -C"$CHRFILES" -caf "$ARCHIVE_NAME" opt/WidevineCdm/manifest.json opt/WidevineCdm/_platform_specific/linux_arm/ --format ustar --owner 0 --group 0
rm -r "$CHRFILES"
echo "Created: $ARCHIVE_NAME"
# Cleanup
if [ "$USER" = "root" ]; then
umount "$MNTPNT"
losetup -d "$LOOPD"
rmdir "$MNTPNT"
ALLMNTS="$(lsblk -lo NAME,MOUNTPOINT "$LOOPD" | sed -n '/\//s/^\(loop[0-9]\+p[0-9]\+\).*/\1/p')"
echo "$ALLMNTS" | xargs -I{} -n1 udisksctl unmount -b /dev/{}
if [ "$LOOPD" != "$(losetup -f)" ]; then
udisksctl loop-delete -b "$LOOPD"
if [ "$DEL_IMG" = "N" ] || [ "${1:-EMPTY}" = "-k" ]; then
# Inform the user how to proceed
cat <<EOF
To install the contents of these files on an ARMhf device, copy the tar archive over to the target machine and issue the following:
sudo tar Cfx / $ARCHIVE_NAME
After install of these libraries, issue the following two commands and restart Vivaldi:
mkdir -p ~/.config/vivaldi/WidevineCdm
echo '{"Path":"/opt/WidevineCdm"}' > ~/.config/vivaldi/WidevineCdm/latest-component-updated-widevine-cdm
Copy link

krisztianb commented Oct 14, 2020

I've just tested this successfully with the latest Raspberry Pi OS (2020-08-20) and latest Vivaldi (3.3). Thank you very much! 👍


Actually you don't need Vivaldi and can use Chromium. You just have to copy the file from the TGZ to the following folder on your Pi: /usr/lib/chromium-browser/

Copy link

BelT327l commented Oct 19, 2020

cmon, the script runs, but it uses the WRONG partition/folder which is §$&§$&§$& too small for that ~ 1 GiB... on my emmc card not much space left.
i have enough external drives which fit that download thousand times but the heck just aims for the real wrong turn. stdout folder plz noot

Is there a way to add some lines to force to use my favourite work folder (not using root dir) especially on an external drive /media/usr/whateverilike)?

Thanks guys and girls.

Copy link

mobluse commented Nov 5, 2020

I've just tested this successfully in Vivaldi with the latest Raspberry Pi OS (2020-11-05) and latest Vivaldi (3.4). I run the script, but got errors related to loop devices, and it opened them in folders, but when I run it the second time it worked but then stopped, but when I closed the folders it finished.

I could not get it to work in Chromium by hard linking to /usr/lib/chromium-browser/.

Copy link

zenagius commented Nov 6, 2020

I tested as well today with Vivaldi 3.4.2066.94 and Widevine 4.10.1679.0 on a Rpi 4. Lib is loaded and DRM passes the test but Netflix deny the access with this stupid error.
As mentioned above, it's cause by the User Agent. The User Agent Switcher extension in Chrome Store are not working properly (they must be very badly coded, this is just a HTTP header 🤦)
Switching the user agent from the CLI is simpler and working fine :

vivaldi-stable %U --user-agent="Mozilla/5.0 (X11; CrOS armv7l 12371.89.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"

Copy link

MarFel90 commented Nov 21, 2020

Hi, thanks for the script!
Unfortunatly it does not work with Vivaldi 3.4.2066.106 and Widevine 4.10.1679.0 on my OPi+ with Armbian 20.08.17 Buster.
I used script to extract
Bitmovin DRM test is not passing and vivaldi://components says Version
Is there any chance to fix this for my system?

Copy link

iisti commented Apr 4, 2021

Thank you very much for the script! I bought Raspberry for creating a silent media center, but was disappointed when Netflix didn't work. With the script I got it working. I did some work to get the script working with the newest versions.

  • Using:
    • Raspberry Pi 4B, 8GB
    • OS image:
    • Vivaldi version: vivaldi-stable_3.7.2218.52-1_armhf.deb

Flash player is obsolete

  • Running the script gives error about Flash player:
    pi@raspberrypi:~/widevinecdm $ ./
    --2021-04-03 18:42:38--
    Resolving (, 2a00:1450:4014:80c::200e
    Connecting to (||:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 1039156004 (991M) [application/zip]
    Saving to: ‘STDOUT’
    -                             100%[==============================================>] 991.02M  2.62MB/s    in 7m 14s
    2021-04-03 18:49:52 (2.28 MB/s) - written to stdout [1039156004/1039156004]
    Mapped file /tmp/ChromeOS-IMG.q2VV12/chromeos_13729.56.0_elm_recovery_stable-channel_mp-v2.bin as /dev/loop0.
    install: cannot stat '/media/pi/ROOT-A/opt/google/chrome/pepper/': No such file or directory
  • I think the Flash player has been removed completely from ChromeOS, so the error is not a surprise. Source:
  • Fixed the script by:
    # Remove line
    install -Dm644 "$MNTPNT"/opt/google/chrome/pepper/ "$CHRFILES"/opt/google/chrome/PepperFlash/
    # Remove Flash file from line:
    tar -C"$CHRFILES" -caf "$ARCHIVE_NAME" opt/WidevineCdm/manifest.json opt/WidevineCdm/_platform_specific/linux_arm/ opt/google/chrome/PepperFlash/ --format ustar --owner 0 --group 0
    # Line after modification
    tar -C"$CHRFILES" -caf "$ARCHIVE_NAME" opt/WidevineCdm/manifest.json opt/WidevineCdm/_platform_specific/linux_arm/ --format ustar --owner 0 --group 0

Links are not pointing to correct places

  • The Vivaldi 3.7 is pointing to /opt/google/chrome/WidevineCdm, but the script puts the files into /opt/WidevineCdm.
    • Fixed broken links by:
      cd /opt/vivaldi
      sudo rm WidevineCdm
      sudo ln -s /opt/WidevineCdm WidevineCdm
      • After restart Vivaldi should show in vivaldi://components
        Widevine Content Decryption Module - Version: 4.10.1679.0
        Status - Up-to-date



  • The Widewine files work also with Chromium, and the Netflix videos seem to run smoother (better hardware acceleration?).
    cd /usr/lib/chromium-browser
    sudo ln -s /opt/WidevineCdm WidevineCdm

Copy link

NonstickAtom785 commented May 10, 2021

@iisti I'm glad someone came up behind the original man and cleaned up the script. I was coming down here to do the same thing you did. But you beat by a month or 2. Thanks for posting this. I guess it would be wise to actually inform Vivaldi of this so they can do something about it.

Copy link

ruario commented Sep 20, 2021

@iisti Thanks! Sorry I meant to updater this long ago and in my mind I kind of thought I had but it seems not. Anyway, it is fixed now.

Copy link

waddon1 commented Oct 29, 2021

Like a comment above I tried this on a pinebook pro and got (gzip: stdout: No space left on device)

Copy link

ruario commented Oct 29, 2021

@waddon1 Because you have no space left on disk. The ChromeOS image is 2Gb+ unpacked. That is what I stated above

Since it downloads a fairly large file (2Gb+ on disk after download) it is recommended that you run the script on a machine that has plenty of disk space.

I you can create the archive on another machine and then copy it over.

Copy link

waddon1 commented Oct 29, 2021

@ruario I have around 45Gb free

Copy link

ruario commented Oct 29, 2021

@waddon1 On your /tmp partition?

Copy link

waddon1 commented Oct 29, 2021

@ruario Sorry I'm a noob at Linux didn't realise /tmp used separate storage to the rest of the filesystem

Copy link

ruario commented Oct 29, 2021

@waddon1 it may or may not. It depends on how you are setup. I am largely guessing that this might be issue given the error message you received and knowing that /tmp could be mounted separately.

Copy link

waddon1 commented Oct 29, 2021

@ruario Apparently it does on my system, as it says /tmp has 0 free storage space even though the /tmp folder is in the same partition as root which has 45gb free. I'll leave you alone now and fix it myself.

Copy link

ruario commented Oct 29, 2021

Define the variable TMPDIR to another location e.g. TMPDIR=$HOME/Downloads before running the script (or add that to the second line of the script itself) and you should be OK.

Copy link

ruario commented Oct 29, 2021

@waddon1 the above was to you

Copy link

shinkou commented Dec 17, 2021

First of all, I'd like to thank all of you who are contributing to this thread. It's been very promising so far.

@iisti I've been following the instructions here with your big update, but Vivaldi seems to crash every time with that Widevine configuration. I've tried both 5.0 and 3.4. And they both don't seem to work. Also, the symbolic link for chromium don't seem to do anything neither. Because I simply don't see Widevine under chrome://component. My best guess is that they might have changed something in that, but I could be wrong.

Copy link

NonstickAtom785 commented Dec 20, 2021

@shinkou just a thought... Have you tried using Microsoft Edge? I'm not sure if it has widevine support or not. But I will say that widevine is definitely supported on armhf and arm64. The lineage os builds for the rpis have them built in.

Copy link

Exceen commented Apr 6, 2022

How can I remove this? Especially the big files? I installed it on accident on a wrong Raspberry Pi of mine. I only find files that take a few megabytes, those aren't the issue. But the README states something about 2Gb+ files and I can't find these.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment