{"id":24466230,"url":"https://github.com/vitto4/yt-playlist-diff","last_synced_at":"2026-03-07T19:31:33.194Z","repository":{"id":222209285,"uuid":"756565864","full_name":"vitto4/yt-playlist-diff","owner":"vitto4","description":"A Python script to dump and diff YouTube playlists as csv archives.","archived":false,"fork":false,"pushed_at":"2025-05-22T08:58:53.000Z","size":131,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-22T10:08:37.375Z","etag":null,"topics":["youtube","youtube-playlist","youtube-playlist-backup","youtube-playlist-export"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vitto4.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-02-12T21:42:01.000Z","updated_at":"2025-05-22T08:58:58.000Z","dependencies_parsed_at":null,"dependency_job_id":"c4224dca-c60f-457c-b8b8-be8329a4f77b","html_url":"https://github.com/vitto4/yt-playlist-diff","commit_stats":null,"previous_names":["vitto4/yt-playlist-bookmarklet","vitto4/yt-playlist-diff"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/vitto4/yt-playlist-diff","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitto4%2Fyt-playlist-diff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitto4%2Fyt-playlist-diff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitto4%2Fyt-playlist-diff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitto4%2Fyt-playlist-diff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vitto4","download_url":"https://codeload.github.com/vitto4/yt-playlist-diff/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitto4%2Fyt-playlist-diff/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30227793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T19:01:10.287Z","status":"ssl_error","status_checked_at":"2026-03-07T18:59:58.103Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["youtube","youtube-playlist","youtube-playlist-backup","youtube-playlist-export"],"created_at":"2025-01-21T06:13:59.739Z","updated_at":"2026-03-07T19:31:33.169Z","avatar_url":"https://github.com/vitto4.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# yt-playlist-diff\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://youtube.com\"\u003e\n    \u003cimg alt=\"YouTube\" src=\"https://img.shields.io/badge/YouTube-%23FF0000.svg?\u0026logo=YouTube\u0026logoColor=white\"\n  /\u003e\u003c/a\u003e\n  \u003ca href=\"https://python.org/downloads\"\u003e\n    \u003cimg alt=\"Python\" src=\"https://img.shields.io/badge/python-3.4+-blue.svg\"\n  /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/vitto4/yt-playlist-diff/releases\"\u003e\n    \u003cimg alt=\"GitHub Release\" src=\"https://img.shields.io/github/v/release/vitto4/yt-playlist-diff\"\n  /\u003e\u003c/a\u003e\n  \n\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\u003ci\u003eA python script to dump and diff YouTube playlists as csv archives.\u003c/i\u003e\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003cpicture\u003e\u003cimg src=\"misc/ascii-banner.svg\" style=\"width: 93%;\"\u003e\u003c/picture\u003e\n\u003c/div\u003e\n\n## 🏮 Index\n1. [Overview](#-overview)\n2. [Installation](#-installation)\n3. [Usage](#-usage)\n4. [Additional notes](#-additional-notes)\n5. [Contributing](#-contributing)\n\n\n## ☁ Overview\n\nWhen a video goes private or is deleted, it will appear as such in YouTube playlists, making it difficult for the user to figure out what the original video was.\n\nThis project is my attempt at solving the problem. It consists of a script that can both :\n- Dump for you the contents of any YouTube playlist you can view through your browser. Outputs a `csv` archive.\n- Perform a `diff` on two archives of the same playlist to compile a list of all videos that went missing, along with their original title and channel if available in the older archive.\n\n\u003cbr\u003e\n\n\u003cdiv align=\"center\"\u003e    \n\u003cdetails\u003e\n  \u003csummary\u003e\u003cins\u003eUsage\u003c/ins\u003e\u003c/summary\u003e\n  \u003cdiv align=\"left\"\u003e\n    \n```\nUsage: script.pyz [-h] OPERATION ...\n\n| Fetch a YouTube playlist using its ID.\n| Dump it into a CSV archive.\n| Diff two archives of the same playlist to (hopefully) recover lost videos.\n\nPositional Arguments:\n  OPERATION\n    dump      Dump the playlist into a CSV archive.\n    up-diff   Fetch upstream and perform a diff with your local archive.\n    local-diff\n              Perform a local diff between two archives.\n\nOptions:\n  -h, --help  show this help message and exit\n\n| Examples :\n|\n|  * Dump a playlist\n|    \u003e script.pyz dump --id LOremipSUmdolOrsiTamEtConseCtETuRA --browser chrome --output ./cool_playlist.csv\n|\n|  * Diff an archive with upstream\n|    \u003e script.pyz up-diff --diff-base ./trendy_memes.csv --browser firefox\n|\n|  * Diff two local archives\n|    \u003e script.pyz local-diff --diff-base ./dusty_old_archive.csv --diff-with ./shiny_new_archive.csv\n|\n```\n\n  \u003c/div\u003e\n\u003c/details\u003e\n\u003c/div\u003e\n\n\u003cbr\u003e\n\nAll in all, it should enable you to keep track of which video vanishes over the course of time.\n\nThe major advantage of this approach is that it'll work regardless of whether the playlist is set to public or private, as long as you tell the script in which browser you're logged into YouTube.\n\nHere's an example of the structure of an archive :\n\n```csv\nPlaylist ID : LOremipSUmdolOrsiTamEtConseCtETuRA\nArchived on : 1704067200000\nindex, id, isUnavailable, channel, channelUrl, title\n1, GGrFShhGRWc, True, \"Unknown artist\", \"Unknown link\", \"[Deleted video]\"\n2, dQw4w9WgXcQ, False, \"Rick Astley\", \"https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw\", \"Rick Astley - Never Gonna Give You Up (Official Music Video)\"\n3, ...\n```\n\n\n## 💾 Installation\n\n1. Make sure you have [Python](https://www.python.org/downloads/) installed.\n2. Grab both `requirements.txt` and `script.pyz` from the latest [release](https://github.com/vitto4/yt-playlist-diff/releases).\n3. Install the required dependencies. I recommend using a [venv](https://docs.python.org/3/tutorial/venv.html).\n\nIf you're on linux :\n\n```sh\n$ python3 -m venv env\n$ source env/bin/activate\n(env) $ pip install -r requirements.txt\n```\nOr for windows :\n```bat\n\u003e py -m venv env\n\u003e env\\Scripts\\activate.bat\n(env) \u003e pip install -r requirements.txt\n```\n\n4. You're all set. The script is distributed as a python [zipapp](https://docs.python.org/3/library/zipapp.html) (`.pyz` file), but fear not, you can run it like any other python script.\n\n## 📚 Usage\n\n### General workflow\n\n![](misc/diagram.drawio.svg)\n\n\n#### 1 : Dump your playlist\n\nThe first step is to make a clean archive of your playlist. We'll call it *BASE*.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cins\u003eUsage :\u003c/ins\u003e \u003ccode\u003edump\u003c/code\u003e\u003c/summary\u003e\n\n```\nUsage: script.pyz dump [-h] --id PLAYLIST_ID [--browser BROWSER] [--output PATH]\n\nOptions:\n  -h, --help         show this help message and exit\n  --id PLAYLIST_ID   YouTube ID of the playlist to dump\n                     E.g. : `LOremipSUmdolOrsiTamEtConseCtETuRA`.\n  --browser BROWSER  Browser to use for session cookies (required to access private playlists when fetching)\n                     E.g. : `chrome`, `firefox`.\n  --output PATH      Customize the path (and name) of the output archive\n                     E.g. : `./folder/my_shiny_new_archive.csv`.\n```\n\n\u003c/details\u003e\n\nSo you can do something like :\n\n```sh\nscript.pyz dump --id \u003cPlaylistID\u003e\n```\n\n#### 2 : Diff two archives\n\nYou have a clean archive from some time ago, and now your playlist's missing a few videos.\nTo find out what these are, perform an **upstream diff**.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cins\u003eUsage :\u003c/ins\u003e \u003ccode\u003eup-diff\u003c/code\u003e\u003c/summary\u003e\n\n```\nUsage: script.pyz up-diff [-h] --diff-base PATH [--id-override PLAYLIST_ID] [--browser BROWSER]\n\nOptions:\n  -h, --help            show this help message and exit\n  --diff-base PATH      Path to your existing archive in CSV format\n                        E.g. : `./dusty_old_archive.csv`.\n  --id-override PLAYLIST_ID\n                        YouTube ID of the playlist to fetch. This should be detected automatically using the archive provided in `--diff-base`.\n  --browser BROWSER     Browser to use for session cookies (required to access private playlists when fetching)\n                        E.g. : `chrome`, `firefox`.\n```\n\n\u003c/details\u003e\n\nHence you can run the script like :\n\n```sh\nscript.pyz up-diff --diff-base ./old_archive.csv\n```\n\nThis will produce a detailed report of what videos are gone, with metadata if possible (title, channel, URL, ...).\n\nInternally, the *UPSTREAM* version of the playlist is fetched directly from YouTube ; i.e. your `old_archive.csv` will be diffed against the latest version of the playlist available online.\n\nNote that this step can also be performed locally, with a **local diff**.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cins\u003eUsage :\u003c/ins\u003e \u003ccode\u003elocal-diff\u003c/code\u003e\u003c/summary\u003e\n\n```\nUsage: script.pyz local-diff [-h] --diff-base PATH --diff-with PATH\n\nOptions:\n  -h, --help        show this help message and exit\n  --diff-base PATH  Path to your existing archive in CSV format\n                    E.g. : `./dusty_old_archive.csv`.\n  --diff-with PATH  Path to the most recent of the two archives you want to diff.\n```\n\n\u003c/details\u003e\n\nThis enables you to provide a second archive to diff against, in place of *UPSTREAM* :\n\n```sh\nscript.pyz local-diff --diff-base ./old_archive.csv --diff-with ./new_archive.csv\n```\n\n#### 3 : Dump it again\n\nWhen you're done recovering videos, don't forget to make a new **clean** archive of your updated/repaired playlist for future use with this script.\n\nThis will prevent redundant hits next time you perform a **diff**. (i.e. a video would be flagged as lost when you have, in fact, already replaced it with a reuploaded version)\n\n## 🔖 Additional notes\n\nThis repo [used to host](https://github.com/vitto4/yt-playlist-diff/tree/yt-playlist-bookmarklet) a JS bookmarklet to perform the dump, but it was a bit too tedious to maintain, hence the switch to [`yt-dlp`](https://github.com/yt-dlp/yt-dlp).\n\nThe zipapp is created using the following command :\n\n```sh\npython3 -m zipapp src --main=main:main --output=script.pyz\n```\n\nI had a surprisingly hard time to try and explain how to actually use my code, this is when I decided to make the [workflow diagram](#general-workflow), hopefully it clears things up a bit !\n\nI initially made this for my own use, but I hope it can be useful to others as well :)\n\n\n## 🧩 Contributing\n\nContributions are (of course) welcome.\n\nAs long as `yt-dlp` keeps working, I believe the script should not break ; hence the project probably doesn't need much updating.\nFeel free to open an issue if I've missed anything or if you need some help !\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitto4%2Fyt-playlist-diff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvitto4%2Fyt-playlist-diff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitto4%2Fyt-playlist-diff/lists"}