How to remove all YouTube Shorts from watch history

Sunday, May 5, 2024

Unlike any other solution out there that simply hide the shorts, this one will actually remove shorts from watch history since beginning of time.

By the nature of ever changing YouTube, this solution is likely to break whenever YouTube changes. Check the comments in case someone already posted a fix.

WARNING: I do not take responsibility if something happens to your account, as Google may not like such activity. If you have a billion of shorts watched, removing all may take several hours. So it’s good to take a break once in a while. It is safe to close the page or the browser at any moment and continue later. Also, watch your RAM consumption, loading too much of the watch history may gradually eat all the RAM.

Go to, then open browser console (usually F12) and put:

(async function() {
  while (window.innerHeight + window.scrollY < document.documentElement.scrollHeight) {
    let days = Array.from(document.querySelectorAll('#contents > .ytd-reel-shelf-renderer'));
    for (let i = 0; i < days.length; ++i) {
      let day = days[i];
      day.scrollIntoView({behavior: 'smooth', block: 'start'});

      let rightArrowButton = day.querySelector('#right-arrow > ytd-button-renderer');
      while (rightArrowButton.checkVisibility()) {;
        await new Promise(resolve => setTimeout(resolve, 100));

      let videos = Array.from(day.querySelectorAll('ytd-reel-item-renderer'));
      for (let j = 0; j < videos.length; ++j) {
        console.log(`day ${i + 1}/${days.length} | video ${j + 1}/${videos.length}`);

        while (true) {
          var menuButton = videos[j].querySelector('#button[aria-label="Action menu"]');
          if (!menuButton) {
            console.log('waiting for menu...');
            await new Promise(resolve => setTimeout(resolve, 1000));
          } else {

        if (!menuButton.checkVisibility()) {
        await new Promise(resolve => setTimeout(resolve, 100));

        let removeButton = Array.from(document.querySelectorAll('.style-scope.ytd-menu-service-item-renderer')).
          find(el => el.textContent === 'Remove from watch history');
        if (removeButton.checkVisibility()) {
        await new Promise(resolve => setTimeout(resolve, 100));

    window.scrollTo(0, document.documentElement.scrollHeight);
    await new Promise(resolve => setTimeout(resolve, 5000));

This will go through every visible short on the page, click the three dots menu and then click “Remove from watch history”. After processing all visible shorts, it will scroll the page for more. Once it reaches the end of the history, it will just stop.

How to install Intune in Debian 12

Tuesday, March 26, 2024

I wish I didn’t have to use this abomination of a software from Microsoft, but I have to. After spending countless hours I had to document this in case I have to go through this again.

Common misconceptions:

General steps:

  1. Follow Ubuntu 22.04 instructions to install Intune.
  2. apt install msopenjdk-11 (comes from Microsoft’s repository, which you added at the previous step).
  3. ln -s /usr/lib/jvm/msopenjdk-11-amd64 /usr/lib/jvm/java-11-openjdk-amd64
  4. update-alternatives --config java and select the one that points to msopenjdk-11.
  5. Finally run intune-portal.


  1. When clicking “Sign up”, the new window is blank. The content is there but for some reason it fails to render. One workaround is to blindly navigate to the input field. Click the window in the top left corner and press Tab, it should jump to the input field, then type in your corporate email and press Enter. It should open a new window for entering password, which will have its content visible. Another workaround is to run a separate X server with software renderer:
    sudo apt install xserver-xephyr
    Xephyr -ac -br -noreset -screen 1280x1024 :1 &
    # optionaly, run some window manager to be able to change window geometry:
    # DISPLAY=:1 openbox &
    DISPLAY=:1 intune
  2. After entering email there is no separate third window to enter password, instead it asks for password in the same second window. Possible reasons:
    • No symlink to msopenjdk-11.
    • Intune fails to create a keyring to store the tokens. See below how to use Seahorse to debug this problem.
  3. You authenticated but it shows “Get the app” button. After clicking the button nothing happens. See the previous step.

  4. You get error [1001]. Most likely you tried too many times to authenticate and the procedure is temporarily blocked. Try again in 10 minutes or later.

  5. If device compliance check asks you to to downgrade to a supported distribution (LOL), grab /etc/os-release from Ubuntu 22.04 installation (you may use this one).

To debug the keyring problem, install and run Seahorse (apt install seahorse). Click the + button in the corner and make sure there is a menu item to create “Password keyring”. If there is no such item, happy debugging. Possibly something is wrong with how dbus daemon is launched.

Merge Git repositories, preserving chronological order of commits

Wednesday, May 22, 2019

Sometimes you want to split a huge monorepo into multiple smaller repositories, like if you were migrating from Subversion. What if you want to do the opposite? I did.

I had a set of tiny git repositories that describe how to build Debian packages in OpenSuse Build Service. The repositories had similar directory structure and even the commits in each repository were alike. Eventually I thought that merging all repositories into a single monorepo would be more efficient.

I started searching if somebody had already achieved something like this. I found few attempts but they all differ in a way how the history is preserved.

Basically all the solutions I found merge repositories with --allow-unrelated-histories option. It allows merging branches that don’t share common ancestor commits. But in result the history of each repository is trapped inside merge commits. Here is how it would look for two repositories:

     A  B  C  D       W  X  Y  Z
     •--•--•--•   +   •--•--•--•

     A  B  C  D       W  X  Y  Z
     •--•--•--•   +   •--•--•--•
    /                /

Which is inconvenient if you still want to reorder or squash some consequent commits across multiple repositories. This is how I wanted the commits, ordered chronologically:

A  W  B  C  X  Y  Z  D

Here is the approach I went with:

mkdir -p ~/merge-attempt/repos
cd ~/merge-attempt/repos
git clone git@githost:user/repo1
git clone git@githost:user/repo9
cd repos
for R in *; do
  pushd $R
  git format-patch --root --src-prefix=a/$R/ --dst-prefix=b/$R/
  for P in *.patch; do
    grep '^Date: ' $P | \
      sed 's/^Date: \(.*\)/\1/' | \
      date -Iseconds -f - | \
      sed 's/[:+]/-/g' | \
      xargs -I{} mv $P {}.patch
  mv *.patch ../..

Here --src-prefix and --dst-prefix arguments allow us to preconfigure the patches, so when applied, the files would be created in subdirectories named according to original repository.

We should get something like this:

mkdir -p ~/merge-attempt/monorepo
cd ~/merge-attempt/monorepo
git init .
git am ../*.patch
git filter-branch --env-filter 'export GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"'

And that would be it.

SanDisk Sansa Clip Zip with 128GB microSD

Monday, January 7, 2019

If you ever wondered whether Sansa Clip Zip is able to handle microSD cards larger than 32GB, I managed to stick in a 128GB one from Kingston and it worked just fine.

Only had to format it to FAT32.

Handmade screen locker with fade out

Monday, October 10, 2016

Just wanted to share my solution for screen locking that proved itself over the years very practical. As I stepped away from full-blown desktop environments, here and there I had to search how to get some of the features back, but keep it very lightweight on resources. I wanted a very primitive solution with a few dependencies.

Imagine you are watching a long YouTube video and suddenly the screen is locked. Not very user friendly. So fading out notification with possibility to cancel screen locking is a must have feature.

For this recipe we will need:

Here goes the script that will start screen fading out:


[ $# -eq 1 ] && SEC=$1
SLEEP=`echo $SEC / $FRAMES | bc -l`

trap "xcalib -clear" EXIT

sleep 0.1

for (( i = 1; i <= $FRAMES; i++ )); do
  if [ $LAST_IDLE -gt $NEW_IDLE ]; then
    exit 0
  xcalib -co 95 -a
  sleep $SLEEP

xset dpms force off

When executed it will gradually lower the contrast to pitch black. If there is any mouse movement or a key press it will cancel fading out. If you like customization, it also accept a parameter, number of seconds during which screen will be fading out (10 by default). Save it somewhere in the home directory, for example in ~/.bin/.

Screen fader by itself is not enough, we also need a locker tool as well as the tool that will detect when there is no user activity. Add the following line to your script that runs at log in, in case of Openbox it is ~/.config/openbox/autostart.

$ xautolock -time 30 -locker "dm-tool lock" -notify 15 -notifier "$HOME/.bin/" &

In here 30 is the number of minutes after which the screen will be locked if no user activity detected. 15 is the number of seconds prior to screen locking when notifier will be executed. In our case the notifier is script from above. Replace dm-tool lock with locker of your choice, if you don’t use lightdm.

Then we need to change the setting for the default X screen saver that it doesn’t interfere with ours. So add this line as well to your autostart script:

$ xset s 1800 1800 dpms 1800 1800 1800 # screen blanking after 30 minutes

Finally, set a handler to lock the screen on demand via a hot key. For Openbox that would be in ~/.config/openbox/rc.xml:

<keybind key="C-A-L">
  <action name="Execute">
    <name>Lock Screen</name>
    <command>xautolock -locknow</command>

That’s all. Now you are in full control of screen locking. Sweet!

Older Posts ->