{"id":41182677,"url":"https://github.com/nosoop/moonarchive","last_synced_at":"2026-01-22T20:10:50.055Z","repository":{"id":243848488,"uuid":"813628240","full_name":"nosoop/moonarchive","owner":"nosoop","description":"YouTube livestream downloader.","archived":false,"fork":false,"pushed_at":"2026-01-16T17:02:07.000Z","size":212,"stargazers_count":7,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-17T04:54:10.085Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"0bsd","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nosoop.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-06-11T12:44:57.000Z","updated_at":"2026-01-16T16:44:41.000Z","dependencies_parsed_at":"2024-06-29T17:43:03.066Z","dependency_job_id":"1a3c02d8-113a-4af8-abf1-a261ecd5283b","html_url":"https://github.com/nosoop/moonarchive","commit_stats":null,"previous_names":["nosoop/moonarchive"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/nosoop/moonarchive","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nosoop%2Fmoonarchive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nosoop%2Fmoonarchive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nosoop%2Fmoonarchive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nosoop%2Fmoonarchive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nosoop","download_url":"https://codeload.github.com/nosoop/moonarchive/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nosoop%2Fmoonarchive/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28670366,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-22T19:36:09.361Z","status":"ssl_error","status_checked_at":"2026-01-22T19:36:05.567Z","response_time":144,"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":[],"created_at":"2026-01-22T20:10:47.676Z","updated_at":"2026-01-22T20:10:50.046Z","avatar_url":"https://github.com/nosoop.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# moonarchive\n\nA Python module and tool to download ongoing YouTube streams in its entirety.\n\nShout outs to [`ytarchive`][] and [`yt-dlp`][] for their efforts in the space.  They get all the\ncredit for documenting the download process; I'm only working to improve parts of that.\n\nAlso to [glubsy/livestream_saver][], though I saw that long after the above.\n\n(If I knew Go or was comfortable parsing out yt-dlp's live-from-start logic, I'd be contributing\nchanges to those projects instead.)\n\n\u003e [!WARNING]\n\u003e \n\u003e You should probably not use this tool as your only solution for downloading livestreams\n\u003e unsupervised; the author recommends running one of the other projects listed above in parallel\n\u003e or at least actively watching the progress to ensure that the tool is behaving correctly.\n\n\u003e [!NOTE]\n\u003e \n\u003e The module API design is currently in flux.  Unless you want to keep up with changes, maybe\n\u003e don't import the module and call the downloading logic yourself for now.  The command-line\n\u003e frontend probably won't change too much in comparison, but do review the help text after\n\u003e upgrading.\n\n[`ytarchive`]: https://github.com/Kethsar/ytarchive\n[`yt-dlp`]: https://github.com/yt-dlp/yt-dlp\n[glubsy/livestream_saver]: https://github.com/glubsy/livestream_saver\n\n## Installation\n\n\u003e [!WARNING]\n\u003e To minimize the risk of your connection being flagged, see the section on\n\u003e [Proof-of-origin downloads](#proof-of-origin-downloads).\n\n(Looking for a graphical interface to manage things?  Check out [moombox][].)\n\n[moombox]: https://github.com/nosoop/moombox\n\n### Dependencies\n\n- `moonarchive` is tested against Python 3.11.  The installation will bring in a number of other\nthird-party packages.\n- [ffmpeg][] is required for creating the final file.  This is not installed automatically.\nOn Windows, you can use `winget install ffmpeg`; for other platforms refer to your package\nmanager.\n\n[ffmpeg]: https://ffmpeg.org/download.html\n\n### via pip\n\nIf you're comfortable with Python, it's probably a good idea to install this in a virtual\nenvironment so the libraries are isolated from the rest of the system.\n\nIf you're not, consider [installing via uv](#via-uv) instead.\n\n1. `pip install \"moonarchive[keepawake,cookies] @ git+https://github.com/nosoop/moonarchive\"`\n    - `keepawake` and `cookies` are optional extras.  See the [Extras](#extras) section below\n    for further details on each.\n2. At minimum, `moonarchive ${URL}` on an upcoming or currently live stream to download;\n`moonarchive --help` to view all possible options.\n\nFor development:\n\n1. Use `[keepawake,cookies,dev]` with the `pip` command above to install all packages, including\nthose for development.\n2. Install [`just`](https://github.com/casey/just).\n3. Make your changes.  Prior to committing, run `just test` and `just format`.\n\n### via uv\n\n[`uv`][] is an alternative package manager for Python.  The benefit of using `uv` is that it'll\nmanage installing Python as needed and isolating the library dependencies for you.\n\n1. [Install `uv`.](https://docs.astral.sh/uv/getting-started/installation/)\n2. `uv python install 3.11` to install Python 3.11.  Newer versions may work.\n3. `uv tool install \"moonarchive[keepawake,cookies] @ git+https://github.com/nosoop/moonarchive\"`\n4. `moonarchive ${URL}` (you may need to `uv tool update-shell` or `source $HOME/.local/bin/env`\nto update your `PATH` environment variable)\n\n[`uv`]: https://docs.astral.sh/uv/\n\n### Extras\n\nThe package includes a number of optional dependencies that can be installed for additional\nfunctionality.\n\n#### `keepawake`\n\nThis installs an optional dependency ensuring that, when using the CLI, the system doesn't\nautomatically go into standby while the application is running (waiting for a stream and while\ndownloading).\n\nIf this extra is not installed, you will need to pass `--no-keep-awake` to the application to\nacknowledge the possibility that the system may sleep.\n\nIn the event that the system does go to sleep (whether manually or on idle) moonarchive should\ngracefully recover, provided the stream contents are still available.\n\nThe keepawake behavior only applies to the CLI application and is not active when using the\nmodule API.\n\n#### `cookies`\n\nThis installs an optional dependency on [browser-cookie3][], allowing moonarchive to access the\nbrowser's cookies on-demand whenever certain requests are made.\n\nWith this extra, you can use `--cookies-from-browser` to specify a given browser to extract\ncookies from, and `--cookies` to specify the cookie database for that browser.\n\nWithout this extra, you can still use `--cookies` to specify a cookie file in Netscape format,\nwhich will also be loaded as needed on requests.  It's recommended to follow\n[the yt-dlp guide on exporting YouTube cookies][yt-dlp cookies] in that case to ensure cookies\nare valid across the lifetime of the stream download.\n\nI've personally checked the code in [the 0.20.1 release][audited bc3] and found nothing\nquestionable.  Whether or not that's acceptable is up to you to decide.\n\n[browser-cookie3]: https://pypi.org/project/browser-cookie3\n[audited bc3]: https://github.com/borisbabic/browser_cookie3/commit/03895797e48dd107806db171d8392c562151807d\n[yt-dlp cookies]: https://github.com/yt-dlp/yt-dlp/wiki/Extractors#exporting-youtube-cookies\n\n#### `dev`\n\nInstalls `mypy` and `ruff` for typechecking and linting respectively.  This ensures\nconsistency of code formatting for contributors, and tries to check the correctness of type\nannotations.\n\n## Features\n\n- Detection of split broadcasts (updated manifests).  See [ytarchive#56][].  tl;dr in certain\nsituations, ytarchive will not reset its sequence counter when attempting to retrieve fragments\non a new broadcast, appearing to stall.\n    - moonarchive, in its current form, will mux out an individual file per broadcast (assuming\n    no other broadcast issues).  In theory it's possible to precisely merge broadcasts to one\n    file, but this requires processing the raw file's internal segment metadata added by YouTube\n    plus potential reencoding due to differing resolutions and codecs.\n    - As far as I'm aware, regardless of this functionality, you cannot obtain fragments from a\n    previous broadcast if you start the application late.\n- Handling of landscape-and-portrait transitions.  A streamer may swap width and height \u0026mdash;\nthis does not generate a new manifest, and resulting naively muxed files end up being garbage.\n    - moonarchive will not merge streams affected by this, and it's left to the user to decide\n    how to process the result (video streams will be split at resolution change boundaries,\n    while the audio streams will span the length of the broadcast).\n\n[ytarchive#56]: https://github.com/Kethsar/ytarchive/issues/56\n\n### Proof-of-origin downloads\n\nYouTube has started enforcing the use of proof-of-origin to verify that requests are made from a\nreal browser environment.\n\nWithout it, streams will expire after 30 seconds and `moonarchive` will need to make repeated\nrequests for new copies of the manifest.  This behavior, when done at a high enough frequency,\nwill trigger YouTube's bot detection, which at minimum will invalidate ongoing downloads.\n\nIf `moonarchive` needs to obtain a new copy of the manifest, the following message is displayed\nin the application output:\n\n\u003e Received HTTP 403 error cur_seq=21605, getting new player response\n\n(As stated before, with proof-of-origin, manifests normally expire after 6 hours.  Seeing this\nmessage on long-running streams is expected behavior.)\n\nWhile one instance of `moonarchive` should not make requests frequently enough to trigger\nYouTube, if you plan on downloading items in parallel it's strongly recommended to\n[obtain a token][].\n\n\u003e [!WARNING]\n\u003e The below information is somewhat outdated due to tests being rolled out by YouTube and will\n\u003e be removed once the application is stabilized.  Please skip to to the section on\n\u003e [content-based tokens](#content-based-tokens) for the current changes.\n\nPass one of the following (where `${VAR}` is variable information):\n\n- `--po-token ${POTOKEN} --visitor-data ${VISITOR_DATA}` for non-logged in contexts\n- `--po-token ${POTOKEN} --cookies ${COOKIE_FILE}` for logged in contexts (member streams, etc.)\n\nIt's not really clear how often you need to obtain a new proof-of-origin token.  The linked\nguide says 12 hours, but my personal experience on a residential connection has visitor data\nremaining valid for 180 days, with their respective tokens working for the same amount of time.\n(Once the visitor data expires, no tokens generated from that information will work, with\nYouTube behaving as if the token was not present at all.)\n\nIt's likely that tokens linked to an actual user will be rotated out sooner, though I haven't\nbeen able to test that myself.\n\nProof-of-origin is very much an evolving thing, so my personal observations at a given time may\ndiffer from that of others.\n\n[obtain a token]: https://github.com/yt-dlp/yt-dlp/wiki/PO-Token-Guide\n\n#### Content-based tokens\n\nAs of around 2025-10-14, YouTube started broadly rolling out a change where the video server\n(GVS) now accepts tokens generated based on video ID.\n\nThis effectively makes token generation occur at every download, instead of on the order of\nmonths.\n\nIn v0.4.4, `moonarchive` is able to leverage\n[Brainicism's proof-of-origin token provider][bgutil-pot], the same software currently\nrecommended by yt-dlp's maintainers.  To use this, pass the URL of the instance:\n\n```\n--unstable-bgutil-pot-provider-url \"http://127.0.0.1:4416\"\n```\n\nNote that this option is currently experimental and subject to change once the API stabilizes.\n\n[bgutil-pot]: https://github.com/Brainicism/bgutil-ytdlp-pot-provider\n\n## Contributions\n\nI designed this tool around my specific needs, so issues / pull requests that are opened may\nbe ignored as I don't have the capacity to maintain it for other peoples' use cases.  Sorry!\n\nHowever, feel free to open both regardless \u0026mdash; issue trackers are meant to serve the\nend users as much as they serve the project owner(s).  Maybe someone else will see them and\nwould be willing to maintain the project or lift the important parts of the code for use in\nbetter-maintained projects.  The less work I have to do, the better!\n\nIf you are interested in opening a pull request with the sole purpose of getting it merged,\nplease poke me with an issue beforehand to confirm that I'm both interested in taking it and\nnot already working on something similar.\n\n## License\n\nAll code in this repository is released under the Zero-Clause BSD license.  That said, please\nalso review the dependencies' licenses to ensure compliance with them as well should you be\ndistributing the code.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnosoop%2Fmoonarchive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnosoop%2Fmoonarchive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnosoop%2Fmoonarchive/lists"}