{"id":51541817,"url":"https://github.com/stanislav-janu/bunny-stream-uploader","last_synced_at":"2026-07-09T14:01:55.052Z","repository":{"id":367803240,"uuid":"1282364906","full_name":"stanislav-janu/bunny-stream-uploader","owner":"stanislav-janu","description":"Native macOS app for fast parallel uploads of large video files to Bunny Stream over TUS concatenation","archived":false,"fork":false,"pushed_at":"2026-06-27T18:53:39.000Z","size":1168,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-27T19:15:36.537Z","etag":null,"topics":["bunny-stream","bunnycdn","macos","parallel-upload","swift","swiftui","tus","uploader","video-upload"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stanislav-janu.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":"2026-06-27T17:12:22.000Z","updated_at":"2026-06-27T18:53:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/stanislav-janu/bunny-stream-uploader","commit_stats":null,"previous_names":["stanislav-janu/bunny-uploader"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/stanislav-janu/bunny-stream-uploader","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stanislav-janu%2Fbunny-stream-uploader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stanislav-janu%2Fbunny-stream-uploader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stanislav-janu%2Fbunny-stream-uploader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stanislav-janu%2Fbunny-stream-uploader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stanislav-janu","download_url":"https://codeload.github.com/stanislav-janu/bunny-stream-uploader/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stanislav-janu%2Fbunny-stream-uploader/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35301501,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-09T02:00:07.329Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["bunny-stream","bunnycdn","macos","parallel-upload","swift","swiftui","tus","uploader","video-upload"],"created_at":"2026-07-09T14:01:54.713Z","updated_at":"2026-07-09T14:01:55.044Z","avatar_url":"https://github.com/stanislav-janu.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bunny Stream Uploader\n\n[![Build](https://github.com/stanislav-janu/bunny-stream-uploader/actions/workflows/build.yml/badge.svg)](https://github.com/stanislav-janu/bunny-stream-uploader/actions/workflows/build.yml)\n\nA native macOS app for uploading large video files to [Bunny Stream](https://bunny.net) as fast as the link allows. It splits a single file into many parts and uploads them in parallel over TUS `concatenation`, reaching multiples of single-stream throughput.\n\nOn a test link a single 9 GB file went from ~56 minutes (single stream) to ~5 minutes (64 parallel threads), roughly 11x faster.\n\n![Bunny Stream Uploader](docs/screenshot.png)\n\n## Features\n\n- **Parallel single-file upload.** One file is split into N parts, each uploaded over its own TCP connection, then merged on Bunny via `Upload-Concat: final`.\n- **Automatic thread count** based on file size (configurable).\n- **Drag and drop**, plus a Finder **Quick Action / \"Open with\"** for common video formats (mp4, m4v, mov, mkv, webm, avi, flv, wmv, ts, mpeg).\n- **Resumable single-stream** fallback for small files (via TUSKit).\n- **Live throughput** and per-upload progress, with cancel.\n- **Credentials stored in the macOS Keychain**, never shown or logged.\n- **Localized** in English, Czech, Hungarian, Polish, and German.\n\n## Requirements\n\n- macOS 26 or later\n- Xcode 26 toolchain (Swift 6) to build\n- A Bunny Stream library (Library ID + Stream API key)\n\n## Build\n\n```sh\nswift build -c release\n./scripts/make-app.sh release\nopen BunnyUploader.app\n```\n\n`scripts/make-app.sh` builds the binary, assembles `BunnyUploader.app` (icon, localizations, document types), code-signs it, and registers it with LaunchServices.\n\n### Code signing (optional but recommended)\n\nA stable signing identity keeps the Keychain \"Always Allow\" decision valid across rebuilds. The script auto-detects the first available codesigning identity, or you can pin one:\n\n```sh\nBUNNY_SIGN_IDENTITY=\"Apple Development: you@example.com (TEAMID)\" ./scripts/make-app.sh release\n```\n\nWithout any identity it falls back to ad-hoc signing, and the app still runs locally (the Keychain will prompt on each rebuild).\n\nFor the Finder Quick Action and the app icon to behave reliably, move `BunnyUploader.app` into `/Applications`.\n\n## Usage\n\n1. Open **Settings** (⌘,) and enter your Bunny **Library ID** and **Stream API key**. They are stored in the Keychain.\n2. Upload a video three ways:\n   - **Drag and drop** an `.mp4`/`.mov` onto the window.\n   - **Right-click** a video in Finder → **Quick Actions / Services → Upload to Bunny**.\n   - **Open with → Bunny Stream Uploader**.\n3. Watch the aggregate throughput and per-file progress. Uploads can be cancelled mid-flight.\n\nBy default the app picks the thread count by file size (≥ 1 GB → 64, 500 MB–1 GB → 32, 100–500 MB → 16, 50–100 MB → 8, under 50 MB → single stream). You can switch to a manual thread count in Settings.\n\nIf a new Service does not appear in the Finder menu, enable it under **System Settings → Keyboard → Keyboard Shortcuts → Services**.\n\n## How it works\n\nBunny's TUS endpoint advertises the `concatenation` extension and runs over HTTP/1.1, so parallel requests use independent TCP connections (each with its own congestion window). For one file the app:\n\n1. Creates the video via the REST API to get a `videoId`.\n2. Signs the upload: `SHA256(libraryId + apiKey + expiration + videoId)`.\n3. Creates N partial uploads (`Upload-Concat: partial`) and PATCHes each part's bytes in parallel, reading from disk with `pread` off the cooperative pool.\n4. Merges them with `Upload-Concat: final;\u003curls\u003e`.\n\nSingle TCP throughput to Bunny is bounded by round-trip time; aggregating independent connections is what multiplies it. See [docs/performance.md](docs/performance.md) for the measured scaling curve.\n\n## License\n\nMIT. See [LICENSE](LICENSE). You are free to use, modify, and distribute this software.\n\n---\n\nBuilt by [stanislav-janu](https://github.com/stanislav-janu), co-authored with Claude (Anthropic).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstanislav-janu%2Fbunny-stream-uploader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstanislav-janu%2Fbunny-stream-uploader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstanislav-janu%2Fbunny-stream-uploader/lists"}