Skip to content

Instantly share code, notes, and snippets.

@guillaumematheron
Last active April 4, 2024 21:08
Show Gist options
  • Save guillaumematheron/89f52ffd274ff3ac99f6dc0249bcc331 to your computer and use it in GitHub Desktop.
Save guillaumematheron/89f52ffd274ff3ac99f6dc0249bcc331 to your computer and use it in GitHub Desktop.
"""
Simulate re-watching all your videos from a given date onwards.
If part of your youtube history has been deeleted but is still visible from 'my activity', then
you can export it using google takeout, and use this script to simulate watching all these videos
in order, in a relatively short time.
Note that the history will not be backdated.
License: CC0 / Public domain
"""
import hashlib
import json
import random
import os
import time
from typing import Any, Dict, List
import yt_dlp
RESUME = "2022-08-17T11:50:00.000Z"
def main():
# An empty file will be created in the 'done' directory to keep track
# of videos that were already watched, so that this script can be
# interrupted and resumed.
try:
os.makedirs("done")
except FileExistsError:
pass
# This file should be generated using google takeout
with open("watch-history.json", encoding="utf8") as f:
data = json.load(f)
kept: List[Dict[str, Any]] = []
for event in data:
# Disregard youtube music
if event["header"] != "YouTube":
continue
if "details" in event and event["details"][0]["name"] == "From Google Ads":
continue
if event["time"] < RESUME:
continue
if "titleUrl" not in event:
continue
kept.append(event)
print(f"Found {len(kept)} videos to watch")
# Deduplicate
kept = [event for event in {event["titleUrl"]: event for event in kept}.values()]
print(f"Found {len(kept)} videos to watch after de-duplication")
# Sort
kept.sort(key=lambda x: x["time"])
opts = {
"mark_watched": True,
"simulate": True,
"quiet": True,
"cookiesfrombrowser": ("firefox",),
}
with yt_dlp.YoutubeDL(opts) as ydl:
for i, event in enumerate(kept):
t = event["time"]
url = event["titleUrl"]
title = event["title"][8:]
m = hashlib.sha256()
m.update(url.encode("utf-8"))
marker = "done/" + m.hexdigest()
print(
f"{i}/{len(kept)} \t {t} \t {url} \t {title} ... ", end="", flush=True
)
try:
with open(marker, "r"):
pass
print(" -> Already done")
continue
except FileNotFoundError:
pass
try:
ydl.download(url)
print(" -> Sleeping ... ", end="", flush=True)
time.sleep(3 + random.random() * 8)
print(" -> Done")
except yt_dlp.utils.DownloadError:
print(" -> DownloadError")
with open(marker, "w"):
pass
if __name__ == "__main__":
main()
@seaque
Copy link

seaque commented Mar 15, 2024

an update, the history entries started coming back for a lot of people. It seems mine is mostly fixed.

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