✔︎ Last updated on October 2nd, 2024
If you are looking to download the MKBHD’s recently launched wallpapers, you are in the right place.
YouTuber Marques Brownlee, aka MKBHD, is known for his tech expertise, but his latest venture, a wallpaper app called Panels, has left many of us scratching our heads. After 15 years of building a reputation in the tech world, MKBHD put it all on the line with this app, but unfortunately, it’s been a disaster from the start.
From the get-go, people were unhappy with the app’s permissions, security, pricing, and even its aggressive ad placement. The “premium” wallpapers were hidden behind a paywall, while the free ones were only available in SD quality (480p) – and only after you’d sat through a 30-second ad (initially, it was two ads, but they later reduced it to one).
The TL;DR Version
Here’s the gist: people didn’t take long to reverse-engineer the app, figuring out where MKBHD was storing his wallpapers. One of the programmers Nadim Kobeissi soon wrote a script to download all the wallpapers in HD quality for free!
As it turns out, the URL from where these wallpapers were served was this: https://storage.googleapis.com/panels-api/data/20240916/media-1a-i-p~s. (They have now changed it.)
Visiting this page revealed all the links for all the wallpapers in both HD and SD quality.
The links with dhd
key are for HD and dsd
are the SD ones.
(There goes the security of your paywalled content).
Nadim Kobeissi has humorously named the script MKBSD which is a play on the Marques’ pseudonym (MKBHD).
The script is available for Python and Javascript.
This is what the JavaScript version looks like:
// Copyright 2024 Nadim Kobeissi
// Licensed under the WTFPL License
// Importing the 'fs' module for file system operations
const fs = require(`fs`);
// Importing the 'path' module for handling file and directory paths
const path = require(`path`);
// Main asynchronous function to execute the script
async function main() {
// URL to fetch JSON data from
const url = 'https://storage.googleapis.com/panels-api/data/20240916/media-1a-i-p~s';
// Function to create a delay (in milliseconds)
const delay = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Try-catch block to handle errors during execution
try {
// Fetching the JSON data from the specified URL
const response = await fetch(url);
// Checking if the response is OK (status code 200-299)
if (!response.ok) {
// Throwing an error if the fetch fails
throw new Error(`⛔ Failed to fetch JSON file: ${response.statusText}`);
}
// Parsing the JSON data
const jsonData = await response.json();
// Extracting the 'data' property from the JSON
const data = jsonData.data;
// Checking if 'data' exists
if (!data) {
// Throwing an error if 'data' is not found
throw new Error('⛔ JSON does not have a "data" property at its root.');
}
// Defining the directory to save downloaded images
const downloadDir = path.join(__dirname, 'downloads');
// Checking if the download directory exists
if (!fs.existsSync(downloadDir)) {
// Creating the directory if it does not exist
fs.mkdirSync(downloadDir);
console.info(`📁 Created directory: ${downloadDir}`);
}
// Initializing a file index for naming downloaded images
let fileIndex = 1;
// Iterating over each key in the data object
for (const key in data) {
const subproperty = data[key];
// Checking if the subproperty has a 'dhd' property (image URL)
if (subproperty && subproperty.dhd) {
const imageUrl = subproperty.dhd; // Extracting the image URL
console.info(`🔍 Found image URL!`);
await delay(100); // Adding a small delay before downloading
// Extracting the file extension from the image URL
const ext = path.extname(new URL(imageUrl).pathname) || '.jpg';
// Constructing the filename for the downloaded image
const filename = `${fileIndex}${ext}`;
// Constructing the full path for saving the image
const filePath = path.join(downloadDir, filename);
// Calling the function to download the image
await downloadImage(imageUrl, filePath);
console.info(`🖼️ Saved image to ${filePath}`); // Logging the saved image path
fileIndex++; // Incrementing the file index for the next image
await delay(250); // Adding a delay between downloads
}
}
} catch (error) {
// Logging any errors that occur during execution
console.error(`Error: ${error.message}`);
}
}
// Function to download an image from a URL and save it to a specified file path
async function downloadImage(url, filePath) {
// Fetching the image from the URL
const response = await fetch(url);
// Checking if the response is OK
if (!response.ok) {
// Throwing an error if the download fails
throw new Error(`Failed to download image: ${response.statusText}`);
}
// Converting the response to an ArrayBuffer
const arrayBuffer = await response.arrayBuffer();
// Creating a Buffer from the ArrayBuffer
const buffer = Buffer.from(arrayBuffer);
// Writing the buffer to the specified file path
await fs.promises.writeFile(filePath, buffer);
}
// Function to display ASCII art and a message
function asciiArt() {
console.info(`
/$$ /$$ /$$ /$$ /$$$$$$$ /$$$$$$ /$$$$$$$
| $$$ /$$$| $$ /$$/| $$__ $$ /$$__ $$| $$__ $$
| $$$$ /$$$$| $$ /$$/ | $$ \\ $$| $$ \\__/| $$ \\ $$
| $$ $$/$$ $$| $$$$$/ | $$$$$$$ | $$$$$$ | $$ | $$
| $$ $$$| $$| $$ $$ | $$__ $$ \\____ $$| $$ | $$
| $$\\ $ | $$| $$\\ $$ | $$ \\ $$ /$$ \\ $$| $$ | $$
| $$ \\/ | $$| $$ \\ $$| $$$$$$$/| $$$$$$/| $$$$$$$/
|__/ |__/|__/ \\__/|_______/ \\______/ |_______/`);
console.info(``);
console.info(`🤑 Starting downloads from your favorite sellout grifter's wallpaper app...`);
}
// Immediately invoking the function to display ASCII art and start the main function after a delay
(() => {
asciiArt(); // Display ASCII art
setTimeout(main, 5000); // Delay the execution of the main function by 5 seconds
})();
(comments added by me)
A simple script, that does what it promises. I ran it and within a few minutes, it downloaded all the wallpapers to my desktop.
As Nadeem notes on his GitHub repo, if you’re a Node.js user, you can simply fire up your terminal and run node mkbsd.js
.
If you’re more of a Python person, just make sure you have the aiohttp
package installed (a quick pip install aiohttp
should do the trick) and then run python mkbsd
.
But if running scripts isn’t your thing, don’t worry – someone’s already done the heavy lifting for you. There’s a website called Walldrip.app with all these wallpapers, so you can browse and pick the ones you like.
But here’s the thing: the wallpapers themselves are a major letdown. They aren’t even that high-quality, and some seemed like they were generated by AI. Not exactly what I’d call “premium” content. And the worst part? The app is charging an exorbitant price for this subpar content.
The idea
I think I understand what he is trying to do here. He wants to create a subscription-based service, kind of like Netflix or Spotify, but for wallpapers.
The idea is to split the revenue with the “artists” who create the wallpapers. Sounds fair, right? Except, unlike Netflix and Spotify, which have massive costs to cover server resources and bandwidth, this app is charging a hefty 50/50 commission. Even Apple, who is ridiculed for its hefty price tags charges only 30% commission on Apps bought through its App Store.
Also read: How to Lighten or Darken a Color with Pure CSS
What happened
For years, he has built a reputation as a genuine, non-greedy reviewer who puts the consumer first. He’s known for his attention to detail and incisive analysis of products, which has earned him nearly 20 million subscribers on YouTube and made him one of the richest tech influencers out there.
But with the launch of his wallpaper app, something feels off. The price point of $15.99/month (later lowered to $11.99/month) is just ridiculous, especially when you consider that Netflix offers thousands of high-quality movies for a similar price. And if you’re not a paying member, you have to watch (uhm drumroll… 🥁 🥁 ) two 30-second ads to download an SD version of the wallpaper.
Are you kidding me?
It seems like he’s lost touch with reality, thinking that people would pay top dollar for AI-generated wallpapers. The irony is that his channel is called MKBHD, but people are now jokingly calling it MKBSD (low-def, get it?). It’s a shame because I used to respect this guy for his honest reviews and consumer-centric approach. But now, it looks like he’s just chasing the money.
Business out of Wallpapers (in 2024?)
He advertised his app in his iPhone 16 review video to ride on the popularity of his iPhone review videos, which historically received a broad viewership. Apparently, people have been searching for “Where does Marques Brownlee get his wallpapers from” on Google, and some entrepreneur friend of his thought, “Hey, let’s turn this into a business!”
They actually thought these Google searches were sufficient to make a business out of wallpapers, no matter the ridiculousness and callousness of the proposition and time. I mean, it’s not 2004 now, is it?
Also read: 4 Awesome Tricks to Change the Default Fonts in Excalidraw
The wholesome thing for him would have to put all of his wallpapers in a shareable Google Drive folder and offer them for free as a token of appreciation. Or maybe create an ad-free app, with everything available to download for everyone, except maybe with occasional links to his YouTube vids or merch tossed in as a bonus.
Instead, this shameless money-grab has left us feeling let down. As a respected tech reviewer, Marques has built trust with his audience over the years, influencing our purchasing decisions that can add up to hundreds or thousands of dollars. This bold display of greed has shaken that trust.
Let this be a lesson to other influencers: don’t create products just to cash in. Your audience deserves better.