├── .github └── workflows │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── pyproject.toml └── yt_dlp_plugins └── postprocessor └── chrome_cookie_unlock.py /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create release 2 | on: [push] 3 | 4 | jobs: 5 | release: 6 | permissions: 7 | contents: write 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | 14 | - name: Set variables 15 | id: set_variables 16 | run: | 17 | echo "::group::Variables" 18 | cat << EOF | tee -a "$GITHUB_OUTPUT" 19 | tag=$(git describe --tags --abbrev=0) 20 | version=v$(sed -n 's/^version\s*=\s*"\(.*\)"$/\1/p' pyproject.toml) 21 | EOF 22 | echo "::endgroup::" 23 | 24 | - name: Bundle and create release 25 | env: 26 | GH_TOKEN: ${{ github.token }} 27 | tag: ${{ steps.set_variables.outputs.tag }} 28 | version: ${{ steps.set_variables.outputs.version }} 29 | if: | 30 | env.tag != env.version 31 | run: | 32 | files=(*) 33 | 34 | mkdir bundle/ 35 | cp -rt bundle -- "${files[@]}" 36 | 37 | cd bundle/ 38 | zip -9 --recurse-paths yt-dlp-ChromeCookieUnlock * 39 | gh release create "${version}" --latest \ 40 | --title "yt-dlp-ChromeCookieUnlock ${version}" \ 41 | 'yt-dlp-ChromeCookieUnlock.zip' 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.egg-info/ 3 | *.pyc 4 | dist/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Charles Machalow 4 | Copyright (c) 2024 sepro 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A [yt-dlp plugin](https://github.com/yt-dlp/yt-dlp#plugins) that allows unlocking the cookie database for chromium-based browsers. 2 | 3 | Based on the work of [Charles Machalow](https://github.com/csm10495) [here](https://gist.github.com/csm10495/e89e660ffee0030e8ef410b793ad6a7e). 4 | 5 | See [installing yt-dlp plugins](https://github.com/yt-dlp/yt-dlp#installing-plugins) for how this plugin package can be installed. 6 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["hatchling >=1.9.0, <2"] 3 | build-backend = "hatchling.build" 4 | 5 | [project] 6 | name = "yt-dlp-ChromeCookieUnlock" 7 | description = "A yt-dlp plugin to unlock the cookie database of chromium-based browsers" 8 | authors = [{name = "sepro", email = "sepro@sepr0.com"}] 9 | version = "2024.04.29" 10 | readme = "README.md" 11 | requires-python = ">=3.8" 12 | license = {file = "LICENSE"} 13 | keywords = [ 14 | "yt-dlp", 15 | "yt-dlp-plugins", 16 | ] 17 | 18 | [tool.hatch.build.targets.wheel] 19 | packages = ["yt_dlp_plugins"] 20 | -------------------------------------------------------------------------------- /yt_dlp_plugins/postprocessor/chrome_cookie_unlock.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | import yt_dlp.cookies 4 | 5 | original_func = yt_dlp.cookies._open_database_copy 6 | 7 | def unlock_chrome(database_path, tmpdir): 8 | try: 9 | return original_func(database_path, tmpdir) 10 | except PermissionError: 11 | print('Attempting to unlock cookies', file=sys.stderr) 12 | unlock_cookies(database_path) 13 | return original_func(database_path, tmpdir) 14 | 15 | yt_dlp.cookies._open_database_copy = unlock_chrome 16 | 17 | 18 | # Adapted from https://gist.github.com/csm10495/e89e660ffee0030e8ef410b793ad6a7e 19 | # By Charles Machalow under the MIT License 20 | 21 | from ctypes import windll, byref, create_unicode_buffer, pointer, WINFUNCTYPE 22 | from ctypes.wintypes import DWORD, WCHAR, UINT 23 | 24 | ERROR_SUCCESS = 0 25 | ERROR_MORE_DATA = 234 26 | RmForceShutdown = 1 27 | 28 | @WINFUNCTYPE(None, UINT) 29 | def callback(percent_complete: UINT) -> None: 30 | pass 31 | 32 | rstrtmgr = windll.LoadLibrary("Rstrtmgr") 33 | 34 | def unlock_cookies(cookies_path): 35 | session_handle = DWORD(0) 36 | session_flags = DWORD(0) 37 | session_key = (WCHAR * 256)() 38 | 39 | result = DWORD(rstrtmgr.RmStartSession(byref(session_handle), session_flags, session_key)).value 40 | 41 | if result != ERROR_SUCCESS: 42 | raise RuntimeError(f"RmStartSession returned non-zero result: {result}") 43 | 44 | try: 45 | result = DWORD(rstrtmgr.RmRegisterResources(session_handle, 1, byref(pointer(create_unicode_buffer(cookies_path))), 0, None, 0, None)).value 46 | 47 | if result != ERROR_SUCCESS: 48 | raise RuntimeError(f"RmRegisterResources returned non-zero result: {result}") 49 | 50 | proc_info_needed = DWORD(0) 51 | proc_info = DWORD(0) 52 | reboot_reasons = DWORD(0) 53 | 54 | result = DWORD(rstrtmgr.RmGetList(session_handle, byref(proc_info_needed), byref(proc_info), None, byref(reboot_reasons))).value 55 | 56 | if result not in (ERROR_SUCCESS, ERROR_MORE_DATA): 57 | raise RuntimeError(f"RmGetList returned non-successful result: {result}") 58 | 59 | if proc_info_needed.value: 60 | result = DWORD(rstrtmgr.RmShutdown(session_handle, RmForceShutdown, callback)).value 61 | 62 | if result != ERROR_SUCCESS: 63 | raise RuntimeError(f"RmShutdown returned non-successful result: {result}") 64 | else: 65 | print("File is not locked") 66 | finally: 67 | result = DWORD(rstrtmgr.RmEndSession(session_handle)).value 68 | 69 | if result != ERROR_SUCCESS: 70 | raise RuntimeError(f"RmEndSession returned non-successful result: {result}") 71 | --------------------------------------------------------------------------------